计算闰年之间的年份



在计算两个日期之间的年份时,第二个日期是从第一个日期计算的(这是我正在研究的一个简化示例),LocalDatePeriod似乎计算年份略有不同。

例如,

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天!)。

最新更新