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

时间:2018-07-23 17:12:24

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

我在编写一个测试用例时遇到了这个问题,我不得不在一个时间戳范围内获得一系列记录–使用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而言太大,不是应该失败吗?

P.S。如果已经问过这个问题,由于我找不到原始照片,有人可以链接它吗。

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

对于由TimestampInstant.MIN组成的Instant.MAX实例,我具有以下值:

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

对于由TimestampLong.MIN_VALUE制成的Long.MAX_VALUE实例,我得到:

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

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

1 个答案:

答案 0 :(得分:3)

这是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。不会。

链接