Home  >  Article  >  Java  >  How to handle Spring Boot date and time

How to handle Spring Boot date and time

PHPz
PHPzforward
2023-05-18 14:22:06970browse

GET request and POST form date and time string format conversion

This situation should be treated differently from when time is used as a Json string, because the bottom layer of the front-end json to back-end pojo uses the Json serialization Jackson tool (HttpMessgeConverter); when the time string is passed in as a normal request parameter, Converter is used for conversion. There is a difference in the processing methods between the two.

Use custom parameter converter (Converter)

Implementationorg.springframework.core.convert.converter.Converter, custom parameter converter, as follows:

@Configuration
public class DateConverterConfig {
    @Bean
    public Converter<String, LocalDate> localDateConverter() {
       return new Converter<String, LocalDate>() {
            @Override
            public LocalDate convert(String source) {
                return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }
        };
    }
    @Bean
    public Converter<String, LocalDateTime> localDateTimeConverter() {
        return new Converter<String, LocalDateTime>() {
            @Override
            public LocalDateTime convert(String source) {
                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            }
        };
    }
}

Comment: The above two beans will be injected into the parameter parser of spring mvc (it seems to be called ParameterConversionService). When the incoming string is to be converted to the LocalDateTime class, spring will call the Converter Convert this input parameter.

Note: Regarding the custom parameter converter Converter, there is a pitfall. If the above anonymous inner class is simplified into a lambda expression:

   @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalDate> localDateConverter() {
        return source -> LocalDate.parse(source, DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT));
    }

When the project is started again An exception will occur:

Caused by: java.lang.IllegalArgumentException: Unable to determine source type 1eefd63bbe027a2807ccada294a3372c and target type 8742468051c85b06f0a0af9e3e506b5c for your Converter [com.example.demo126.config. MappingConverterAdapter$$Lambda$522/817994751]; does the class parameterize those types?

Because:

It will be initialized when the web project starts the registrationrequestMappingHandlerAdapter ##WebBindingInitializer

adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer());

And

ConfigurableWebBindingInitializer requires FormattingConversionService, and FormattingConversionService will add all Converter Come in, you need to obtain generic information when adding:

@Override
public void addFormatters(FormatterRegistry registry) {
    for (Converter<?, ?> converter : getBeansOfType(Converter.class)) {
       registry.addConverter(converter);
    }
    for (GenericConverter converter : getBeansOfType(GenericConverter.class)) {
       registry.addConverter(converter);
    }
    for (Formatter<?> formatter : getBeansOfType(Formatter.class)) {
       registry.addFormatter(formatter);
    }
}

Adding Converter.class usually obtains the specific types of two generics through the interface

public ResolvableType as(Class<?> type) {
    if (this == NONE) {
      return NONE;
    }
    Class<?> resolved = resolve();
    if (resolved == null || resolved == type) {
      return this;
    }
    for (ResolvableType interfaceType : getInterfaces()) {
      ResolvableType interfaceAsType = interfaceType.as(type);
      if (interfaceAsType != NONE) {
        return interfaceAsType;
      }
    }
    return getSuperType().as(type);
}

The interface of Lambda expression is Converter, and The specific type cannot be obtained. In this case, the solution is:

  • The simplest method is not to use Lambda expressions and use anonymous inner classes, so that the above problems will not exist

  • Just wait until

    requestMappingHandlerAdapterbean is registered and then add your own converter and it will not be registered in FormattingConversionService

  • @Bean
    @ConditionalOnBean(name = "requestMappingHandlerAdapter")
    public Converter<String, LocalDateTime> localDateTimeConverter() {
      return source -> LocalDateTime.parse(source, DateTimeUtils.DEFAULT_FORMATTER);
    }
can also perform regular matching on the string passed by the front end, such as yyyy-MM-dd HH:mm:ss, yyyy-MM-dd, HH:mm:ss, etc., for matching. to adapt to various scenarios.

@Component
public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String value) {
        /**
         * 可对value进行正则匹配,支持日期、时间等多种类型转换
         * 这里在匹配Date日期格式时直接使用了 hutool 为我们已经写好的解析工具类,这里就不重复造轮子了
         * cn.hutool.core.date.DateUtil
         * @param value
         * @return
         */
        return DateUtil.parse(value.trim());
    }
}

Note: Here, hutool is used directly when matching the Date date format. It is the parsing tool class we have already written. We will not reinvent the wheel here. The following method also uses this tool class. If you want It is also very simple to use this tool class in your own project. Just introduce the hutool dependency in the project pom file, as follows:

<!--hu tool 工具类-->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.1.3</version>
</dependency>

Use Spring annotations

Use spring's own annotations

@DateTimeFormat(pattern = "yyyy-MM-dd"), as follows:

@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date startDate;

If a custom parameter converter is used, Spring will preferentially use this method for processing, that is, Spring annotations will not Take effect.

Use ControllerAdvice with initBinder

@ControllerAdvice
public class GlobalExceptionHandler {
    @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd")));
            }
        });
        binder.registerCustomEditor(LocalDateTime.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalDateTime.parse(text, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            }
        });
        binder.registerCustomEditor(LocalTime.class, new PropertyEditorSupport() {
            @Override
            public void setAsText(String text) throws IllegalArgumentException {
                setValue(LocalTime.parse(text, DateTimeFormatter.ofPattern("HH:mm:ss")));
            }
        });
    }
}

As you can see from the name, this is a loop cut in the controller (global exception capture can also be done here), and the parameters are converted before entering the handler; conversion For our corresponding objects.

Global processing of JSON input parameters and return values

The request type is:

post,content-type=application/json, and the background uses @RequestBody Reception, the default reception and return value format is: yyyy-MM-dd HH:mm:ss

Modify the application.yml file

Add in the application.propertities file The following content:

spring:

jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT 8

Support

(content-type=application/json) The string format in the request is yyyy-MM-dd HH:mm:ss, use @ in the background RequestBody is received, and the return value date is converted to yyyy-MM-dd HH:mm:ss format string;

is not supported (

content-type=application/json ) The strings of yyyy-MM-dd and other types in the request are converted to date; java8dateapi is not supported;

Using Jackson's JSON serialization and deserialization

@Configuration
public class JacksonConfig {
    /** 默认日期时间格式 */
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    /** 默认日期格式 */
    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    /** 默认时间格式 */
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        // 忽略json字符串中不识别的属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 忽略无法转换的对象
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // PrettyPrinter 格式化输出
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        // NULL不参与序列化
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 指定时区
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        // 日期类型字符串处理
        objectMapper.setDateFormat(new SimpleDateFormat(DEFAULT_DATE_TIME_FORMAT));
        // java8日期日期处理
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)));
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)));
        javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));
        objectMapper.registerModule(javaTimeModule);
        converter.setObjectMapper(objectMapper);
        return converter;
    }
}

The above is the detailed content of How to handle Spring Boot date and time. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete