【问题标题】:java.sql.Timestamp made from java.time.Instant's MIN/MAX behaves differently than when constructed from Long.MIN_VALUE / Long.MAX_VALUE从 java.time.Instant 的 MIN/MAX 生成的 java.sql.Timestamp 的行为与从 Long.MIN_VALUE / Long.MAX_VALUE 构造时的行为不同
【发布时间】:2018-07-23 17:12:24
【问题描述】:

我在编写一个测试用例时遇到了这个问题,我必须在一系列时间戳之间获取一系列记录——使用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_VALUE 组成的Timestamp 实例,我得到:

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

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

【问题讨论】:

  • 另外,如果 Instant 表示的时间对于 Timestamp 来说太大了,它不应该失败吗? 而不是什么?
  • What does your step debugger tell you?。使用步进调试器可以非常快速轻松地回答您的问题。在使用 StackOverflow 之前,您应该始终尝试使用步进调试器解决您的问题。
  • 对于由 Instant.MIN 和 Instant.MAX 生成的时间戳实例 -- 过去 = 169108098-07-03 21:51:43.0 未来 = 169104627-12-11 11:08:15.999999999 现在 = 2018 -07-23 10:46:50.842 对于由 Long.MIN_VALUE 和 Long.MAX_VALUE 制作的时间戳实例,我得到:过去 = 292278994-08-16 23:12:55.192 未来 = 292278994-08-16 23:12:55.807现在 = 2018-07-23 10:49:54.281我发布这个问题的原因是我自己无法解决。
  • @shmosel 而不是静默失败或在内部使用不同的值。基本上,我想要得到的是:如果不可能,转换应该明确失败。
  • 感谢@sidmishraw 添加更多信息。请在问题中 (edit it) 这样做,而不是在 cmets 中这样做,以便我们将所有内容集中在一个地方。谢谢。

标签: java java-8 java-time java.time.instant


【解决方案1】:

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

预期的行为很明确

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

Instant 可以在未来更远的时间线上存储点并且 比Date 更早。在这种情况下,此方法将 抛出异常。

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

重现错误很简单

在我的 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

前面的转换很明显是错误的:很久以前的时间已经转换为遥远(虽然不是很远)未来的时间(转换回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。它没有。

链接

【讨论】:

    猜你喜欢
    • 2015-07-23
    • 2018-11-05
    • 1970-01-01
    • 2015-07-06
    • 2012-08-26
    • 2016-08-22
    • 1970-01-01
    • 2020-12-23
    • 1970-01-01
    相关资源
    最近更新 更多