gpt4 book ai didi

从 java.time.Instant 的 MIN/MAX 生成的 java.sql.Timestamp 的行为与从 Long.MIN_VALUE/Long.MAX_VALUE 构造时的行为不同

转载 作者:行者123 更新时间:2023-11-30 07:45:42 25 4
gpt4 key购买 nike

我在编写测试用例时遇到了这个问题,我必须在一系列时间戳之间获取一系列记录——使用 H2 嵌入式数据库和 spring-data-jpa.

原始问题位于:Fetching records BETWEEN two java.time.Instant instances in Spring Data Query

我将时间戳作为 java.time.Instant 实例。

如果用户没有提供开始和结束时间戳,我会继续并分别插入 Instant.MINInstant.MAX

令我困惑的是以下测试用例通过了:

  @Test
public void test_date_min_max_instants_timestamps() {
Timestamp past = new Timestamp(Long.MIN_VALUE);
Timestamp future = new Timestamp(Long.MAX_VALUE);
Timestamp present = Timestamp.from(Instant.now());

assertTrue(present.after(past));
assertTrue(future.after(past));
assertTrue(future.after(present));

assertTrue(present.before(future));
assertTrue(past.before(present));
assertTrue(past.before(future));
}

但是,下面的测试用例失败了:

 @Test
public void test_instant_ranges() throws InterruptedException {
Timestamp past = Timestamp.from(Instant.MIN);
Timestamp future = Timestamp.from(Instant.MAX);
Timestamp present = Timestamp.from(Instant.now());

assertTrue(present.after(past));
assertTrue(future.after(past));
assertTrue(future.after(present));

assertTrue(present.before(future));
assertTrue(past.before(present));
assertTrue(past.before(future));
}

此外,如果pastfuture不是MIN/MAX值,而是正常值,则结果符合预期。

知道为什么 java.sql.Timestamp 会这样吗?

此外,如果 Instant 表示的时间对于 Timestamp 来说太大了,它不应该失败吗?

附言如果这个问题已经被问过,有人可以链接原件吗,因为我找不到它。

编辑:添加了我在评论部分提到的调试信息,以便我们将所有内容集中在一个地方。

对于由 Instant.MINInstant.MAX 生成的 Timestamp 实例,我有以下值:

past = 169108098-07-03 21:51:43.0 
future = 169104627-12-11 11:08:15.999999999
present = 2018-07-23 10:46:50.842

对于 Long.MIN_VALUELong.MAX_VALUETimestamp 实例,我得到:

past = 292278994-08-16 23:12:55.192 
future = 292278994-08-16 23:12:55.807
present = 2018-07-23 10:49:54.281

为了澄清我的问题,时间戳应该显式失败,而不是默默地失败或在内部使用不同的值。目前没有。

最佳答案

这是 Timestamp 类及其从 Instant 的转换中的一个已知错误。它于三年半前于 2015 年 1 月在 Java 错误数据库中注册(并且仍然开放,没有确定的修复版本)。请参阅底部的官方错误报告链接。

预期的行为很明确

Timestamp.from(Instant) 的文档对此非常清楚:

Instant can store points on the time-line further in the future and further in the past than Date. In this scenario, this method will throw an exception.

所以是的,应该抛出异常。

重现bug很简单

在我的 Java 10 上,我重现了几个示例,在这些示例中,转换默默地给出了不正确的结果,而不是抛出异常。一个例子是:

        Instant i = LocalDate.of(-400_000_000, Month.JUNE, 14)
.atStartOfDay(ZoneId.of("Africa/Cairo"))
.toInstant();
Timestamp ts = Timestamp.from(i);
System.out.println("" + i + " -> " + ts + " -> " + ts.toInstant());

这打印:

-400000000-06-13T21:54:51Z -> 184554049-09-14 14:20:42.0 -> +184554049-09-14T12:20:42Z

前一个转换显然是错误的:很久以前的时间已经被转换成遥远(虽然不是很远)的 future (转换回 Instant 似乎是正确)。

附录:JDK源码

好奇的是转换方法的实现:

public static Timestamp from(Instant instant) {
try {
Timestamp stamp = new Timestamp(instant.getEpochSecond() * MILLIS_PER_SECOND);
stamp.nanos = instant.getNano();
return stamp;
} catch (ArithmeticException ex) {
throw new IllegalArgumentException(ex);
}
}

作者似乎预料到乘法中的算术溢出会导致 ArithmeticException。它没有。

链接

关于从 java.time.Instant 的 MIN/MAX 生成的 java.sql.Timestamp 的行为与从 Long.MIN_VALUE/Long.MAX_VALUE 构造时的行为不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51484091/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com