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

Java 时间类(中):JDK 8 全新时间 API 详细教程
(2025–2026 生产视角 · 重点实用写法 + 常见误区 + 面试高频点)

自 JDK 8 引入 java.time 包(JSR-310)之后,基本上所有新项目都不应该再使用 java.util.DateCalendarSimpleDateFormat 这三个“古老且危险”的类。

本篇把 java.time 包中最核心、最常用的类和用法全部梳理一遍,附上最常见的生产场景写法。

一、JDK 8 时间 API 核心类一览表(背下来)

类别核心类是否可变时区相关主要用途面试常考
日期(不含时间)LocalDate不可变生日、订单日期、账单日★★★★★
时间(不含日期)LocalTime不可变上班打卡、下班时间、会议开始时间★★★★☆
日期+时间LocalDateTime不可变大多数业务场景(日志、创建时间、更新时间)★★★★★
带时区日期时间ZonedDateTime / OffsetDateTime不可变跨境业务、全球用户、定时任务跨夏令时★★★★☆
时刻(时间戳)Instant不可变UTC数据库存储时间戳、分布式系统序时★★★★☆
时间段Duration / Period不可变计算两个时间点之间的差值★★★★☆
时间间隔(Cron)TemporalAmount 接口实现类加减年/月/日/时/分/秒★★★☆☆
格式化DateTimeFormatter不可变可带时区所有格式化与解析★★★★★

一句话记忆口诀
Local 系列不带时区最常用,Zoned 带时区跨境用,Instant 是 UTC 绝对时刻,Duration 算时分秒,Period 算年月日。”

二、最常用 20 种写法(生产直接抄)

1. 获取当前日期/时间(最常见)

LocalDate today = LocalDate.now();                // 2025-02-25
LocalTime nowTime = LocalTime.now();              // 14:35:27.123456789
LocalDateTime now = LocalDateTime.now();          // 2025-02-25T14:35:27.123456789
ZonedDateTime londonNow = ZonedDateTime.now(ZoneId.of("Europe/London"));
Instant instantNow = Instant.now();               // UTC 时间戳

2. 指定某个日期/时间

LocalDate birth = LocalDate.of(1990, 5, 20);      // 1990-05-20
LocalDateTime meeting = LocalDateTime.of(2025, 3, 1, 14, 30); 
LocalDateTime parseFromStr = LocalDateTime.parse("2025-02-25 14:30:00", 
    DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

3. 日期计算(加减天/月/年)

LocalDate tomorrow = today.plusDays(1);
LocalDate nextMonth = today.plusMonths(1);
LocalDate lastYear = today.minusYears(1);

// 更精确的加减(支持 Duration / Period)
LocalDateTime after2Hours = now.plus(Duration.ofHours(2));
LocalDate after3Months = today.plus(Period.ofMonths(3));

4. 计算两个日期之间的差值

// 两个 LocalDate 之间相差多少天
long daysBetween = ChronoUnit.DAYS.between(startDate, endDate);

// Duration(精确到纳秒)
Duration duration = Duration.between(startDateTime, endDateTime);
long hours = duration.toHours();
long minutes = duration.toMinutesPart();

// Period(年月日)
Period period = Period.between(birthDate, today);
int yearsOld = period.getYears();

5. 格式化与解析(最常用模板)

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
DateTimeFormatter dtfChina = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分", Locale.CHINA);

// 格式化
String str = now.format(dtf);                    // "2025-02-25 14:35:27"

// 解析
LocalDateTime parsed = LocalDateTime.parse("2025-02-25 14:35:27", dtf);

常用格式化字母表(背熟最常考):

  • y → 年
  • M → 月
  • d → 日
  • H → 24小时制
  • h → 12小时制
  • m → 分钟
  • s → 秒
  • S → 毫秒
  • E → 星期几(英文)
  • a → 上午/下午

6. 时区相关操作(跨境业务必考)

// 当前伦敦时间
ZonedDateTime londonTime = ZonedDateTime.now(ZoneId.of("Europe/London"));

// 北京时间
ZonedDateTime beijingTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));

// 转换为另一个时区
ZonedDateTime tokyoTime = londonTime.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));

// 转成 UTC Instant
Instant utcInstant = londonTime.toInstant();

7. 与旧 API 互转(遗留系统对接)

// Date → LocalDateTime
Date oldDate = new Date();
LocalDateTime ldt = oldDate.toInstant()
                          .atZone(ZoneId.systemDefault())
                          .toLocalDateTime();

// LocalDateTime → Date
Date date = Date.from(now.atZone(ZoneId.systemDefault()).toInstant());

// Calendar → ZonedDateTime
Calendar cal = Calendar.getInstance();
ZonedDateTime zdt = cal.toInstant().atZone(ZoneId.systemDefault());

三、2025–2026 年生产中最常犯的 10 个坑(避坑指南)

  1. 直接用 new Date() 或 System.currentTimeMillis() 存创建时间
    → 应统一用 Instant.now()LocalDateTime.now(),数据库存 TIMESTAMP WITH TIME ZONE
  2. SimpleDateFormat 多线程使用
    → 完全禁止!改用 DateTimeFormatter(线程安全)
  3. 时区写死 “GMT+8”
    → 用 ZoneId.of("Asia/Shanghai"),支持夏令时和政治变化
  4. 误用 LocalDateTime 做全球时间比较
    → 应该转成 Instant 或 ZonedDateTime 再比较
  5. 生日用 LocalDateTime 存
    → 只用 LocalDate 就够了(不需要时间部分)
  6. Duration.between 用错了顺序
    → between(start, end) → end 在前会返回负值
  7. 格式化带毫秒时漏写 SSS
    → “yyyy-MM-dd HH:mm:ss” 不会带毫秒,要写 “yyyy-MM-dd HH:mm:ss.SSS”
  8. 跨年月计算时用 plusDays
    → 应该用 Period.ofMonths() 或 ChronoUnit.MONTHS.between()
  9. 数据库取出的 Timestamp 直接 toLocalDateTime()
    → 必须指定时区:timestamp.toLocalDateTime().atZone(ZoneId.systemDefault())
  10. 以为 LocalDateTime 包含时区
    → 它不包含!要带时区用 ZonedDateTime 或 OffsetDateTime

四、总结:2026 年最推荐的使用原则

  1. 业务日期(生日、订单日) → LocalDate
  2. 业务时间点(创建时间、更新时间、日志) → LocalDateTime(数据库存无时区)
  3. 全球一致时间戳 → Instant(数据库存 UTC)
  4. 跨时区显示/计算 → ZonedDateTime
  5. 所有格式化/解析 → DateTimeFormatter(永远不要再碰 SimpleDateFormat)
  6. 时间计算 → 优先用 plus/minus + Duration / Period / ChronoUnit

需要继续深入哪一块?

  • 与数据库(MySQL/PostgreSQL)交互的最佳实践
  • ZonedDateTime 在夏令时切换时的行为
  • Java 21+ 虚拟线程对时间 API 的影响(几乎无)
  • 常见时间计算面试题 20 道(带代码)
  • 三方库(Joda-Time → java.time 迁移指南)

告诉我你的需求,我继续展开~

文章已创建 4758

发表回复

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

相关文章

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

返回顶部