I am trying to set a global format for date-time, so that I don’t have to annotate each and every method or on DTO fields.
I have tried to configure it globally, the only impact it had was on API response, it is formatting the date in specific pattern.
But it does not accept it as an input format.
@Configuration public class DateFormatConfig { public DateFormatConfig() { TimeZone.setDefault(TimeZone.getTimeZone("UTC")); } @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder -> { builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); builder.serializers(new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); }; } }
Exception message
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.Date'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@org.springframework.web.bind.annotation.RequestParam java.util.Date] for value '2022-01-03 19:32:22'; nested exception is java.lang.IllegalArgumentException .... Caused by: java.lang.IllegalArgumentException: null at java.base/java.util.Date.parse(Date.java:616) ~[na:na] at java.base/java.util.Date.<init>(Date.java:274) ~[na:na] at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na] at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490) ~[na:na]
A REST controller method where the call lands is as follow
public Response update(@PathVariable("id") Long id, @RequestParam(value = "someTime", required = false) Date someTime)
My expectaion from above configuration is, it should work for both input and output of the API
- It must accept the format I specificy for input
- It must accept null as value
Everything works fine if I use @DateTimeFormat
annotation, which means for each field I have to make such changes
@RequestParam(value = "someTime", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date someTime
Advertisement
Answer
you should use
@Bean public Converter<String, Date> stringDateConverter() { return new Converter<String, Date>() { final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Override public Date convert(@NonNull String source) { return Date.from(formatter.parse(source, LocalDateTime::from).toInstant(ZoneOffset.UTC)); } }; }
AbstractNamedValueMethodArgumentResolver#resolveArgument
use WebDataBinder
,WebDataBinder
use ConversionService
registered in WebMvcAutoConfiguration.EnableWebMvcConfiguration#mvcConversionService
, and this converter will be registered in org.springframework.boot.convert.ApplicationConversionService#addBeans