侧边栏壁纸
博主头像
bearjun博客

行动起来,活在当下

  • 累计撰写 102 篇文章
  • 累计创建 90 个标签
  • 累计收到 99 条评论

目 录CONTENT

文章目录

Java 中的 Date、Instant 与 LocalDateTime:全面解析与 Spring Boot 实践

bearjun
2025-06-04 / 0 评论 / 2 点赞 / 22 阅读 / 0 字
温馨提示:
部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

今天在浏览Gitee的时候,我看到了RuoYi-Vue-Plus的一个合并请求update:弃用java.util.Date改为java8 java.time.Instant。显然在 Java 开发中,处理时间是一个非常常见的需求。然而,在 Java 的发展过程中,出现了多个用于处理日期和时间的类,比如 DateInstantLocalDateTime。它们分别来自不同的 API 版本,用途和适用场景也有所不同。

本文将从定义、特点、适用场景以及在 Spring Boot 项目中的使用实例等方面,对这三个常用的时间处理类进行全面分析和对比,帮助开发者更好地选择合适的类型来处理时间相关问题。


一、概述对比表

类名所属包精度是否包含时区是否可变推荐程度
java.util.Datejava.util毫秒❌(内部存储为 UTC 时间戳)✅ 可变⚠️ 不推荐
java.time.Instantjava.time纳秒✅ UTC 时间戳❌ 不可变✅ 推荐
java.time.LocalDateTimejava.time纳秒❌ 无❌ 不可变✅ 推荐

二、逐个解析

java.util.Date

  • 所属 API: Java 1.0 原始时间 API
  • 特点:
    • 内部用的是一个毫秒级的时间戳(从 1970-01-01T00:00:00Z 开始计算)
    • 存在线程安全问题(如 SimpleDateFormat
    • 被标记为过时,不推荐在新项目中使用
  • 适用场景:
    • 旧系统维护或与遗留代码交互
    • 需要兼容老版本 JDK 的环境

示例代码(Spring Boot Controller):

@RestController
public class DateController {

    @GetMapping("/date")
    public String getDate() {
        Date date = new Date();
        return "Current Date: " + date.toString();
    }
}

java.time.Instant

  • 所属 API: Java 8 引入的 java.time
  • 特点:
    • 表示的是一个“时间线上的瞬间点”
    • 精度为纳秒,常用于记录事件发生的真实时间点
    • 是不可变对象,线程安全
  • 适用场景:
    • 日志记录、审计日志、数据库插入时间等需要精确时间点的场景

示例代码(Spring Boot Controller):

@RestController
public class InstantController {

    @GetMapping("/instant")
    public String getInstant() {
        Instant now = Instant.now();
        return "Current Instant (UTC): " + now;
    }
}

输出结果类似:

Current Instant (UTC): 2025-04-05T10:00:00.123Z

java.time.LocalDateTime

  • 所属 API: Java 8 的 java.time
  • 特点:
    • 表示本地日期时间(年月日+时分秒),不包含时区信息
    • 更适合显示给用户看的时间格式
  • 适用场景:
    • 展示给用户的业务时间
    • 与时区无关的本地逻辑判断

示例代码(Spring Boot Controller)

@RestController
public class LocalDateTimeController {

    @GetMapping("/localdatetime")
    public String getLocalDateTime() {
        LocalDateTime now = LocalDateTime.now();
        return "Current LocalDateTime: " + now;
    }
}

输出结果取决于运行环境的时区:

Current LocalDateTime: 2025-04-05T18:00:00.123

三、类型转换详解(Spring Boot 常见操作)

InstantDate

Date date = Date.from(Instant.now());
Instant instant = date.toInstant();

InstantLocalDateTime

需要通过 ZoneId 来指定时区:

import java.time.ZoneId;

// Instant -> LocalDateTime
Instant now = Instant.now();
LocalDateTime localDateTime = LocalDateTime.ofInstant(now, ZoneId.systemDefault());

// LocalDateTime -> Instant
Instant convertedBack = localDateTime.atZone(ZoneId.systemDefault()).toInstant();

DateLocalDateTime

// Date -> LocalDateTime
Date date = new Date();
LocalDateTime ldt = date.toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime();

// LocalDateTime -> Date
Date convertedBack = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

四、Spring Boot 数据库映射建议

Java 类型推荐数据库字段类型是否含时区信息说明
InstantTIMESTAMP WITH TIME ZONE(PostgreSQL)
TIMESTAMP(MySQL)
✅ 是表示绝对时间点
LocalDateTimeDATETIMETIMESTAMP WITHOUT TIME ZONE❌ 否表示本地时间

实体类示例

@Entity
public class MyEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private Instant createdAt;          // 对应 TIMESTAMP WITH TIME ZONE
    private LocalDateTime updatedAt;    // 对应 DATETIME / TIMESTAMP WITHOUT TIME ZONE

    // getter/setter
}

MySQL 建表语句示例

CREATE TABLE my_entity (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT NULL
);

五、最佳实践总结

场景推荐类
日志记录、事件时间点Instant
用户界面展示时间LocalDateTime
数据库存储时间戳Instant(带时区)或 LocalDateTime(不带时区)
与前端交互(JSON)LocalDateTime(结合时区转换)
与遗留系统交互Date(但尽量封装转换逻辑)

六、Spring Boot 平滑过渡到 Instant / LocalDate / LocalDateTime

如果你希望返回的 LocalDateTime 显示为某种格式,比如 "2025-04-05 18:00:00",可以使用注解来接受返回指定格式的时间:

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime localDateTime;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Instant instant;

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate localDate;

或者自定义配置:

@Configuration
public class GlobalDateTimeConfig implements Jackson2ObjectMapperBuilderCustomizer {

    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
    private static final String DATE_DATE_PATTERN = "yyyy-MM-dd";
    private static final ZoneId ZONE_ID = ZoneId.of("Asia/Shanghai");
    @Override
    public void customize(Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder) {
        jackson2ObjectMapperBuilder.simpleDateFormat(DATE_TIME_PATTERN).timeZone(TimeZone.getTimeZone(ZONE_ID));
        // 自定义时间序列化模块
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(LocalDateTime.class, new JsonSerializer<>() {
            @Override
            public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeString(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN).format(value));
            }
        });

        simpleModule.addSerializer(LocalDate.class, new JsonSerializer<>() {
            @Override
            public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeString(DateTimeFormatter.ofPattern(DATE_DATE_PATTERN).format(value));
            }
        });
        simpleModule.addSerializer(Instant.class, new JsonSerializer<>() {
            @Override
            public void serialize(Instant value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeString(DateTimeFormatter.ofPattern(DATE_TIME_PATTERN).withZone(ZONE_ID).format(value));
            }
        });
        jackson2ObjectMapperBuilder.modules(simpleModule);
    }
}

而且 application.yml 中配置不会对 Instant / LocalDate / LocalDateTime 生效:

spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: Asia/Shanghai

上述配置 spring.jackson.date-format 是针对 Date 起作用,如果需要转换 Instant / LocalDate / LocalDateTime 。需要引入上述注解或者重写 JackJson 配置

七、结语

Java 的时间处理 API 在不断发展和完善,Date 已被时代淘汰,取而代之的是功能更强大、设计更合理的 java.time 包中的 InstantLocalDateTime。它们各自有明确的职责划分,在实际开发中应根据具体业务需求合理选用。

2

评论区