ChronoUnit.DAYS.between()最直接计算两个LocalDate间的整数天数差,不涉时区与时间部分;需确保参数为LocalDate类型,否则抛异常;结果可正可负,性能优;Period.between()返回日历跨度而非总天数;避免用Date/Calendar毫秒相减;LocalDate.now()须显式指定业务所需时区。

用 ChronoUnit.DAYS.between() 算天数最直接
它返回两个 LocalDate 之间的**整数天数差**,不关心时区、不考虑时间部分,只看日期本身。适合绝大多数“日历天数”需求,比如计算请假天数、活动持续天数。
常见错误是传入 LocalDateTime 或 ZonedDateTime —— 会抛 DateTimeException,因为 between() 要求两个参数类型必须一致且为日期型(LocalDate、YearMonth 等)。
- 必须先用
.toLocalDate()截断时间部分:ChronoUnit.DAYS.between(start.toLocalDate(), end.toLocalDate()) - 结果可正可负:如果
start在end后,返回负值;需要绝对值就套Math.abs() - 性能好、无对象创建开销,底层是纯数值计算
Period.between() 返回的是“年月日”结构,不是天数
它算的是两个 LocalDate 之间按日历规则的“跨度”,比如从 2023-01-15 到 2024-03-10 是 P1Y1M24D(1年1个月24天),但 Period 没有“总天数”字段。
有人误调 period.getDays(),以为得到总天数——其实只是“日”字段(24),不是 365+31+24=420 这种累计值。
立即学习“Java免费学习笔记(深入)”;
- 想转成总天数?必须手动计算:
start.until(end, ChronoUnit.DAYS)(等价于ChronoUnit.DAYS.between()) -
Period主要用于格式化展示或做日期加减(如 “加1个月零5天”),不适合计数场景 - 跨闰年、大小月时,
Period的语义更贴近人类直觉,但数值不可直接用于数学运算
别用 Date 和 Calendar 做减法
老代码里常见 (date2.getTime() - date1.getTime()) / (1000 * 60 * 60 * 24),看着简单,实际埋雷:
- 结果是
double,强制转int会截断小数,遇到夏令时切换可能差1天 - 没处理时区:两个
Date对象本质是毫秒戳,但构造时若用SimpleDateFormat解析字符串,时区不一致会导致偏差 - Java 8+ 已标记
Date和Calendar为 legacy,维护成本高、易出错
真要兼容旧 API,也请先统一转成 Instant 再用 ChronoUnit:ChronoUnit.DAYS.between(date1.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), ...)
注意 LocalDate.now() 的隐式时区依赖
很多人写 ChronoUnit.DAYS.between(LocalDate.now(), someDate),却没意识到 LocalDate.now() 默认用系统默认时区获取当前日期 —— 在服务器部署时,如果 JVM 时区和业务时区不一致(比如服务器在 UTC,业务在 Asia/Shanghai),凌晨时段可能算错1天。
- 显式指定时区更安全:
LocalDate.now(ZoneId.of("Asia/Shanghai")) - 如果业务逻辑本就基于 UTC,那就用
LocalDate.now(ZoneOffset.UTC) - 千万别依赖
System.getProperty("user.timezone"),它不一定反映 JVM 实际生效的时区
日期计算里最麻烦的从来不是函数怎么写,而是“今天”到底指哪一天 —— 这个“今天”得由你明确定义,而不是交给系统猜。










