⭐ 推荐:社区规则条款 V1.0

Java 的日期时间 API?Java 8 Date/Time API?

GJ论坛站长
GJ论坛站长 管理员资深会员 👑黑卡会员
发布于 2026-10-21 20:22 ·10 浏览 ·0 回复
内容摘要

介绍 Java 8 java.time 的核心用法:按需选择 LocalDate、LocalTime、LocalDateTime、Instant、ZonedDateTime,掌握不可变对象的加减、DateTimeFormatter 格式化解

学完这篇你能把 Java 8 的 java.time 用起来:创建日期、加减时间、格式化解析、处理时区、计算间隔,以及和老的 Date/Calendar 互相转换,全程不踩常见坑。

Java 8 引入了 java.time 包(JSR-310),从 Java 8 起新建项目就不该再用 java.util.Date 和 SimpleDateFormat 做业务日期计算了。下面按步骤走一遍,代码可直接复制进 main 方法跑。

第一步:认识三个核心类,按需求选类型

这一步要搞清楚「日期」「时间」「日期+时间」分别用哪个类,选错类型后面全是麻烦。规则很简单:只要年月日用 LocalDate,只要时分秒用 LocalTime,两者都要用 LocalDateTime;需要绝对时间点(时间戳)用 Instant;需要带时区用 ZonedDateTime。

LocalDate date = LocalDate.of(2024, 3, 15);        // 2024-03-15
LocalTime time = LocalTime.of(14, 30, 0);          // 14:30
LocalDateTime dt = LocalDateTime.of(date, time);   // 2024-03-15T14:30
LocalDate today = LocalDate.now();                 // 系统默认时区的今天

注意:LocalDate.of(2024, 3, 15) 的月份是从 1 开始(3 就是 3 月),这一点和老的 Calendar.MARCH(值为 2)正好相反,从旧代码迁过来最容易在这翻车。月份写错会直接抛 DateTimeException(比如写 13),不会静默出错。

第二步:加减和修改,记住对象不可变

这一步要掌握日期计算,做完你就能替代掉以前一堆 Calendar.add() 的代码。所有 plusXxx / minusXxx / withXxx 方法都返回新对象,原对象不变,所以必须接收返回值。

LocalDate d = LocalDate.of(2024, 1, 31);
LocalDate next = d.plusMonths(1);       // 2024-02-29,自动处理月末和闰年
LocalDate firstDay = d.withDayOfMonth(1); // 2024-01-01
LocalDateTime later = LocalDateTime.now().plusHours(2).minusMinutes(30);

需要「本月最后一天」用 d.with(TemporalAdjusters.lastDayOfMonth()),需要「下一个周一」用 TemporalAdjusters.next(DayOfWeek.MONDAY),都在 java.time.temporal 包里。

注意:d.plusDays(1); 单独写一行是无效操作,编译不报错但结果丢了,这是新手最常见的低级 bug。

第三步:格式化和解析,用 DateTimeFormatter

这一步解决「日期和字符串互转」。用 DateTimeFormatter 替换 SimpleDateFormat,它是线程安全的,可以定义成 static final 常量复用。

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s = LocalDateTime.now().format(fmt);          // 2024-03-15 14:30:00
LocalDateTime parsed = LocalDateTime.parse("2024-03-15 14:30:00", fmt);
LocalDate iso = LocalDate.parse("2024-03-15");        // 不传 formatter,默认 ISO_LOCAL_DATE

模式字母别写错:yyyy 年、MM 月、dd 日、HH 24 小时制、hh 12 小时制、mm 分、ss 秒。

注意:YYYY(大写)是「按周算的年份」,跨年那几天会得到错误年份,一定要用小写 yyyy;同理 DD 是一年中的第几天,别和 dd 混。

第四步:处理时区和绝对时间点

这一步解决「服务器在 UTC、用户在 +08:00」这类问题,做完你能正确存库和展示。跨时区存储统一用 Instant(UTC 时间戳),展示时再转成本地时区。

Instant now = Instant.now();
ZonedDateTime bj = now.atZone(ZoneId.of("Asia/Shanghai"));
LocalDateTime local = bj.toLocalDateTime();
ZonedDateTime utc = now.atZone(ZoneId.of("UTC"));

注意:ZonedDateTime.now() 依赖 JVM 默认时区,服务器和本地不一致就会差 8 小时,务必显式写 ZoneId.of("Asia/Shanghai"),别依赖默认值。

第五步:算间隔,Duration 和 Period 别用混

这一步要算「两个时间差多久」。Duration 算的是时分秒级别的量(基于 Instant),Period 算的是年月日级别的量(基于 LocalDate)。

Duration dur = Duration.between(
    LocalDateTime.of(2024,3,15,9,0),
    LocalDateTime.of(2024,3,15,17,30));
long minutes = dur.toMinutes();            // 510

Period p = Period.between(LocalDate.of(2024,1,1), LocalDate.of(2024,3,15));
// P2M14D,用 p.getMonths()、p.getDays() 取各段

long days = ChronoUnit.DAYS.between(LocalDate.of(2024,1,1), LocalDate.of(2024,3,15));

注意:算「总天数」用 ChronoUnit.DAYS.between(),不要用 Period.getDays(),后者只返回「天数那一段」,上面例子是 14 而不是 74。

第六步:和旧 API 互转

这一步处理老代码兼容。核心桥梁是 Instant:

// Date -> LocalDateTime
LocalDateTime ldt = LocalDateTime.ofInstant(new Date().toInstant(), ZoneId.systemDefault());
// LocalDateTime -> Date
Date legacy = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
// 数据库字段:java.sql.Timestamp
Timestamp ts = Timestamp.valueOf(ldt);

LocalDate 与 java.sql.Date 之间用 java.sql.Date.valueOf(localDate) 和 sqlDate.toLocalDate()。

注意:java.sql.Date.toInstant() 会抛 UnsupportedOperationException,必须走 toLocalDate() 中转,别照抄 java.util.Date 的写法。

碰到 UnsupportedTemporalTypeException,多半是给 LocalDate 加了小时(LocalDate 没有时分秒),换成 LocalDateTime 即可。实际项目里遇到类似报错,可以在 GJ站长论坛的「后端开发 → Java」子版块搜一下报错关键字,通常有人贴过完整堆栈。

小结

  • Java 8 起用 java.time,日期 LocalDate、时间 LocalTime、日期时间 LocalDateTime、时间戳 Instant、带时区 ZonedDateTime。
  • 月份从 1 开始,与 Calendar 相反;格式化年份用小写 yyyy,别用 YYYY。
  • 所有 plus/minus/with 都返回新对象,必须接收返回值。
  • DateTimeFormatter 线程安全,可直接做 static final 常量;SimpleDateFormat 不是。
  • 算总天数用 ChronoUnit.DAYS.between(),Duration 管时分秒、Period 管年月日。
  • 跨时区存储统一用 Instant(UTC),展示时再转本地时区,不依赖 JVM 默认时区。
  • 新老 API 互转以 Instant 为桥梁,java.sql.Date 走 toLocalDate()。
本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2362.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 0

还没有回复,来抢沙发~