在计算两个日期之间的年份时,第二个日期是从第一个日期计算的(这是我正在研究的一个简化示例),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只提供了简单的符号运算,所有的计算都是通过递增Year,或Month,或Day来完成的。
换句话说,当调用LocalDate.plusYears()
时,您添加的是每年365天的概念年,而不是一年内的确切时间。
这使得Day是可以添加到LocalDate
表示的日期的最低时间单位。
在人类的理解中,日期不是时间上的一个时刻,而是一个周期。
从00h 00m 00s(…)开始,到23h 59m 59s(…)结束。
然而, LocalDate
避免了时间测量和人类时间单位模糊的问题(小时、日、月和年都可以有不同的长度),并将日期符号简单地建模为一个元组:
(years, months within a year, days within a month )
从纪元开始算起。
在这种解释中,Day是影响日期的最小单位是有意义的。
例如:
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 = 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天!)。