本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net
- 使用 LocalDateDeserializer 与 LocalDateTimeDeserializer 对前端传输的时间戳格式进行统一反序列成 LocalDate 或者 LocalDateTime 类型,这样有利于前端在传输时间时统一者传时间戳,后端在接收时统一使用 LocalDate 或者 LocalDateTime 类型来接收,这样就不会因为格式的问题而烦脑。
/**
* 扩展jackson反序列, 使其支持时间戳转LocalDate
*
* @author: mxj
*/
public class LocalDateExtDeserializer extends LocalDateDeserializer {
public LocalDateExtDeserializer() {
super(DateTimeFormatter.ISO_LOCAL_DATE);
}
public LocalDateExtDeserializer(DateTimeFormatter dtf) {
super(dtf);
}
@Override
protected JsonDeserializer<LocalDate> withDateFormat(DateTimeFormatter formatter) {
return new LocalDateExtDeserializer(formatter);
}
@Override
public LocalDate deserialize(JsonParser parse, DeserializationContext context) throws IOException {
if (parse.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(parse.getLongValue()), ZoneId.systemDefault()).toLocalDate();
}
return super.deserialize(parse, context);
}
}
/**
* 扩展jackson反序列, 使其支持时间戳转LocalDateTime
*
* @author: mxj
*/
public class LocalDateTimeExtDeserializer extends LocalDateTimeDeserializer {
public LocalDateTimeExtDeserializer() {
super(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
public LocalDateTimeExtDeserializer(DateTimeFormatter formatter) {
super(formatter);
}
@Override
protected JsonDeserializer<LocalDateTime> withDateFormat(DateTimeFormatter formatter) {
return new LocalDateTimeExtDeserializer(formatter);
}
@Override
public LocalDateTime deserialize(JsonParser parse, DeserializationContext context) throws IOException {
if (parse.hasToken(JsonToken.VALUE_NUMBER_INT)) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(parse.getLongValue()), ZoneId.systemDefault());
}
return super.deserialize(parse, context);
}
}
- 使用 LocalDateSerializerh 或者 LocalDateTimeSerializer 对后端接口中 LocalDate 或者 LocalDateTime 类型返回给前端时间进行统一序列化成时间戳,这样对于前端来说可以根据时间戳统一进行封装进行统一处理。
package com.cell.jackson.serializer;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
/**
* 将LocalDate转为时间戳
*
* @author: mxj
*/
public class LocalDateExtSerializer extends LocalDateSerializer {
private final Boolean writeDatesAsTimestamps;
public LocalDateExtSerializer(Boolean writeDatesAsTimestamps) {
super(DateTimeFormatter.ISO_LOCAL_DATE);
this.writeDatesAsTimestamps = writeDatesAsTimestamps;
}
protected LocalDateExtSerializer(LocalDateSerializer base,
Boolean useTimestamp, DateTimeFormatter dtf, JsonFormat.Shape shape, Boolean writeDatesAsTimestamps) {
super(base, useTimestamp, dtf, shape);
this.writeDatesAsTimestamps = writeDatesAsTimestamps;
}
@Override
protected LocalDateSerializer withFormat(Boolean useTimestamp, DateTimeFormatter dtf, JsonFormat.Shape shape) {
return new LocalDateExtSerializer(this, useTimestamp, dtf, shape, writeDatesAsTimestamps);
}
@Override
public void serialize(LocalDate localDate, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (writeDatesAsTimestamps) {
jsonGenerator.writeNumber(localDate.atStartOfDay(ZoneOffset.systemDefault()).toInstant().toEpochMilli());
} else {
super.serialize(localDate, jsonGenerator, serializerProvider);
}
}
}
package com.cell.jackson.serializer;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
/**
* 将LocalDateTime转为时间戳
*
* @author: mxj
*/
public class LocalDateTimeExtSerializer extends LocalDateTimeSerializer {
private final Boolean writeDatesAsTimestamps;
public LocalDateTimeExtSerializer(Boolean writeDatesAsTimestamps) {
super(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
this.writeDatesAsTimestamps = writeDatesAsTimestamps;
}
protected LocalDateTimeExtSerializer(DateTimeFormatter f, Boolean writeDatesAsTimestamps) {
super(f);
this.writeDatesAsTimestamps = writeDatesAsTimestamps;
}
@Override
protected LocalDateTimeSerializer withFormat(Boolean useTimestamp, DateTimeFormatter f, JsonFormat.Shape shape) {
return new LocalDateTimeExtSerializer(f, writeDatesAsTimestamps);
}
@Override
public void serialize(LocalDateTime localDateTime, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (writeDatesAsTimestamps) {
jsonGenerator.writeNumber(localDateTime.toInstant(OffsetDateTime.now().getOffset()).toEpochMilli());
} else {
super.serialize(localDateTime, jsonGenerator, serializerProvider);
}
}
}
- 配置好以上两步后, 要在 WebMvcConfigurer 配置类中进行增加 RequestBody 内容转换器(缺点就是只针对于 Controller 层中的接中 @RequestBody 注解中的参数生效,解决方案在后头)
/**
* WebMvcConfig
*
* @author: mxj
* @Date 2023/1/28 11:29
*/
@ConditionalOnClass(WebMvcConfigurer.class)
public class WebMvcConfiguration implements WebMvcConfigurer {
/**
* 增加了Jackson对Jdk8Time的支持
*
* @param converters RequestBody内容转换器列表, 这里只需要对jackson提供的转换器进行拦截
*/
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (MappingJackson2HttpMessageConverter.class.isAssignableFrom(converter.getClass())) {
ObjectMapper objectMapper = ((MappingJackson2HttpMessageConverter) converter).getObjectMapper();
SimpleModule dateModule = new SimpleModule();
//jackson序列化
dateModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
dateModule.addSerializer(LocalDate.class, new LocalDateExtSerializer(true));
dateModule.addSerializer(LocalDateTime.class, new LocalDateTimeExtSerializer(true));
//jackson反序列化, 默认的格式为 yyyy-MM-ddTHH:mm:ss, 故需要配置
dateModule.addDeserializer(LocalDate.class, new LocalDateExtDeserializer());
dateModule.addDeserializer(LocalDateTime.class, new LocalDateTimeExtDeserializer());
objectMapper.registerModule(dateModule);
}
}
}
}
- 在第 3 点中的缺点,就是要解决掉 @PathVariable 与 @RequestParam 等注解的接收参数时间转换问题(以 LongLocalDateTime 为例子)。利以 Converter 将前端数据转换为 java 对应的对象
package com.cell.converter;
import com.cell.converter.handler.localDateTime.LocalDateTimeConverterHandler;
import com.cell.converter.handler.localDateTime.LongLocalDateTimeConverter;
import com.cell.converter.handler.localDateTime.StringLocalDateTimeConverter;
import com.cell.webExport.config.response.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.convert.converter.Converter;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 统一转换成LocalDateTime
*
* @Author MXJ
*/
@Slf4j
public class LocalDateTimeConverter implements Converter<String, LocalDateTime> {
private static List<LocalDateTimeConverterHandler> localDateTimeConverterHandlerList = new ArrayList<>();
static {
//可以配置多种类型格式解释(这理配置了两种格式)
localDateTimeConverterHandlerList.add(new StringLocalDateTimeConverter());
localDateTimeConverterHandlerList.add(new LongLocalDateTimeConverter());
}
@Override
public LocalDateTime convert(String source) {
for (LocalDateTimeConverterHandler item : localDateTimeConverterHandlerList) {
try {
return item.getLocalDateTime(source);
} catch (Exception e) {
log.info("LocalDateTime时间格式解释失败");
}
}
throw new BusinessException("LocalDateTime时间格式解释失败");
}
}
package com.cell.converter.handler.localDateTime;
import java.time.LocalDateTime;
/**
* 解释类型接口
*
* @Author MXJ
*/
public interface LocalDateTimeConverterHandler {
/**
* 获取LocalDateTime
*/
LocalDateTime getLocalDateTime(String source);
}
package com.cell.converter.handler.localDateTime;
import lombok.extern.slf4j.Slf4j;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
/**
* LocalDateTime时间戳类型(根据时间戳解释成LocalDateTime)
*
* @Author MXJ
*/
@Slf4j
public class LongLocalDateTimeConverter implements LocalDateTimeConverterHandler {
/**
* 获取LocalDateTime
*/
@Override
public LocalDateTime getLocalDateTime(String source) {
Instant instant = Instant.ofEpochMilli(Long.parseLong(source));
return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
}
}
package com.cell.converter.handler.localDateTime;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* LocalDateTime字符串类型
* @Author MXJ
*/
public class StringLocalDateTimeConverter implements LocalDateTimeConverterHandler{
/**
* 获取LocalDateTime(不同种格式)
* @param source
* @return
*/
@Override
public LocalDateTime getLocalDateTime(String source) {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yy-M-d ah:mm");
return LocalDateTime.parse(source, formatter);
}catch (Exception e){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
return LocalDateTime.parse(source, formatter);
}
}
}
- 以上就是统一实现前后端时间格式问题(请大家多多指教)