Spring Boot 全局时间格式化:Date 和 LocalDateTime 统一配置实战
做 Java 后端开发,时间格式化是高频痛点。接口返回的时间字段,前端要 yyyy-MM-dd HH:mm:ss,你总不能每个 DTO 字段都加 @JsonFormat 注解。更麻烦的是项目里 java.util.Date 和 java.time.LocalDateTime 混用,配了一种另一种不生效。
这篇把 3 种全局配置方式整理一遍,代码直接复制就能用。
问题场景
先看一个典型的 DTO:
@Data
public class OrderDTO {
private LocalDateTime createTime;
private Date updateTime;
}
接口返回结果里,时间字段默认是时间戳或 ISO 格式,前端展示不友好。如果你在 application.properties 里配了:
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
你会发现只对 Date 生效,LocalDateTime 还是原样。这就是问题所在。
方式一:@JsonFormat 注解(局部方案)
最直接的方式,每个字段加注解:
@Data
public class OrderDTO {
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
优点:简单粗暴,每个字段可以自定义格式。
缺点:不是全局方案,字段多了注解满天飞,维护成本高。
适合:个别字段需要特殊格式的场景。
方式二:@JsonComponent(推荐)
这是我个人最推荐的方式。自定义一个全局配置类,同时处理 Date 和 LocalDateTime:
@JsonComponent
public class DateFormatConfig {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
/**
* Date 类型全局时间格式化
*/
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilder() {
return builder -> {
TimeZone tz = TimeZone.getTimeZone("GMT+8");
SimpleDateFormat df = new SimpleDateFormat(pattern);
df.setTimeZone(tz);
builder.failOnEmptyBeans(false)
.failOnUnknownProperties(false)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.dateFormat(df);
};
}
/**
* LocalDateTime 类型全局时间格式化
*/
@Bean
public LocalDateTimeSerializer localDateTimeSerializer() {
return new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(pattern));
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
return builder -> builder.serializerByType(LocalDateTime.class, localDateTimeSerializer());
}
}
关键点:
Date类型通过Jackson2ObjectMapperBuilderCustomizer配置SimpleDateFormatLocalDateTime通过自定义LocalDateTimeSerializer处理- 时区统一设为
GMT+8,避免服务器时区不一致导致时间差
和 @JsonFormat 的配合:如果你有个别字段想用不同格式,直接在那个字段上加 @JsonFormat,注解优先级高于全局配置。这样大部分字段统一格式,少数特殊字段单独覆盖,灵活性最高。
方式三:@Configuration 手动配置 ObjectMapper
这种方式和方式二效果一样,但更底层——手动创建 ObjectMapper 并注册模块:
@Configuration
public class DateFormatConfig2 {
@Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
private String pattern;
public static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Bean
@Primary
public ObjectMapper serializingObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
objectMapper.registerModule(javaTimeModule);
return objectMapper;
}
// Date 序列化
@Component
public class DateSerializer extends JsonSerializer<Date> {
@Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(dateFormat.format(date));
}
}
// Date 反序列化
@Component
public class DateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
try {
return dateFormat.parse(p.getValueAsString());
} catch (ParseException e) {
throw new RuntimeException("日期解析失败", e);
}
}
}
// LocalDateTime 序列化
public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(DateTimeFormatter.ofPattern(pattern)));
}
}
// LocalDateTime 反序列化
public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern(pattern));
}
}
}
注意:这种方式手动注册了 ObjectMapper 并标记 @Primary,会覆盖 Spring Boot 的自动配置。用了这种方式后,字段上的 @JsonFormat 注解将不再生效——全局配置说了算。
三种方式对比
| 方式 | 适用类型 | 灵活性 | 推荐度 |
|---|---|---|---|
| @JsonFormat | 单个字段 | 最高,每字段自定义 | 局部特殊场景 |
| @JsonComponent | Date + LocalDateTime | 高,全局+注解覆盖 | 最推荐 |
| @Configuration | Date + LocalDateTime | 中,全局统一 | 需要完全掌控时用 |
几个容易踩的坑
- 时区问题:服务器部署在云上,默认时区可能是 UTC,不设
GMT+8会差 8 小时。 - 反序列化也要配:只配了序列化(返回给前端),没配反序列化(接收前端传的时间),传参时还是会报错。
- LocalDate 和 LocalTime:如果项目里用到了
LocalDate、LocalTime,也要单独配对应的 serializer。 - Spring Boot 版本差异:Spring Boot 2.x 和 3.x 的 Jackson 配置类路径有变化,注意对应版本的依赖。
写在最后
时间格式化这种小细节,看似不起眼,但每个项目都要配一遍。把这份配置类存下来,新项目直接复制,省得每次重新查。
参考原文可以看这里——查看原文了解更多细节。
项目里时间格式化你是怎么配的?评论区交流。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_70361709/article/details/169553997



