上一篇中使用的是全局使用字符串处理时间参数。本文提供第三种处理方式:使用全局时间戳方式处理入参时间,如入参:1657096088961,这种方式同全局字符串处理方式类似,置于两者用哪种,看个人所需。
- 优点:参数上不需要加任何注解,即可全局统一入参格式&出参格式
- 缺点:时间报文不可读
- 日期时间对象全注解方式出入参:https://www.jianshu.com/p/7049d5161c16
- 日期时间对象全局字符串出入参:https://www.jianshu.com/p/545859a91bcd
package com.vip.tools.format.timestamp.constant;
public interface TimeConstants {
String LOCAL_TIME_REGULAR = "^\d{1,2}:\d{1,2}:\d{1,2}$";
String TIMESTAMP_REGULAR = "^\d{13}$";
String DEFAULT_TIME_FORMAT = "HH:mm:ss";
}
转换类核心代码:TimestampToDateConverter
import com.vip.tools.format.timestamp.constant.TimeConstants; import lombok.SneakyThrows; import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.lang.Nullable; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneOffset; import java.util.Date; @Configuration public class TimestampToDateConverterJackson参数转换器配置{ @Bean(name = "localDateTimeConverter") public Converter localDateTimeConverter() { return new Converter () { @Override public LocalDateTime convert(@Nullable String source) { if(StringUtils.isBlank(source)){ return null; } if(!source.matches(TimeConstants.TIMESTAMP_REGULAR)){ throw new IllegalArgumentException("Invalid Time Value of Type String:" + source); } return new Date(Long.parseLong(source)).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime(); } }; } @Bean(name = "localDateConverter") public Converter localDateConverter() { return new Converter () { @Override public LocalDate convert(@Nullable String source) { if(StringUtils.isBlank(source)){ return null; } if(!source.matches(TimeConstants.TIMESTAMP_REGULAR)){ throw new IllegalArgumentException("Invalid Time Value of Type String:" + source); } return new Date(Long.parseLong(source)).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate(); } }; } @Bean(name = "localTimeConverter") public Converter localTimeConverter() { return new Converter () { @Override public LocalTime convert(@Nullable String source) { if(StringUtils.isBlank(source)){ return null; } if(!source.matches(TimeConstants.LOCAL_TIME_REGULAR)){ throw new IllegalArgumentException("Invalid Time Value of Type String:" + source); } return LocalTime.parse(source); } }; } @Bean(name = "dateConverter") public Converter dateConverter() { return new Converter () { @Override @SneakyThrows public Date convert(@Nullable String source) { if (StringUtils.isBlank(source)) { return null; } if (!source.matches(TimeConstants.TIMESTAMP_REGULAR)) { throw new IllegalArgumentException("Invalid Time Value of Type String:" + source); } return new Date(Long.parseLong(source)); } }; } }
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.vip.tools.format.timestamp.constant.TimeConstants;
import lombok.SneakyThrows;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;
public class JacksonHttpMessageConverter extends MappingJackson2HttpMessageConverter {
public JacksonHttpMessageConverter() {
handleDateTime();
}
private void handleDateTime() {
ObjectMapper objectMapper = this.getObjectMapper();
// Date出入参格式化
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// Java8时间格式化
JavaTimeModule javaTimeModule = new JavaTimeModule();
// 出参格式化
javaTimeModule.addSerializer(LocalDateTime.class, new MyLocalDateTimeSerializer());
javaTimeModule.addSerializer(LocalDate.class, new MyLocalDateSerializer());
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT)));
// 入参格式化
javaTimeModule.addDeserializer(LocalDateTime.class, new MyLocalDateTimeDeserializer());
javaTimeModule.addDeserializer(LocalDate.class, new MyLocalDateDeserializer());
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TimeConstants.DEFAULT_TIME_FORMAT)));
// 把“忽略重复的模块注册”禁用,否则下面的注册(LocalDateTime)不生效
objectMapper.disable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
objectMapper.registerModule(javaTimeModule);
// 然后再设置为生效,避免被其他地方覆盖
objectMapper.enable(MapperFeature.IGNORE_DUPLICATE_MODULE_REGISTRATIONS);
// 设置格式化内容
this.setObjectMapper(objectMapper);
}
public static class MyLocalDateTimeSerializer extends JsonSerializer {
@Override
@SneakyThrows
public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
jsonGenerator.writeNumber(localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
}
}
public static class MyLocalDateSerializer extends JsonSerializer {
@Override
@SneakyThrows
public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
jsonGenerator.writeNumber(localDate.atStartOfDay(ZoneOffset.ofHours(8)).toInstant().toEpochMilli());
}
}
public static class MyLocalDateTimeDeserializer extends JsonDeserializer {
@Override
@SneakyThrows
public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
return LocalDateTime.ofEpochSecond(jsonParser.getLongValue() / 1000, 0, ZoneOffset.ofHours(8));
}
}
public static class MyLocalDateDeserializer extends JsonDeserializer {
@Override
@SneakyThrows
public LocalDate deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) {
return new Date(jsonParser.getLongValue()).toInstant().atOffset(ZoneOffset.of("+8")).toLocalDate();
}
}
}
WebMvcConfiguration
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
import java.util.List;
@Configuration
@RequiredArgsConstructor
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
private final Converter localDateTimeConverter;
private final Converter localDateConverter;
private final Converter localTimeConverter;
private final Converter dateConverter;
@Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(localDateTimeConverter);
registry.addConverter(localDateConverter);
registry.addConverter(localTimeConverter);
registry.addConverter(dateConverter);
}
@Override
public void configureMessageConverters(List> converters) {
super.configureMessageConverters(converters);
JacksonHttpMessageConverter converter = new JacksonHttpMessageConverter();
converters.add(converter);
}
}
开始写测试代码
DTO
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
@Data
public class TinyDTO {
private Date date;
private LocalDate localDate;
private LocalDateTime localDateTime;
private LocalTime localTime;
private String name;
}
controller
import com.vip.tools.format.timestamp.dto.TinyDTO;
import com.vip.tools.normal.UuidUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
@RestController
@RequestMapping(value = "/timestamp_format")
public class TimestampDateFormatController {
@PostMapping("/test")
public TinyDTO format(@RequestParam Date date, @RequestParam LocalDate localDate, @RequestParam LocalDateTime localDateTime
, @RequestParam LocalTime localTime, @RequestBody TinyDTO dto) {
System.out.println(date);
System.out.println(localDate);
System.out.println(localDateTime);
System.out.println(localTime);
System.out.println(dto);
return dto;
}
}
测试
URL: http://localhost:9600/timestamp_format/test?date=1610595117250&localDate=1610595117250&localDateTime=1610595117250&localTime=12:00:23
Body参数:
{
"date": 1610595117250,
"localDate": 1610595117250,
"localDateTime": 1610595117250,
"localTime": "23:59:59",
"name": "TEST"
}
控制台打印:
Thu Jan 14 11:31:57 CST 2021 2021-01-14 2021-01-14T11:31:57.250 12:00:23 TinyDTO(date=Thu Jan 14 11:31:57 CST 2021, localDate=2021-01-14, localDateTime=2021-01-14T11:31:57, localTime=23:59:59, name=TEST)
响应结果:
{
"date": 1610595117250,
"localDate": 1610553600000,
"localDateTime": 1610595117000,
"localTime": "23:59:59",
"name": "TEST"
}
日期对象全局字符串出入参配置完成。
总结: 以上介绍了三种时间参数转换的方式,全注解方式是最不推荐的,推荐使用全局字符串转换方式,这样便于前端格式化日期格式,以及时间可阅读,全局时间戳转换方式按需使用。



