Java 时间类(中):JDK8 全新时间 API 详细教程

Java 的时间类(中):JDK 8 全新日期时间 API(java.time 包)详解

这是目前(2026年)Java 项目中最推荐、最主流的时间处理方式。
旧的 java.util.Date + Calendar + SimpleDateFormat 组合已被严重不推荐(线程不安全、可变、设计混乱、月份从0开始等坑)。

JDK 8 引入的 java.time 包(受 Joda-Time 启发,由 Stephen Colebourne 主导设计)彻底解决了这些问题。

核心设计理念(必须记住)

  • 不可变(Immutable) → 线程安全,天生可并发
  • 职责单一 → 日期、时间、带时区、时间戳、时间段各自独立类
  • 人类友好 → 月份从 1 开始,星期用枚举,周一是一周第一天(ISO标准)
  • 链式调用 → plusXxx / minusXxx / withXxx 方法返回新对象
  • 清晰命名 → now()、of()、parse()、format() 等方法语义明确

java.time 包主要类速览(分类记忆)

分类主要类是否带时区是否带时间是否带日期典型用途是否可做键(Map/Set)
本地日期LocalDate××生日、账单日、开业日期
本地时间LocalTime××上班打卡、下班时间、电影开始时间
本地日期时间LocalDateTime×订单创建时间、日志时间(最常用)
带时区日期时间ZonedDateTime / OffsetDateTime国际会议、跨时区航班、用户所在时区
瞬时(时间戳)InstantUTC机器时间戳、事件发生瞬间、缓存过期
时间段(精确)Duration两个时间点之间精确差(秒+纳秒)
日期段(人类)Period年/月/日差(生日还差几个月)
格式化DateTimeFormatter自定义格式化 & 解析

1. 获取当前时间(最常用几种写法)

LocalDate     today       = LocalDate.now();                // 2026-03-22
LocalTime     nowTime     = LocalTime.now();                // 02:10:45.123456789
LocalDateTime now         = LocalDateTime.now();            // 2026-03-22T02:10:45.123456789
ZonedDateTime nowInTokyo  = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
Instant       currentUtc  = Instant.now();                  // UTC 时间戳

2. 显式创建(of / parse)

// 推荐方式:of()
LocalDate     birth      = LocalDate.of(1995, 10, 8);       // 1995-10-08
LocalDateTime meet       = LocalDateTime.of(2026, 3, 22, 14, 30);
LocalDateTime meet2      = LocalDateTime.of(2026, Month.MARCH, 22, 14, 30);

// 从字符串解析(最安全方式)
LocalDate     d1         = LocalDate.parse("2026-03-22");
LocalDateTime dt         = LocalDateTime.parse("2026-03-22T14:30:00");

// 自定义格式解析
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
LocalDateTime custom = LocalDateTime.parse("2026年03月22日 14:30", fmt);

3. 常用运算(加减、调整)

所有运算都返回新对象(不可变)

LocalDateTime now = LocalDateTime.now();

// 加减
LocalDateTime tomorrow    = now.plusDays(1);
LocalDateTime nextWeek    = now.plusWeeks(1);
LocalDateTime nextMonth   = now.plusMonths(1);
LocalDateTime twoHoursAgo = now.minusHours(2);

// 更精确控制
LocalDateTime future = now.plus(3, ChronoUnit.MONTHS)
                          .plus(15, ChronoUnit.MINUTES);

// 调整到特定值(with 开头)
LocalDateTime startOfDay  = now.withHour(0).withMinute(0).withSecond(0).withNano(0);
LocalDate firstDayOfMonth = now.toLocalDate().with(TemporalAdjusters.firstDayOfMonth());
LocalDate lastDayOfMonth  = now.toLocalDate().with(TemporalAdjusters.lastDayOfMonth());
LocalDate nextMonday      = now.toLocalDate().with(TemporalAdjusters.next(DayOfWeek.MONDAY));

4. 时间间隔计算(Duration & Period)

// 精确到纳秒(机器友好)
LocalDateTime start = LocalDateTime.of(2026, 3, 22, 10, 0);
LocalDateTime end   = LocalDateTime.of(2026, 3, 22, 12, 45, 30);

Duration duration = Duration.between(start, end);
System.out.println(duration.toHours());        // 2
System.out.println(duration.toMinutes());      // 165
System.out.println(duration.getSeconds());     // 9930

// 年/月/日(人类友好)
LocalDate birth = LocalDate.of(1995, 10, 8);
Period age = Period.between(birth, LocalDate.now());
System.out.println("年龄:" + age.getYears() + "年 " + age.getMonths() + "月 " + age.getDays() + "天");

5. 时区处理(最重要场景之一)

// 当前时区
ZonedDateTime beijingNow = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

// 转换时区
ZonedDateTime tokyo = beijingNow.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));

// UTC 时间戳 ↔ 本地时间
Instant utc = Instant.now();
ZonedDateTime local = utc.atZone(ZoneId.systemDefault());

// 常见时区ID(不要硬编码字符串,推荐用 ZoneId.systemDefault() 或配置文件)
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
ZoneId utcZone  = ZoneId.of("UTC");

6. 格式化与解析(DateTimeFormatter)

DateTimeFormatter isoDate     = DateTimeFormatter.ISO_DATE;          // 2026-03-22
DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE_TIME;    // 2026-03-22T02:10:45.123456789

// 自定义(线程安全!可以做 static final 常量)
DateTimeFormatter custom = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String str = now.format(custom);                // "2026-03-22 02:10:45"

LocalDateTime parsed = LocalDateTime.parse("2026-03-22 02:10:45", custom);

7. 常见坑 & 最佳实践(2026 年视角)

场景错误写法正确写法 / 推荐做法
格式化器复用new SimpleDateFormat() 多线程用DateTimeFormatter 做 static final 常量
生日/有效期判断用 Calendar 或 Date 比较用 LocalDate.isBefore() / isAfter() / isEqual()
跨时区订单时间存储存 LocalDateTime存 Instant 或 ZonedDateTime(推荐后者带时区信息)
计算两个时间相差多少工作日自己写循环用 ChronoUnit.DAYS.between() + TemporalAdjusters
JSON 序列化默认用 toString()用 Jackson 的 JavaTimeModule
数据库存储存 Timestamp / Date推荐存 Instant 或 OffsetDateTime

8. 快速对照表(面试/日常速查)

需求 → 推荐类
纯日期(生日、假期) → LocalDate
纯时间(每天固定时间) → LocalTime
日期+时间(日志、订单) → LocalDateTime(无时区需求时最常用)
跨系统/跨时区事件 → Instant 或 ZonedDateTime
两个时间点精确间隔 → Duration
两个日期之间年月日差 → Period
需要调整到月初/月末/周一 → TemporalAdjusters

你现在是想重点练习哪一块?

  • 常见面试题(生日判断、跨年计算、时区转换等)
  • 实际业务代码示例(订单超时判断、排班系统、国际会议时间)
  • 与旧 Date/Calendar 的转换写法
  • Spring Boot + Jackson 如何优雅序列化

告诉我,我可以继续给出针对性更强的代码和说明。

文章已创建 5225

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部