当计算两个日期之间的年数时,第二个日期是从第一个日期开始计算的(这是我正在处理的一个简化示例),LocalDate
和 Period
似乎计算年份略有不同。
例如,
LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusYear = date.plusYears(1);
System.out.println(Period.between(date, plusYear).getYears());
同时
LocalDate date = LocalDate.of(1996, 3, 29);
LocalDate plusYear = date.plusYears(1);
System.out.println(Period.between(date, plusYear).getYears());
尽管明确添加了年份,第一个 Period
将年份返回为 0
,而第二个案例返回 1
。
有解决这个问题的巧妙方法吗?
最佳答案
这个问题具有哲学性质,涉及时间测量和日期格式约定等几个问题。
LocalDate
是 ISO 8601 的一个实现日期交换标准。
Java Doc 明确指出此类不表示时间,而仅提供标准日期表示法。
API 仅提供对符号本身的简单操作,所有计算都是通过递增年、月或日来完成的给定的日期。
换句话说,当调用 LocalDate.plusYears()
时,您添加的是每年 365 天的概念年,而不是一年中的确切时间量。
这使得 Day 成为可以添加到由 LocalDate
表示的日期的最小时间单位。
在人类的理解中,date 不是一个时刻,而是一个时期。
它以 00h 00m 00s (...) 开始,以 23h 59m 59s (...) 结束。
LocalDate
然而避免了时间测量和人类时间单位模糊的问题(小时、日、月 , 和 year 都可以有不同的长度)和模型日期符号简单地作为一个元组:
(年,一年中的几个月,一个月中的几天)
从纪元开始计算。
在这种解释中,日 是影响日期的最小单位是有道理的。
例如:
LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusSecond = date.plus(1, ChronoUnit.SECONDS);
返回
java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: Seconds
... 这表明,使用 LocalDate
并添加秒数(或更小的单位来提高精度),您无法克服问题中列出的限制。
查看实现,您会发现 LocalDate.plusYears()
在添加年份后调用 resolvePreviousValid()
。然后,此方法检查闰年并按以下方式修改 day 字段:
day = Math.min(day, IsoChronology.INSTANCE.isLeapYear((long)year)?29:28);
换句话说,它通过有效地扣除 1 天来纠正它。
您可以使用 Year.length()
返回给定年份的天数,并返回 366 闰年。所以你可以这样做:
LocalDate plusYear = date.plus(Year.of(date.getYear()).length(), ChronoUnit.DAYS);
您仍然会遇到以下奇怪情况(为简洁起见,调用 Year.length()
替换为天数):
LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusYear = date.plus(365, ChronoUnit.DAYS);
System.out.println(plusYear);
Period between = Period.between(date, plusYear);
System.out.println( between.getYears() + "y " +
between.getMonths() + "m " +
between.getDays() + "d");
返回
1997-02-28
0y 11m 30d
然后
LocalDate date = LocalDate.of(1996, 3, 29);
LocalDate plusYear = date.plus(365, ChronoUnit.DAYS);
System.out.println(plusYear);
Period between = Period.between(date, plusYear);
System.out.println( between.getYears() + "y " +
between.getMonths() + "m " +
between.getDays() + "d");
返回
1997-03-29
1y 0m 0d
最后:
LocalDate date = LocalDate.of(1996, 2, 29);
LocalDate plusYear = date.plus(366, ChronoUnit.DAYS);
System.out.println(plusYear);
Period between = Period.between(date, plusYear);
System.out.println( between.getYears() + "y " +
between.getMonths() + "m " +
between.getDays() + "d");
返回:
1997-03-01
1y 0m 1d
请注意,将日期移动 366 而不是 365 天可将期间从 11 个月零 30 天 增加到 1年零 1 天(增加 2 天!)。
关于java - 从闰年计算年数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32944569/