向startDate添加期间不会产生endDate

问题描述 投票:2回答:1

我有两个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添加错误的期间?

kotlin java-time
1个回答
0
投票

如果您查看如何为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-292019-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.betweenLocalDate#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);
}
© www.soinside.com 2019 - 2024. All rights reserved.