Skip to content
Advertisement

Spring Boot unable to set global date format for REST API input

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

  1. It must accept the format I specificy for input
  2. 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

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement