LocalDateTime 详解:告别 Date 的坑(AI 辅助学习 Java 8)

33 阅读4分钟

LocalDateTime 详解:告别 Date 的坑(AI 辅助学习 Java 8)

前五周学了 Lambda、Stream、Optional。今天进入第六周:Java 8 全新的时间 API。彻底告别 Date/Calendar 的各种坑。


一、Date/Calendar 为什么该被淘汰?

问题Date/CalendarJava 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 重构现有的时间字段。


系列文章导航

更多请关注我的微信公众号,感谢!

微信图片_20260427132414_18_737.jpg