duration表示秒级精度的绝对时间长度,period表示年月日级的日历相对差;前者用于耗时/定时等纯时间场景,后者用于年龄/合同等日历计算。

Duration 是秒级精度的时长,Period 是年月日级的日期差
Java 里 Duration 和 Period 看似都表示“间隔”,但底层语义完全不同:Duration 基于纳秒计数,只认时间轴上的绝对长度(比如 3600 秒、2.5 小时);Period 是基于日历系统的相对变化(比如“加 1 个月”或“减 2 天”),不固定对应多少毫秒。
常见错误是拿 Duration.between(LocalDateTime.now(), LocalDateTime.of(2025, 1, 1, 0, 0)) 算跨月天数——结果会因时区、夏令时、闰秒等出错,而且根本得不到“几个月几天”这种可读结果。
- 用
Duration处理耗时、超时、定时器、线程 sleep 等纯时间长度场景 - 用
Period处理年龄计算、合同起止月数、生日倒计时(按日历算)、数据库中INTERVAL YEAR TO MONTH映射 -
Duration支持ofSeconds()、ofMillis()、parse("PT2H30M");Period支持ofMonths(3)、ofYears(1)、parse("P1Y2M3D")
LocalDateTime 不能直接传给 Duration.between()?其实是能的,但要小心隐式转换
Duration.between() 接收两个 Temporal,LocalDateTime 符合要求,所以语法上完全合法。问题在于:它把两个 LocalDateTime 当作同一时区下的瞬时点来算差,忽略时区信息——如果这两个时间本就来自不同地区(比如用户 A 提交时间和服务器日志时间),结果就失真了。
更隐蔽的坑是:你用 LocalDateTime.now().plusDays(1) 和 LocalDateTime.now() 算 Duration,看似没问题,但一旦涉及夏令时切换日(比如 3 月10 日凌晨 2 点跳到 3 点),实际跨度可能变成 23 小时或 25 小时,而 Duration 仍会返回 86400 秒——它不感知日历规则。
立即学习“Java免费学习笔记(深入)”;
- 跨时区比较必须先转成
ZonedDateTime或Instant再调Duration.between() - 若只关心“日历上过了几天”,别用
Duration,改用ChronoUnit.DAYS.between(ldt1, ldt2)或构造Period -
LocalDateTime之间用Duration仅适用于同地、无 DST 影响、且你明确只要“绝对秒数”的场景
Period 的 plus() 和 minus() 不是线性叠加,而是日历滚动
Period.ofMonths(1).plus(Period.ofDays(30)) 不等于 Period.ofMonths(1).plusDays(30),也不等于 “2 个月”。它本质是按日历顺序逐字段加:先加月,再加日,遇到 2 月 30 日会自动归整为 3 月 2 日(或 3 月 3 日,取决于是否闰年)。
典型误用是拿 Period 做数学运算:比如想算“当前日期加 45 天”,写成 localDate.plus(Period.ofDays(45)) 没问题;但写成 localDate.plus(Period.ofMonths(1).plusDays(15)) 就可能和预期不符——因为 ofMonths(1) 先滚到下月同日,再加 15 天,不是简单 +45。
-
Period的加减永远以“目标日期是否存在”为前提,不存在则取该月最后一天 - 不要对
Period做multipliedBy()后再加,比如Period.ofMonths(1).multipliedBy(2)是P2M,但localDate.plus(Period.ofMonths(1).multipliedBy(2))≠localDate.plusMonths(2)(后者更可靠) - 需要精确天数偏移时,优先用
LocalDate.plusDays()、plusWeeks()等直接方法,而非绕路Period
从数据库读取 INTERVAL 类型时,PostgreSQL/MySQL 返回类型不同
PostgreSQL 的 INTERVAL 默认映射为 Duration(JDBC 驱动 42.2+),而 MySQL 的 INTERVAL 实际是字符串,需手动解析;Oracle 的 INTERVAL DAY TO SECOND 可转 Duration,但 INTERVAL YEAR TO MONTH 必须转 Period。
最常踩的坑是 Hibernate/JPA 里用 @Column(columnDefinition = "INTERVAL") 却没配对的 AttributeConverter,导致读出来是 String 或抛 SQLException。
- PostgreSQL:查
interval字段 →ResultSet.getObject(idx, Duration.class)可用 - MySQL:查
time或varchar存的 "30:00:00" → 用Duration.parse("PT30H")(注意格式转换) - Oracle
YEAR TO MONTH→ 必须用Period.parse("P1Y3M"),不能强转Duration
跨数据库做时间间隔存储时,别图省事全用 long 存毫秒——丢失语义,后期改需求(比如要支持“每月1号执行”)就只能硬改表结构。










