--- title: "04-Java时间新API" created: 2026-09-03 tags: - Java --- # Java时间新API(java.time) > [!note] 本篇定位 > JDK 8 引入的 `java.time` 彻底替代了 `Date`/`Calendar`/`SimpleDateFormat`。老 API 三大罪:月份从 0 开始、线程不安全、格式化有坑。新 API 一律不可变+线程安全+清晰命名。本篇不重述老 API 用法(鱼皮 [[18-Java 日期时间|18-Java 日期时间]] 已覆盖入门),只讲新 API 的"为什么好"和"怎么用"。 ## 一、问题提出:老 API 的三个经典坑 ```java // 坑1:月份从 0 开始——12 月 new Date(2024, 11, 31) 写成 12 就炸 Date d = new Date(2024, 11, 31); // 实际是 2025 年 12 月 31 日,因为 year 是 1900+124 // 坑2:SimpleDateFormat 线程不安全,多线程下 format 会乱序甚至抛异常 static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // 多线程调 sdf.format(...) 崩 // 坑3:Calendar 的 set 之后不立刻生效,要 get 一下才重算(内部字段惰性) Calendar c = Calendar.getInstance(); c.set(Calendar.MONTH, 13); // 不报错,自动进位到明年 2 月——不报错反而不安全 ``` ## 二、新 API 核心类:三句话记住所有类 | 类 | 表示什么 | 关键方法 | | --- | --- | --- | | `LocalDate` | 日期(年月日) | `now()`, `of(2024,12,31)`, `plusDays(7)`, `isBefore/After` | | `LocalTime` | 时间(时分秒纳秒) | `now()`, `of(10,30,0)`, `plusHours(2)`, `isBefore/After` | | `LocalDateTime` | 日期+时间(无时区) | `now()`, `of(date,time)`, `plusDays(1)`, `toLocalDate()` | | `ZonedDateTime` | 日期+时间+时区 | `now(ZoneId.of("Asia/Shanghai"))`, `withZoneSameInstant()` | | `Instant` | 时间戳(UTC 毫秒/纳秒) | `now()`, `toEpochMilli()`, `plusSeconds(3600)` | | `Duration` | 时间差(时分秒) | `between(t1,t2)`, `toHours()` | | `Period` | 日期差(年月日) | `between(d1,d2)`, `getYears()`, `getMonths()` | **两条铁律**:① 所有新 API 类都是 **不可变**的——`plusDays` 返回新对象,原对象不动(没有副作用,天然线程安全);② 数据库存时间戳用 `Instant`,业务展示用 `LocalDateTime`+时区,跨时区转换走 `ZonedDateTime`。 ```java LocalDate today = LocalDate.now(); LocalDate nextWeek = today.plusDays(7); // 不可变,返回新对象 LocalDate birthday = LocalDate.of(2000, 1, 15); // 1 月就是 1,不是 0 Period age = Period.between(birthday, today); System.out.println(age.getYears()); // 直接拿年数,不用手算 // 跨时区:北京时间 → 纽约时间 ZonedDateTime bj = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); ZonedDateTime ny = bj.withZoneSameInstant(ZoneId.of("America/New_York")); ``` ## 三、格式化:DateTimeFormatter 替代 SimpleDateFormat ```java DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime now = LocalDateTime.now(); String s = now.format(fmt); // 格式化 LocalDateTime parsed = LocalDateTime.parse(s, fmt); // 解析 ``` **线程安全**:`DateTimeFormatter` 不可变,可以放心存成 `static final` 多线程共用——这是和 `SimpleDateFormat` 的最大区别。 预定义格式: ```java DateTimeFormatter.ISO_LOCAL_DATE; // 2024-12-31 DateTimeFormatter.ISO_LOCAL_DATE_TIME; // 2024-12-31T10:30:00 DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); // 根据 Locale 本地化 ``` ## 四、新旧 API 互转 ```java // Date → Instant → LocalDateTime Date old = new Date(); LocalDateTime ldt = old.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); // LocalDateTime → Instant → Date Date back = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); ``` 转换的核心是 `Instant`——它是 UTC 时间戳的通用表示,两边都认。 ## 五、常用操作速查 ```java // 比较 localDate.isBefore(other); localDate.isAfter(other); localDate.isEqual(other); // 调整 localDate.withDayOfMonth(1); // 当月第一天 localDate.with(TemporalAdjusters.lastDayOfMonth()); // 当月最后一天 localDate.with(TemporalAdjusters.next(DayOfWeek.MONDAY)); // 下个周一 // 计算 Duration.between(time1, time2).toMinutes(); // 分钟差 Period.between(date1, date2).getDays(); // 日期差(注意跨月不算自然天) ChronoUnit.DAYS.between(date1, date2); // 自然天数差(推荐) // 时区 ZoneId.of("Asia/Shanghai"); // 永远用"地区/城市"格式,不用 UTC+8 偏移 ZoneId.systemDefault(); // 服务器默认时区 ``` ## 六、高频面试题速答 1. **Date 和 LocalDate 选哪个?** 新项目一律用 java.time;Date 只用于老代码互转、数据库驱动兼容层。 2. **SimpleDateFormat 为什么线程不安全?** 内部 Calendar 字段是共享可变状态,多线程 format 会互相覆盖——用 `DateTimeFormatter` 或每次 new 一个。 3. **LocalDateTime 和 ZonedDateTime 区别?** 前者是"墙上时钟"(不携带时区信息),后者带了时区。存数据库优先用 `Instant`(时间戳),展示用 `LocalDateTime`+前端时区。 4. **Period 和 Duration 区别?** Period 是日期差(年月日),Duration 是时间差(时分秒纳秒)。`Period.between(2024-01-01, 2024-12-31)` 返回 11 个月 30 天,不是 365 天——算自然天数用 `ChronoUnit.DAYS.between`。 ## 勾连 - 不可变对象的线程安全模式:[[01-String详解|01-String详解]](String 不可变同理) - 格式化与国际化:[[08-Stream与Optional|08-Stream与Optional]](Optional 的 orElseGet 惰性求值思想) - 老 API 入门:[[18-Java 日期时间|18-Java 日期时间]] --- ⬅️ [[03-BigDecimal|03-BigDecimal]] 🏠 [[00-Java|00-Java]] ➡️ [[05-Comparable与Comparator|05-Comparable与Comparator]]