Date 类在现代 Java 项目中基本禁用,因其违背不可变性、语义清晰和线程安全原则;设计缺陷包括:方法命名误导(getYear() 返回126)、月份从0开始、setter可变、SimpleDateFormat非线程安全,且 toString() 依赖系统时区。

为什么 Date 类在现代 Java 项目里基本等于“禁用”
因为 Date 不是“不好用”,而是“根本不能放心用”——它从设计上就违背了现代编程的基本原则:不可变性、语义清晰、线程安全。2026 年还在新代码里写 new Date(),相当于在 Spring Boot 3 项目里硬塞 Servlet 2.5 的 XML 配置。
- 它名字叫
Date,实际存的是毫秒时间戳(Instant的语义),但既不带时区,又不说明精度,连 toString() 都偷偷依赖系统默认时区 -
getYear()返回126(对应 2026 年),getMonth()返回0表示一月——这不是 API,是谜题 - 所有 setter(如
setTime()、setYear())都直接改内部状态,多线程下共享一个Date实例?等着数据错乱吧 - 和它绑定的
SimpleDateFormat更是“线程安全粉碎机”,哪怕只在一个工具类里 static 声明一个,高并发下格式化结果就可能变成"2026-13-99 88:77:66"
LocalDateTime 是什么,什么时候该用它
LocalDateTime 不是 Date 的“升级版”,而是“正解”:它明确表示“本地日期+时间”,不含时区、不隐式转换、不依赖系统设置。适合绝大多数业务场景——比如订单创建时间、日志打点、数据库 DATETIME 字段映射。
- ✅ 替换
new Date()→LocalDateTime.now() - ✅ 替换
date.getTime()(仅需毫秒)→Instant.now().toEpochMilli() - ✅ 替换
new Date(1736712000000L)→LocalDateTime.ofInstant(Instant.ofEpochMilli(1736712000000L), ZoneId.systemDefault()) - ❌ 别用它处理跨时区逻辑(如用户在美国下单、服务器在新加坡)——这时该用
ZonedDateTime或Instant
怎么安全地把旧 Date 字段迁移到 LocalDateTime
迁移不是字符串替换,关键在语义对齐。很多老代码把 Date 当作“无时区时间”用,其实它底层是 UTC 时间戳,只是 toString() 给你“伪装”成本地时间。
- 如果数据库字段是
DATETIME(无时区),JPA 实体中把private Date createTime;改为private LocalDateTime createTime;即可,Hibernate 5.2+ 原生支持 - 如果字段是
TIMESTAMP WITH TIME ZONE,别硬套LocalDateTime,该用OffsetDateTime或ZonedDateTime - 和外部系统交互(如 HTTP JSON)时,
Date默认序列化成毫秒数,而LocalDateTime默认是字符串(如"2026-01-12T22:52:00"),需统一配置 Jackson:ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule());
- 千万别写
localDateTime.atZone(ZoneId.systemDefault()).toInstant()再转回Date——这是兜圈子,还可能因夏令时出错
最容易被忽略的坑:时区转换和数据库精度
很多人以为“我只用 LocalDateTime 就不用管时区了”,结果上线后发现凌晨 2 点的定时任务漏跑——因为 LocalDateTime.now() 拿的是 JVM 所在机器的本地时间,而服务器时区可能是 UTC,测试机却是 CST。
立即学习“Java免费学习笔记(深入)”;
- 数据库字段类型必须匹配:MySQL 的
DATETIME对应LocalDateTime,TIMESTAMP对应Instant;PostgreSQL 的timestamp without time zone同理 -
LocalDateTime不包含毫秒以下精度,但 MySQL 5.6+ 支持DATETIME(3),Java 8 的LocalDateTime只能到毫秒,纳秒级需用Instant+ 数据库TIMESTAMP(9) - Spring Boot 3 默认关闭
spring.jackson.deserialization.adjust-dates-to-context-time-zone=false,这意味着传入"2026-01-12T10:00:00"会原样解析为本地时间,不会自动转成服务器时区——这点和旧版行为不同,必须确认










