LocalDateTime 详解:告别 Date 的坑(AI 辅助学习 Java 8)
前五周学了 Lambda、Stream、Optional。今天进入第六周:Java 8 全新的时间 API。彻底告别 Date/Calendar 的各种坑。
一、Date/Calendar 为什么该被淘汰?
| 问题 | Date/Calendar | Java 8 新 API |
|---|---|---|
| 可变性 | Date 是可变的,线程不安全 | 不可变对象,线程安全 |
| 月份 | Calendar 月份从 0 开始 | 月份从 1 开始,符合直觉 |
| 线程安全 | SimpleDateFormat 不是线程安全的 | DateTimeFormatter 是线程安全的 |
| API 设计 | 职责混乱,Date 和 Calendar 混用 | 清晰分类:LocalDate/LocalTime/LocalDateTime |
| 时区 | 处理麻烦,需要 TimeZone 配合 | ZonedDateTime 原生支持 |
可变性陷阱:
Date date = new Date();
Date copy = date;
copy.setTime(0); // 修改了 copy,但 date 也被改了!
二、核心类
| 类 | 说明 | 示例 |
|---|---|---|
LocalDate | 只有日期(年月日) | 2026-06-13 |
LocalTime | 只有时间(时分秒) | 10:30:00 |
LocalDateTime | 日期 + 时间,无时区 | 2026-06-13T10:30:00 |
ZonedDateTime | 带时区的日期时间 | 2026-06-13T10:30:00+08:00[Asia/Shanghai] |
Instant | 时间戳(Unix 纪元秒) | 1718250600 |
Duration | 两个时间之间的间隔(时分秒) | PT8H30M |
Period | 两个日期之间的间隔(年月日) | P5M12D |
三、创建时间对象
// 当前时间
LocalDateTime now = LocalDateTime.now();
// 指定时间
LocalDateTime specific = LocalDateTime.of(2026, 6, 13, 10, 30, 0);
// 从字符串解析
LocalDateTime parsed = LocalDateTime.parse("2026-06-13T10:30:00");
// LocalDate + LocalTime 组合
LocalDate date = LocalDate.now();
LocalTime time = LocalTime.now();
LocalDateTime combined = LocalDateTime.of(date, time);
四、时间操作(不可变,返回新对象)
LocalDateTime now = LocalDateTime.of(2026, 6, 13, 10, 30, 0);
// 加减
now.plusDays(1); // +1 天
now.plusHours(2); // +2 小时
now.minusMinutes(30); // -30 分钟
now.plusWeeks(1); // +1 周
now.plusMonths(3); // +3 月
// 设置
now.withHour(0).withMinute(0).withSecond(0); // 设为当天 00:00:00
重要:所有操作都返回新对象,原对象不变。这和 String 的行为一样。
五、时间间隔计算
LocalDateTime start = LocalDateTime.of(2026, 6, 1, 10, 0);
LocalDateTime end = LocalDateTime.of(2026, 6, 13, 18, 30);
// 相差天数
long days = ChronoUnit.DAYS.between(start, end); // 12
// 相差小时数
long hours = ChronoUnit.HOURS.between(start, end); // 296
// Period:日期间隔(年月日)
Period period = Period.between(
LocalDate.of(2026, 1, 1),
LocalDate.of(2026, 6, 13)
); // 0年 5月 12天
// Duration:时间间隔(时分秒)
Duration duration = Duration.between(start, end); // 296小时 30分钟
六、格式化与解析
LocalDateTime now = LocalDateTime.of(2026, 6, 13, 10, 30, 0);
// 自定义格式(DateTimeFormatter 是线程安全的)
DateTimeFormatter custom = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String formatted = now.format(custom); // "2026年06月13日 10:30:00"
// 解析
LocalDateTime parsed = LocalDateTime.parse("2026年06月13日 10:30:00", custom);
对比 Java 8 之前的
SimpleDateFormat,它不是线程安全的,多线程环境下必须每次 new 或加锁。
七、TemporalAdjuster:时间调节器
LocalDate today = LocalDate.of(2026, 6, 13);
today.with(TemporalAdjusters.firstDayOfMonth()); // 本月第一天
today.with(TemporalAdjusters.lastDayOfMonth()); // 本月最后一天
today.with(TemporalAdjusters.firstDayOfYear()); // 今年第一天
today.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 下周一
today.with(TemporalAdjusters.previous(DayOfWeek.SUNDAY)); // 上个周日
today.with(TemporalAdjusters.firstInMonth(DayOfWeek.MONDAY)); // 当月第一个周一
一行代码搞定所有"某天"计算,不需要手动加减天数。
八、时区处理
// 不同时区的当前时间
ZonedDateTime beijing = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime newyork = ZonedDateTime.now(ZoneId.of("America/New_York"));
// LocalDateTime 转 ZonedDateTime
LocalDateTime local = LocalDateTime.of(2026, 6, 13, 10, 30, 0);
ZonedDateTime shanghaiTime = local.atZone(ZoneId.of("Asia/Shanghai"));
// 时区转换
ZonedDateTime nyTime = shanghaiTime.withZoneSameInstant(ZoneId.of("America/New_York"));
九、运行结果
=== 创建时间对象 ===
now: 2026-06-13T10:21:08
of: 2026-06-13T10:30
=== 时间操作 ===
基准时间: 2026-06-13T10:30
+1 天: 2026-06-14T10:30
-30 分钟: 2026-06-13T10:00
+3 月: 2026-09-13T10:30
=== 时间间隔计算 ===
相差天数: 12
相差小时: 296
间隔: 0年 5月 12天
=== TemporalAdjuster ===
本月第一天: 2026-06-01
下周一: 2026-06-15
当月第一个周一: 2026-06-01
=== 时区处理 ===
北京时间: 2026-06-13T10:22+08:00
东京时间: 2026-06-13T11:22+09:00
纽约时间: 2026-06-12T22:22-04:00
十、本节总结
| 场景 | 用哪个类/方法 |
|---|---|
| 只需要日期 | LocalDate |
| 只需要时间 | LocalTime |
| 日期+时间 | LocalDateTime |
| 带时区 | ZonedDateTime |
| 加减时间 | plusXxx / minusXxx |
| 设置时间 | withXxx / with(TemporalAdjusters) |
| 计算间隔 | Duration.between / Period.between / ChronoUnit.between |
| 格式化 | DateTimeFormatter(线程安全) |
十一、下篇预告
周四我们将深入学习 DateTimeFormatter 的自定义格式、时区处理、以及在博客项目中用新时间 API 重构现有的时间字段。
系列文章导航:
更多请关注我的微信公众号,感谢!