Period用于日历语义(年月日),如生日、工龄;Duration用于精确时间量(秒/毫秒),如接口耗时、定时器。二者不可混用,否则抛DateTimeException或结果错误。

Period 和 Duration 看似都算“时间差”,但它们根本不是同一类东西——一个管“日历上的日期怎么变”,一个管“钟表上的秒针走了多久”。用错类型,轻则结果错得离谱(比如把 1 月 31 日加 Period.ofMonths(1) 得到 2 月 31 日再自动折成 3 月 3 日),重则直接抛 DateTimeException。
什么时候必须用 Period?——只跟年/月/日打交道时
当你在处理生日、工龄、合同起止日、法定节假日间隔等「人类日历语义」问题时,Period 是唯一正解。
-
Period.between(LocalDate.of(2020, 1, 31), LocalDate.of(2020, 3, 31))返回P2M(2个月),它知道 2 月只有 29 天,不硬算天数 -
LocalDate.now().plus(Period.ofYears(1))会智能跳到明年同月同日(哪怕那天不存在,也会按日历规则调整) - 不能传
LocalDateTime或Instant给Period.between(),否则编译不报错但运行时抛DateTimeException -
Period.parse("P1Y2M3D")支持 ISO-8601 字符串解析,但注意:它不接受带时间部分的字符串(如"P1Y2MT3H"是非法的)
LocalDate start = LocalDate.of(2025, 1, 31); LocalDate end = start.plus(Period.ofMonths(1)); // → 2025-02-28(不是 2025-02-31) System.out.println(end);
什么时候必须用 Duration?——只跟秒/毫秒/纳秒精度有关时
当你在测接口耗时、计算任务执行时长、做定时器倒计时、或和数据库 INTERVAL 类型交互时,Duration 才是真·时间尺子。
-
Duration.between(LocalTime.NOON, LocalTime.of(14, 30))返回PT2H30M,精确到纳秒,不关心哪年哪月 -
Duration.ofDays(1)永远是 86400 秒,不管这天是不是闰秒日;而Period.ofDays(1)只是“日历上过一天”,不承诺多少秒 - 传
LocalDate给Duration.between()会直接炸:抛DateTimeException: Unsupported unit: Seconds -
Duration的toString()输出如PT8H6M12.345S,开头PT表示“Period Time”,别被名字骗了——它和Period没半毛钱关系
LocalDateTime start = LocalDateTime.of(2026, 1, 19, 10, 0); LocalDateTime end = LocalDateTime.of(2026, 1, 19, 12, 30, 45, 123_000_000); Duration d = Duration.between(start, end); System.out.println(d.toMinutes()); // → 150 System.out.println(d.toNanos()); // → 9045123000000
最容易踩的坑:混用类型 + 误读 between() 参数顺序
两个类的 between() 都是「第二个参数减第一个参数」,但方向感一错,结果就全反了——而且 isNegative() 不是万能补丁。
立即学习“Java免费学习笔记(深入)”;
-
Period.between(d1, d2):如果d1在d2后面,返回负值(getDays()为负),但plus()仍按正逻辑加,容易绕晕 -
Duration.between(t1, t2)同理:若t1晚于t2,得到负Duration,toSeconds()返回负数,但没人想在日志里写“耗时 -120 秒” - 别试图用
Duration算年龄:它不会处理“2026 年 1 月 19 日减 1990 年 5 月 12 日等于多少年”,因为年不是固定秒数 - Android 开发者注意:
Duration需 API 26+,低版本要用ThreeTenABP兼容库,而Period同样受限
真正要记住的只有一条:日期变化看日历(用 Period),时间流逝看秒表(用 Duration)。
其余所有异常、错位、负值,几乎都源于没守住这条线。










