Description
tl;dr
The default Spring MVC date/time form binding is slightly at odds with the way modern date and time form input fields are submitted by browsers. This results in the need for (quite a bit of) explicit binding configuration to make default form submissions from browsers work.
Details
The current Spring MVC way of binding of date value stems from the time when date and time values were handled through simple <input type="text" … />
elements. When working with those, form values were usually rendered in the format that's matching the user's browser's locale. On form submissions Spring MVC would then consider the Locale
provided (usually in an HTTP header sent by the browser or a value otherwise obtained from the request) when parsing the value provided.
The form elements of type date
and time
have been specified to contain values that roughly match the ISO formats for dates and times. That means that when using those explicit types, the browser now sends an ISO format but still a browser specific locale. Depending on the actual locale, this might cause the binding to fail, depending on whether the format backing the locale is compatible with the ISO format.
Assume a form backing object like this
class Form {
LocalDate date;
LocalTime time;
}
E.g. a browser submission with German locale with a date of 2019-01-15
and time of 20:15
would fail for the date binding, as the German flavor is 15.01.2019
but binding the time would
succeed as by accident the German format parses ISO times properly.
Solutions / Workarounds
Annotating the form backing object
A very obvious way of fixing this is annotating all relevant form object fields with @DateTimeFormat(iso = …)
. While this works, it's a bit tedious one you have many of these types. Also, it's a bit surprising that the default configuration is not able to bind the standard values produced by the browser in the first place. On the other hand, Spring MVC cannot actually know whether the value it's supposed to bind is coming from a text field or a date specific one, especially considering the large amounts of existing applications.
Define ISO formats to be used for all date types globally
Using Spring MVC's WebMvcConfigurer
one can tweak the form binding to globally use ISO formats like this:
@Bean
WebMvcConfigurer isoDatesByDefault() {
return new WebMvcConfigurer() {
public void addFormatters(FormatterRegistry registry) {
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setUseIsoFormat(true);
registrar.registerFormatters(registry);
}
}
}
This has the advantage of avoiding the need for additional annotations on form backing objects. It of course breaks the date submission in any other formats (e.g. from plain text input fields using a format other than ISO).
Questions / Remarks
The Spring reference documentation seems a bit dated as it discusses the form binding of dates from the legacy Date
point of view. Also, declaring a custom FormattingConversionService
is not really idiomatic in a Spring Boot context in which you'd rather declare a WebMvcConfigurer
to tweak the service.
I was wondering whether it might makes sense to let Boot expose a property to enable the configuration tweaks shown above to simplify switching to the ISO format in general. There's precedence in ISO date specific configuration in the Jackson space.