hello,大家好,我是灰小猿!
在前后端分离的spring项目中,往往需要处理时间格式的属性的前后端传递问题,当下比较流行的作为时间格式属性的类型有date、LocalDateTime等,但是自jdk8以来,更推荐使用的是LocalDateTime格式,
但是在进行前后端数据传输时,我们可能会面临一个问题就是字符串格式的时间和LocalDateTime格式的时间转换问题,json的序列化和反序列化操作往往不能将这两种格式的时间进行直接转换,就会导致我们的代码报无法转换的错误,那么我们今天就来一一解决这些问题。
1、Controller中以参数形式直接接收date或LocalDateTime格式的时间报错解决
示例接口如下:
java
@GetMapping("/test")
public Result<String> testDate(@RequestParam LocalDateTime localDateTime) {
return Result.success();
}
对于这种接口,如果不对时间格式进行处理,那么就会直接报字符串无法转换的错误
对此我们需要增加如下WebMvcConfigurer来控制时间字符的转换规则,如下配置支持字符串的时间向参数为Date和LocalDateTime格式转换。
java
/**
* 解决前端请求中对于字符串类型的时间,主动转换为LocalDateTime或date格式
*/
@Configuration
public class DateFormatterConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToDateConverter());
registry.addConverter(new StringToLocalDateTimeConverter());
}
/**
* 字符串与date转换器
*/
static class StringToDateConverter implements Converter<String, Date> {
@Override
public Date convert(@Nonnull String source) {
if (ObjectUtil.isNull(source) || source.trim().isEmpty()) {
return null;
}
String text = source.trim();
Date date = DateUtils.parseDate(text);
if (ObjectUtil.isNull(date)) {
throw new IllegalArgumentException("日期格式不支持: " + text);
}
return date;
}
}
/**
* 字符串与LocalDateTime转换器
*/
static class StringToLocalDateTimeConverter implements Converter<String, LocalDateTime> {
@Override
public LocalDateTime convert(@Nonnull String source) {
if (ObjectUtil.isNull(source) || source.trim().isEmpty()) {
return null;
}
String text = source.trim();
Date date = DateUtils.parseDate(text);
if (ObjectUtil.isNull(date)) {
throw new IllegalArgumentException("日期格式不支持: " + text);
}
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
return instant.atZone(zoneId).toLocalDateTime();
}
}
}
但是需要注意的是,这种配置只适用于时间类型参数直接放置在方法参数上的情况,但是往往我们的传参都会封装在对象内部,这种情况,就需要使用如下方式:
2、时间参数在请求和返回对象内部时时间格式转换解决
对于LocalDateTime类型,Jackson的默认序列化器会将其序列化为数组形式(如[2025,10,24,10,51,30,534759800])或者字符串(如"2025-10-24T10:51:30.5347598"),但是这通常不是我们想要的标准格式。
如果要全局配置LocalDateTime的序列化格式,我们可以使用Jackson的JavaTimeModule,并配合配置类来实现。
解决方案
1、添加依赖 :确保项目中包含了Jackson的JavaTimeModule依赖,通常我们使用jackson-datatype-jsr310。
XML
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
2、配置全局日期格式:通过配置类自定义ObjectMapper,注册JavaTimeModule,并设置日期格式。
java
@Configuration
public class JacksonDateTimeConfig {
private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("HH:mm:ss");
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> {
// 关闭时间戳输出,使用字符串格式
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 序列化格式
builder.serializers(new LocalDateTimeSerializer(DATETIME_FMT));
builder.serializers(new LocalDateSerializer(DATE_FMT));
builder.serializers(new LocalTimeSerializer(TIME_FMT));
// 反序列化:优先使用自定义的 LocalDateTime 以支持多格式字符串
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addDeserializer(LocalDateTime.class, new MultiPatternLocalDateTimeDeserializer());
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DATE_FMT));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(TIME_FMT));
// 保留可扩展点
SimpleModule extraModule = new SimpleModule();
builder.modules(javaTimeModule, extraModule);
};
}
/**
* 兼容多种字符串日期格式的 LocalDateTime 反序列化,复用 DateUtils.parseDate。
*/
static class MultiPatternLocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getValueAsString();
if (text == null || text.trim().isEmpty()) {
return null;
}
Date parsed = DateUtils.parseDate(text.trim());
if (parsed == null) {
// 回退:尝试按常见格式解析,交由默认反序列器兜底
try {
return LocalDateTime.parse(text.trim(), DATETIME_FMT);
} catch (Exception ignore) {
return (LocalDateTime) ctxt.handleWeirdStringValue(LocalDateTime.class, text, "无法解析为 LocalDateTime");
}
}
Instant instant = parsed.toInstant();
return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
}
}
}
当然,如果你的时间格式使用的是Date格式,也可以直接在application.yml中做如下配置即可:
XML
# 设置全局日期时间格式
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
# 针对Java 8日期时间API的配置
serialization:
write-dates-as-timestamps: false
但是,对于LocalDateTime类型,仅仅使用spring.jackson.date-format可能不够,因为Jackson默认使用jackson-datatype-jsr310模块来处理Java8时间类型,而该模块默认使用ISO格式。所以,我们通常需要像上面那样自定义序列化器。
@JsonFormat注解
补充:如果上述配置仍然不生效,补救措施是直接在对应的属性上使用JsonFormat注解,具体如下:
java
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class MyEntity {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createTime;
// 省略 getter 和 setter
}
至此配置:我们项目中的LocalDateTime 类型字段返回给前端时就会是 "yyyy-MM-dd HH:mm:ss" 这样的标准格式,对于前端传递过来的字符类型的时间格式,也可以自动的帮我们转换为LocalDateTime 格式。省去了属性单独配置的工作。
我是灰小猿,我们下期见!