ZonedDateTime返回一个日期的错误月数,直到另一个结束日期



使用ZonedDateTime.until返回给定日期到给定结束日期的错误月数。看起来好像和二月有关;)

这是我写的测试

@Test
fun `three months from end of november till first of march`() {
val dateOfNow = LocalDate.of(2022, 11, 30)
val timeOfNow = LocalTime.of(0, 0, 0, 1)
val dateTimeOfNow = LocalDateTime.of(dateOfNow, timeOfNow)
val timeZoneOfTestland = ZoneOffset.of("+00:00")
val zonedDateTimeOfNow = dateTimeOfNow.atZone(timeZoneOfTestland)
val dateEnd = LocalDate.of(2023, 3, 1)
val timeEnd = LocalTime.of(0, 0, 0, 0)
val dateTimeEnd = LocalDateTime.of(dateEnd, timeEnd)
val zonedDateEnd = dateTimeEnd.atZone(timeZoneOfTestland)
val until = zonedDateTimeOfNow.until(zonedDateEnd, ChronoUnit.MONTHS)
assertEquals(3, until)
}

它返回2而不是预期的3。

当我更改dateTimeOfNow的纳秒时,测试成功

val timeOfNow = LocalTime.of(0, 0, 0, 0)

但是当我将日期设置为11月29日时,它又失败了

val dateOfNow = LocalDate.of(2022, 11, 29)
val timeOfNow = LocalTime.of(0, 0, 0, 1)

在我看来,到2023年3月1日显然是3个月。无论如何。

我尝试使用这些日期不改变年份

val dateOfNow = LocalDate.of(2022, 1, 31)
val timeOfNow = LocalTime.of(0, 0, 0, 1)
val dateTimeOfNow = LocalDateTime.of(dateOfNow, timeOfNow)
val timeZoneOfTestland = ZoneOffset.of("+00:00")
val zonedDateTimeOfNow = dateTimeOfNow.atZone(timeZoneOfTestland)
val dateEnd = LocalDate.of(2022, 5, 1)
val timeEnd = LocalTime.of(0, 0, 0, 0)
val dateTimeEnd = LocalDateTime.of(dateEnd, timeEnd)
val zonedDateEnd = dateTimeEnd.atZone(timeZoneOfTestland)
val until = zonedDateTimeOfNow.until(zonedDateEnd, ChronoUnit.MONTHS)
assertEquals(3, until)

但用until == 2代替3也失败。

对此有什么解释吗?

查看OffsetDateTime#until(重点是我的)的以下文档:

计算返回一个整数,表示的个数<<strong>完成单位/strong>在两个约会时间之间。例如,金额从2012-06-15 t00:00到2012-08-14 t23:59之间的月份只会是一个月,因为它比两个月差一分钟。

在您的示例中,差异是由1纳秒引起的。请记住,2023-02-29和2023-02-30不存在。

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
public class Main {
public static void main(String[] args) {
// 2 months
// 2022-11-30T0:0:0:1 to 2022-12-30T0:0:0:1 - complete
// 2022-12-30T0:0:0:1 to 2023-01-30T0:0:0:1 - complete
// 2023-01-30T0:0:0:1 to 2023-03-01T0:0:0:1 would have been one month but
// the endtime is 2023-03-01T0:0:0:0, 1 nanosecond short
System.out.println(LocalDateTime.of(2022, 11, 30, 0, 0, 0, 1).until(LocalDateTime.of(2023, 3, 1, 0, 0, 0, 0),
ChronoUnit.MONTHS));
// 3 months as explained above
System.out.println(LocalDateTime.of(2022, 11, 30, 0, 0, 0, 1).until(LocalDateTime.of(2023, 3, 1, 0, 0, 0, 1),
ChronoUnit.MONTHS));
// 3 months
// 2022-11-30T0:0:0:0 to 2022-12-30T0:0:0:0 - complete
// 2022-12-30T0:0:0:0 to 2023-01-30T0:0:0:0 - complete
// 2023-01-30T0:0:0:0 to 2023-03-01T0:0:0:0 - complete
System.out.println(LocalDateTime.of(2022, 11, 30, 0, 0, 0, 0).until(LocalDateTime.of(2023, 3, 1, 0, 0, 0, 0),
ChronoUnit.MONTHS));
// 3 months
// 2022-11-28T0:0:0:1 to 2022-12-28T0:0:0:1 - complete
// 2022-12-28T0:0:0:1 to 2023-01-28T0:0:0:1 - complete
// 2023-01-28T0:0:0:1 to 2023-02-28T0:0:0:1 - complete
System.out.println(LocalDateTime.of(2022, 11, 28, 0, 0, 0, 1).until(LocalDateTime.of(2023, 3, 1, 0, 0, 0, 0),
ChronoUnit.MONTHS));
}
}

:

2
3
3
3

看起来使用Date而不是DateTime是计算两个日期之间的月数的最佳方法。因为我的函数gets有两个ZonedDateTime参数我要这样做

LocalDate.from(zonedDateTimeOfNow).until(LocalDate.from(zonedDateTimeEnd), ChronoUnit.MONTHS)

最新更新