Period基于年、月、日三级字段对齐计算:先算年差(结束年−开始年,若结束月日<开始月日则年减1),再算月差(同理调整后计算),最后算日差(调整后结束日−开始日),不依赖总天数或闰年规则。

Period 是 Java 8 引入的 java.time 包中用于表示“年-月-日”时间量的不可变类,专为**人可读的日期差**设计(比如“2年3个月5天”),但它**不适用于计算精确到毫秒的时间差,也不处理时区或日历细节(如闰年、大小月)的底层运算**。它的计算基于日期字段的逐级对齐,而非总天数换算。
Period 是怎么算出来的?关键逻辑是“字段对齐”
Period 的计算不是用两个日期相减再除以365/30/1,而是按年、月、日三级分别对齐:
- 先算年差:结束年 - 开始年,但要确保“结束月日 ≥ 开始月日”,否则年数减1
- 再算月差:在调整后的年基础上,计算月差,同样要保证“结束日 ≥ 开始日”,否则月数减1
- 最后算日差:用调整年月后的“结束日 - 开始日”
例如:Period.between(LocalDate.of(2022, 3, 15), LocalDate.of(2024, 5, 20)) → P2Y2M5D(2年2个月5天)
而 Period.between(LocalDate.of(2022, 3, 25), LocalDate.of(2024, 5, 20)) → P2Y1M25D(因为 5月20日
正确使用 Period 计算日期差的步骤
必须用两个 LocalDate(不能是 LocalDateTime 或 Instant)调用 Period.between():
- 确保两个日期都是
LocalDate类型;如果手头是LocalDateTime,先用.toLocalDate() - 注意顺序:第一个参数是起点(earlier),第二个是终点(later);反序会得到负的 Period(如 P-1Y)
- 获取结果后,用
getYears()、getMonths()、getDays()分别取值,不要试图直接转成总天数
示例:
立即学习“Java免费学习笔记(深入)”;
LocalDate start = LocalDate.of(2020, 1, 30);LocalDate end = LocalDate.of(2023, 4, 5);
Period p = Period.between(start, end);
System.out.println(p); // P3Y2M6D
System.out.println(p.getYears() + "年" + p.getMonths() + "个月" + p.getDays() + "天"); // 3年2个月6天
常见误区和注意事项
- 不能跨时区或含时间信息:Period 不关心时刻、夏令时、UTC偏移。带时间的日期请先截断为日期
- 不等于“总天数 ÷ 365”:它不假设每年365天,而是严格按日历推算(如2024年2月有29天,会影响2月29日到次年2月28日的计算)
- 无法表示“30天”这种模糊周期:Period 只表达年月日三字段,不会把4周自动转成“0年0月28天”
- 和 Duration 完全不同:Duration 表示精确的秒/纳秒差,适合计算耗时;Period 表示日历意义上的跨度,适合展示给用户
什么时候不该用 Period?
如果你需要:
- 两个时间点之间总共多少天 → 用
ChronoUnit.DAYS.between(ld1, ld2) - 精确到小时/分钟的耗时 → 用
Duration.between(ldt1, ldt2) - 考虑工作日、节假日的业务天数 → 需第三方库(如 Time4J)或自定义逻辑
- 把 Period 当“固定长度”去加减日期 → 虽然可以(
localDate.plus(period)),但要注意:加 P1M 不一定等于加30天(2月只有28/29天)


















