Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wrong date format when submit Spring form

I have a project where I use Spring MVC and Thymeleaf. I need to display dates with a different format for each user based on his preferences. For exemple, UserA want to display dates like MM/dd/yyyy and UserB want to display dates like dd/MM/yyyy.

To do this, I use this thymeleaf parameter:

th:value="${#dates.format(myDate, dateFormat)}"

The value "dateFormat" is based on the user preference. This works fine.

My problem is that the date input is in a form, and when I submit the form, it doesn't take the good format. I always get MM/dd/yyyy.

If I choose the format dd/MM/yyyy and enter 18/01/2016, in my spring controller I obtain "Thu Jun 01 00:00:00 CEST 2017" which correspond to 01/06/2017 in dd/MM/yyyy.

What can I do to have the date with the format that I want?

Here is my code:

<form th:action="@{/test}" th:object="${filter}" th:method="POST">
    <input type="date" th:type="date" class="form-control" th:id="myDate"
           th:name="myDate" th:value="${#dates.format(filter.myDate, dateFormat)}"/>
</form>

Controller:

@RequestMapping(value = "/test", method = RequestMethod.POST)
public String myTest(@ModelAttribute Filter filter, Model model) {

    Systeme.out.println(model.dateFormat);
    // dd/MM/yyyy

    Systeme.out.println(filter.myDate.toString());
    // Thu Jun 01 00:00:00 CEST 2017

    return "test";
}
like image 805
YLombardi Avatar asked Jan 18 '16 15:01

YLombardi


3 Answers

You can annotate the Date attribute

    @DateTimeFormat(pattern = "dd/MM/yyyy")
    private Date dob;

without Joda dependency since Spring 3.2

like image 97
Pierpaolo Pagnoni Avatar answered Oct 02 '22 20:10

Pierpaolo Pagnoni


After a day of research, I found that Spring read the value that is sent in the web request and try to bind it with the Filter object.

For a date value, it excepts to find a value with the "MM/dd/yyyy" format. If you send a value with another format, it doesn't work.

To solve this problem, you can use the annotation "InitBinder".

@InitBinder
private void dateBinder(WebDataBinder binder) {
    //The date format to parse or output your dates
    SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormat());
    //Create a new CustomDateEditor
    CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
    //Register it as custom editor for the Date type
    binder.registerCustomEditor(Date.class, editor);
}

This method is executed for each web request. Here I call my "dateFormat()" method to get the format in the user preference and say to Spring that all the java.util.Date that it find in web requests have this format.

Here is my full code :

Filter :

import java.util.Date;

@lombok.Data
public class TestDateFilter {
    private Date myDate;

    public TestDateFilter() {
        this.myDate = new Date();
    }
}

Controller :

@RequestMapping(value = "/testdate")
public String testDate(Model model) {
    model.addAttribute("filter", new TestDateFilter());
    return "testdate";
}

@RequestMapping(value = "/testdate", method = RequestMethod.POST)
public String testDatePost(@ModelAttribute("filter") TestDateFilter filter, Model model) {
    System.out.printf(filter.getLoadingStartDate().toString());
    System.out.printf(dateFormat());
    return "testdate";
}

@ModelAttribute("dateFormat")
public String dateFormat() {
    return userPreferenceService.getDateFormat();
}

@InitBinder
private void dateBinder(WebDataBinder binder) {
    //The date format to parse or output your dates
    SimpleDateFormat dateFormat = new SimpleDateFormat(dateFormat());
    //Create a new CustomDateEditor
    CustomDateEditor editor = new CustomDateEditor(dateFormat, true);
    //Register it as custom editor for the Date type
    binder.registerCustomEditor(Date.class, editor);
}

HTML :

    <form th:action="@{/testdate}" th:object="${filter}" th:method="POST">
        <div class="row">
            <div class="col-xs-12 col-sm-6">
                <div class="input-group date">
                    <input type="date" th:type="date" class="form-control"
                           th:id="loadingStartDate" th:name="loadingStartDate"
                           th:value="${#dates.format(filter.loadingStartDate, dateFormat)}" />
                </div>
            </div>
        </div>
        <div class="row">
            <div class="form-group">
                <div class="col-xs-12 col-sm-12">
                    <button type="submit" class="btn btn-primary btn-lg pull-right">
                        submit
                    </button>
                </div>
            </div>
        </div>
    </form>
like image 28
YLombardi Avatar answered Oct 02 '22 18:10

YLombardi


I think there are trouble because Spring don't understand that inputted value is a Date. Is your variable myDate of type java.util.Date?

If it is, try to register a formatter like that :

package your.pack.formatter;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.format.Formatter;

public class DateFormatter implements Formatter<Date> { 

    final String defaultDateFormat = "dd.MM.yyyy";

    @Override
    public String print(Date object, Locale locale) {
        return new SimpleDateFormat(defaultDateFormat).format(object);
    }

    @Override
    public Date parse(String text, Locale locale) throws ParseException {
        return DateUtils.parseDate(text, defaultDateFormat);
    }
}

Then register it in your configuration :

@Configuration
public class ConversionServiceConfig extends WebMvcConfigurerAdapter {

   @Bean
   public DateFormatter dateFormatter() {
       return new DateFormatter();
   }

   @Override
   public void addFormatters(FormatterRegistry registry) {
       registry.addFormatter(dateFormatter());
   }
}

But I don't know how to make it works dynamically as you want...

Are users preferences store in database or in properties?

like image 20
sanluck Avatar answered Oct 02 '22 19:10

sanluck