我有两个LocalDate
声明如下:
val startDate = LocalDate.of(2019, 10, 31) // 2019-10-31
val endDate = LocalDate.of(2019, 9, 30) // 2019-09-30
然后我使用Period.between
函数计算它们之间的周期:
val period = Period.between(startDate, endDate) // P-1M-1D
在此期间,负数的月份和天数,如果endDate
早于startDate
,则可以预期。
但是,当我将period
添加回startDate
时,得到的结果不是endDate
,而是前一天的日期:
val endDate1 = startDate.plus(period) // 2019-09-29
所以问题是,为什么不不变
startDate.plus(Period.between(startDate, endDate)) == endDate
为这两个日期举行?
是Period.between
返回错误的期间,还是LocalDate.plus
添加错误的期间?
如果您查看如何为plus
实现LocalDate
@Override
public LocalDate plus(TemporalAmount amountToAdd) {
if (amountToAdd instanceof Period) {
Period periodToAdd = (Period) amountToAdd;
return plusMonths(periodToAdd.toTotalMonths()).plusDays(periodToAdd.getDays());
}
...
}
您将在此处看到plusMonths(...)
和plusDays(...)
。
[plusMonths
处理一个月有31天而另一个有30天的情况。因此以下代码将打印2019-09-30
而不是不存在的2019-09-31
println(startDate.plusMonths(period.months.toLong()))
[此后,减去一天将得到2019-09-29
。这是正确的结果,因为2019-09-29
和2019-10-31
相隔1个月1天
Period.between
计算很奇怪,在这种情况下归结为
LocalDate end = LocalDate.from(endDateExclusive);
long totalMonths = end.getProlepticMonth() - this.getProlepticMonth();
int days = end.day - this.day;
long years = totalMonths / 12;
int months = (int) (totalMonths % 12); // safe
return Period.of(Math.toIntExact(years), months, days);
其中getProlepticMonth
是从00-00-00的月份总数。在这种情况下,是1个月零1天。
据我了解,这是负周期交互作用的Period.between
和LocalDate#plus
中的错误,因为以下代码具有相同的含义
val startDate = LocalDate.of(2019, 10, 31)
val endDate = LocalDate.of(2019, 9, 30)
val period = Period.between(endDate, startDate)
println(endDate.plus(period))
但是它打印正确的2019-10-31
。
问题是LocalDate#plusMonths
将日期归一化为始终“正确”。在以下代码中,您可以看到,从2019-10-31
减去1个月后,结果是2019-09-31
,然后将其标准化为2019-10-30
public LocalDate plusMonths(long monthsToAdd) {
...
return resolvePreviousValid(newYear, newMonth, day);
}
private static LocalDate resolvePreviousValid(int year, int month, int day) {
switch (month) {
...
case 9:
case 11:
day = Math.min(day, 30);
break;
}
return new LocalDate(year, month, day);
}