9

在计算两个日期之间的年份时,第二个日期是从第一个日期计算的(这是我正在研究的一个简化示例),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

有没有一个巧妙的方法来解决这个问题?

4

1 回答 1

3

这个问题具有哲学性质,涉及时间测量和日期格式约定等几个问题。

LocalDateISO 8601日期交换标准的实施。Java Doc 明确指出该类不表示时间,而仅提供标准日期表示法。

API 仅提供对符号本身的简单操作,所有计算都是通过递增给定日期的YearMonthDay来完成的。

换句话说,当你打电话给你时,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()which 返回给定年份的天数,并在闰年返回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 天!)。

于 2015-10-14T17:16:24.493 回答