上一篇中使用的是全局使用字符串处理时间参数。本文提供第三种处理方式:使用全局时间戳方式处理入参时间,如入参:1657096088961,这种方式同全局字符串处理方式类似,置于两者用哪种,看个人所需。
- 优点:参数上不需要加任何注解,即可全局统一入参格式&出参格式
- 缺点:时间报文不可读
传送链接:
- 日期时间对象全注解方式出入参:https://www.jianshu.com/p/7049d5161c16
- 日期时间对象全局字符串出入参:https://www.jianshu.com/p/545859a91bcd
代码实现:
定义相关常量:TimeConstants
package com.vip.tools.format.timestamp.constant;
/**
* 时间转换常量
*
* @author wgb
* @date 2021/1/15 15:00
*/
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;
/**
* 自定义类型转换, @RequestParam/@PathVariable的日期字符串转换对应日期类型
*
* @author wgb
* @date 2021/01/14 15:57
*/
@Configuration
public class TimestampToDateConverter {
@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));
}
};
}
}
Jackson参数转换器配置
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;
/**
* 使用官方自带的json格式类库
* 可以处理多种格式化方式,这里只使用了时间格式化
*
* @author wgb
* @date 2020/11/25 18:07
*/
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);
}
/**
* LocalDateTime序列化为时间戳
*/
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());
}
}
/**
* LocalDate序列化时为时间戳
*/
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());
}
}
/**
* 时间戳反序列化为LocalDateTime
*/
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));
}
}
/**
* 时间戳反序列化为LocalDate
*/
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.ByteArrayHttpMessageConverter;
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;
/**
* WebMvcConfiguration
*
* @author wgb
* @date 2020/11/25 16:45
*/
@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);
}
/**
* extends WebMvcConfigurationSupport
* 以下 spring-boot: jackson时间格式化 配置 将会失效
* spring.jackson.time-zone=GMT+8
* spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
* 原因: 会覆盖 @EnableAutoConfiguration 关于 WebMvcAutoConfiguration 的配置
*/
@Override
public void configureMessageConverters(List> converters) {
super.configureMessageConverters(converters);
JacksonHttpMessageConverter converter = new JacksonHttpMessageConverter();
converters.add(converter);
// 添加二进制数组转换器:用以文件下载时二进制流的响应
converters.add(new ByteArrayHttpMessageConverter());
}
}
开始写测试代码
DTO
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Date;
/**
* description
*
* @author wgb
* @date 2021/1/14 15:57
*/
@Data
public class TinyDTO {
/**
* date
*/
private Date date;
/**
* localDate
*/
private LocalDate localDate;
/**
* localDateTime
*/
private LocalDateTime localDateTime;
/**
* localTime
*/
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;
/**
* 时间格式化 控制器
*
* @author wgb
* @date 2021/1/15 13:25
*/
@RestController
@RequestMapping(value = "/timestamp_format")
public class TimestampDateFormatController {
/**
* 格式化测试
*
* @return
*/
@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"
}
日期对象全局字符串出入参配置完成。
总结:以上介绍了三种时间参数转换的方式,全注解方式是最不推荐的,推荐使用全局字符串转换方式,这样便于前端格式化日期格式,以及时间可阅读,全局时间戳转换方式按需使用。