日期时间 API(java.time):告别 Date/Calendar
更新时间:2026-09-20。Java 8 的
java.time(JSR-310)彻底替代了老旧且坑多的Date/Calendar/SimpleDateFormat。核心设计:不可变、分清「本地时间」与「带时区的瞬间」、格式化器线程安全。
一、核心类一张表
| 类 | 表示 | 含时区? | 典型用途 |
|---|---|---|---|
LocalDate | 只有日期 2026-09-20 | 否 | 生日、账单日 |
LocalTime | 只有时分秒纳秒 | 否 | 营业时间、时刻 |
LocalDateTime | 日期 + 时间 | 否 | 人读的本地时间,不能直接对应唯一瞬间 |
Instant | UTC 时间戳(纪元秒+纳秒) | UTC | 机器存储、跨系统传输 |
ZonedDateTime | 日期时间 + 时区规则 | 是 | 需要处理时区/夏令时的真实时刻 |
Duration / Period | 时间跨度 | — | 时分秒间隔 / 年月日间隔 |
java
LocalDate d = LocalDate.now(); // 2026-09-20
LocalTime t = LocalTime.of(9, 30);
LocalDateTime dt = LocalDateTime.of(d, t);
LocalDateTime next = dt.plusDays(1).withHour(10); // 不可变:返回新对象
Instant now = Instant.now(); // 2026-09-20T01:30:00Z(UTC)
ZonedDateTime z = dt.atZone(ZoneId.of("Asia/Shanghai"));
Duration.between(t1, t2).toMillis(); // 两个时刻差多少毫秒
Period p = Period.between(LocalDate.of(2000,1,1), d); // 几年几月几天二、本地时间 vs 瞬间:最关键的区别
LocalDateTime 不带时区,同一个「2026-01-01 00:00」在上海和纽约是两个不同的物理瞬间。因此:
- 存储/传输/比较绝对时间:用
Instant(或 UTC 的OffsetDateTime)。 - 展示给特定地区用户:用
ZonedDateTime配合ZoneId。 - 二者转换:
instant.atZone(ZoneId.of("Asia/Shanghai"))、zdt.toInstant()。

三、格式化与解析
java
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s = dt.format(f); // 格式化
LocalDateTime parsed = LocalDateTime.parse("2026-09-20 09:30:00", f);
DateTimeFormatter iso = DateTimeFormatter.ISO_OFFSET_DATE_TIME; // 内置标准格式DateTimeFormatter 不可变且线程安全,可作为 static final 常量共享——这是相对 SimpleDateFormat 的最大改进。
四、与旧 API 互转
java
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant(); // Date -> Instant
Date back = Date.from(instant);
LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());坑
- 用 LocalDateTime 存跨时区时间戳:丢了时区就无法定位唯一瞬间,分布式系统务必存
Instant/UTC。 - SimpleDateFormat 当静态共享变量:它非线程安全,多线程并发 parse 会出错乱数字甚至异常;要么换 DateTimeFormatter,要么用 ThreadLocal。
- 直接用整数手算加减/跨月跨年:夏令时、大小月、闰年都要自己处理,应用
plusMonths/plusDays与ZonedDateTime。 - Date 的月份从 0 起、年份从 1900 起:旧 Calendar API 反直觉,新代码一律 java.time。
- 解析模式严格:自定义 pattern 与字符串必须严格匹配(
MM不能喂9),否则DateTimeParseException。
总结
java.time 用不可变对象替代 Date/Calendar:LocalDate/Time/DateTime 表达与时区无关的朴素日期时间、Instant 表达 UTC 绝对瞬间、ZonedDateTime 承载时区与夏令时规则,绝对时间存 Instant、给人展示再转 ZonedDateTime,区间用 Duration/Period,格式化统一用线程安全的 DateTimeFormatter;旧代码通过 toInstant/Date.from 互转,切勿再共享 SimpleDateFormat 或用 LocalDateTime 表示跨时区时间戳。