云-缩影头像

Spring Boot 全局时间格式化:Date 和 LocalDateTime 统一配置实战

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 配置 SimpleDateFormat
  • LocalDateTime 通过自定义 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单个字段最高,每字段自定义局部特殊场景
@JsonComponentDate + LocalDateTime高,全局+注解覆盖最推荐
@ConfigurationDate + LocalDateTime中,全局统一需要完全掌控时用

几个容易踩的坑

  1. 时区问题:服务器部署在云上,默认时区可能是 UTC,不设 GMT+8 会差 8 小时。
  2. 反序列化也要配:只配了序列化(返回给前端),没配反序列化(接收前端传的时间),传参时还是会报错。
  3. LocalDate 和 LocalTime:如果项目里用到了 LocalDate、LocalTime,也要单独配对应的 serializer。
  4. Spring Boot 版本差异:Spring Boot 2.x 和 3.x 的 Jackson 配置类路径有变化,注意对应版本的依赖。

写在最后

时间格式化这种小细节,看似不起眼,但每个项目都要配一遍。把这份配置类存下来,新项目直接复制,省得每次重新查。

参考原文可以看这里——查看原文了解更多细节。

项目里时间格式化你是怎么配的?评论区交流。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_70361709/article/details/169553997

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--