ChronoUnit.DAYS.between() 是首选,因它直接返回两个 Temporal 间的完整天数,不四舍五入、时区影响明确;Period 适合人眼可读的“年月日”差,不适用于精确天数计算。

用 ChronoUnit.DAYS.between() 最直接,但要注意时区和日期类型;Period 适合人眼可读的“年月日”差,不适合精确天数计算。
为什么 ChronoUnit.DAYS.between() 是首选
它直接返回两个 Temporal(如 LocalDate、Instant)之间的完整天数,不四舍五入、不忽略时区影响(取决于输入类型)。关键点:
-
LocalDate输入:纯日期比较,无视时间与时区,结果最稳定 -
Instant或ZonedDateTime输入:会按实际毫秒差换算天数,受时区偏移影响(比如ZonedDateTime在夏令时切换日可能差1天) - 参数顺序敏感:
ChronoUnit.DAYS.between(start, end),反了会得负数
示例:
LocalDate start = LocalDate.of(2023, 1, 1); LocalDate end = LocalDate.of(2023, 1, 5); long days = ChronoUnit.DAYS.between(start, end); // 得 4
Period.between() 适合什么场景
它返回的是“逻辑上的年月日差”,不是总天数。比如 2023-01-31 到 2023-02-28,Period 算出是 P1M28D(1个月28天),但实际天数是 28 天——它不帮你换算成单一数值。
立即学习“Java免费学习笔记(深入)”;
- 只接受
LocalDate,不支持Instant或带时区类型 - 结果中的
getDays()是“剩余天数”,不是总天数;getTotalDays()不存在,别想调用 - 适合展示给用户看:“已过去 2 年 3 个月 5 天”,不适合做数学运算或条件判断
错误写法(以为能拿到总天数):
Period p = Period.between(LocalDate.of(2022, 1, 1), LocalDate.of(2024, 4, 6)); int d = p.getDays(); // 得 5,不是 826
容易踩的坑:时区、类型混用和边界值
三个高频翻车点:
- 拿
ZonedDateTime直接喂给ChronoUnit.DAYS.between():如果两个时间在不同时区或夏令时交界日,结果可能比预期少或多 1 天 - 用
LocalDateTime当作日期用:它包含时间部分,between()会算到毫秒级,2023-01-01T00:00:00 和 2023-01-02T00:00:00.001 之间是 1 天,但 2023-01-01T23:59:59 和 2023-01-02T00:00:00 是 0 天 - 忽略
LocalDate.now()的默认时区:它依赖系统默认时区,跨服务部署时若时区不一致,同一天可能算出不同结果
稳妥做法:统一转成 LocalDate 再算,或明确指定时区:
LocalDate today = LocalDate.now(ZoneId.of("UTC"));
性能与兼容性提醒
ChronoUnit.DAYS.between() 是纯计算,无对象创建开销,JDK 8+ 均支持;Period 构造成本略高(内部存三个字段),且 JDK 8 引入,老项目若还在用 Joda-Time 需迁移。
真正麻烦的不是 API 选哪个,而是你传进去的两个时间,到底代表“日历上的某一天”,还是“某个精确时刻”。没理清这个,函数再对也白搭。










