哪些 MIN/MAX 值适用于 ZonedDateTime 和 Instant.toEpochMilli?

What MIN/MAX values will work with both ZonedDateTime and Instant.toEpochMilli?

我想使用 MIN/MAX 可以在 ZonedDateTimeInstant.toEpochMilli() 之间转换的时间值,用作 filter/query.[=21= 的标记值]

我试过:

OffsetDateTime.MIN.toInstant().toEpochMilli();
OffsetDateTime.MAX.toInstant().toEpochMilli();

但我得到这个例外:

java.lang.ArithmeticException: long overflow

    at java.lang.Math.multiplyExact(Math.java:892)
    at java.time.Instant.toEpochMilli(Instant.java:1237)

然后我尝试了这个:

ZonedDateTime.ofInstant(Instant.MIN, ZoneId.systemDefault());
ZonedDateTime.ofInstant(Instant.MAX, ZoneId.systemDefault());

但后来我得到了这个例外:

java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): -1000000001

    at java.time.temporal.ValueRange.checkValidIntValue(ValueRange.java:330)
    at java.time.temporal.ChronoField.checkValidIntValue(ChronoField.java:722)
    at java.time.LocalDate.ofEpochDay(LocalDate.java:341)
    at java.time.LocalDateTime.ofEpochSecond(LocalDateTime.java:422)
    at java.time.ZonedDateTime.create(ZonedDateTime.java:456)
    at java.time.ZonedDateTime.ofInstant(ZonedDateTime.java:409)

我也试过了'Z'ZoneId:

ZonedDateTime.ofInstant(Instant.MIN, ZoneId.of("Z"))

但是 returns 与上一个异常相同。

最后我尝试了以下方法,它似乎有效:

ZonedDateTime.ofInstant(Instant.EPOCH, ZoneId.of("Z"));
ZonedDateTime.ofInstant(Instant.EPOCH.plusMillis(Long.MAX_VALUE), ZoneId.of("Z"));

这是最好的解决方案吗?

Instant.EPOCH 等同于 1970-01-01T00:00Z,所以我不确定它是否适合最小值(当然,这取决于您的需要 - 如果您的所有日期都在1970年,那就好了。

Instant.MINInstant.MAX 转换为 ZonedDateTime 并不总是有效,因为根据偏移量,字段可以设置为超出边界的值(就像发生在年,在你的情况下)。

如果你想要可以转换为 ZonedDateTimeInstant 的遥远的日期,你不需要使用内置的 MIN/MAX 常量,因为它们使用非常远日期(从 -1000000000 到 1000000000 这样的年份),以及此类远日期的等效 epoch milli 值远大于(或小于)long 值的限制.

因为您需要使用 toEpochMilli() 并且它 returns 和 long,您必须保持在 long 值的限制之间:

Instant minInstant = Instant.ofEpochMilli(Long.MIN_VALUE);
Instant maxInstant = Instant.ofEpochMilli(Long.MAX_VALUE);
ZonedDateTime minZonedDateTime = minInstant.atZone(ZoneOffset.UTC);
ZonedDateTime maxZonedDateTime = maxInstant.atZone(ZoneOffset.UTC);

各自的值为:

minInstant=-292275055-05-16T16:47:04.192Z
maxInstant=+292278994-08-17T07:12:55.807Z
minZonedDateTime=-292275055-05-16T16:47:04.192Z
maxZonedDateTime=+292278994-08-17T07:12:55.807Z

以及纪元毫的相应值:

System.out.println("minInstant millis=" + minInstant.toEpochMilli());
System.out.println("maxInstant millis=" + maxInstant.toEpochMilli());

minInstant millis=-9223372036854775808
maxInstant millis=9223372036854775807

我使用 ZoneOffset.UTC 而不是某个特定的时区,因为这些日期到目前为止都在 past/future 中,几个小时的偏移不会造成太大影响。而且它们都将等同于相同的毫秒瞬间。

您也可以使用 atOffset 方法(例如 minInstant.atOffset(ZoneOffset.UTC))将 Instant 转换为 OffsetDateTime


备注:

  • 而不是 ZoneId.of("Z"),您可以使用常量 ZoneOffset.UTC。实际上,如果你检查ZoneId.of("Z").equals(ZoneOffset.UTC),结果是true
    甚至 ZoneId.of("Z") == ZoneOffset.UTC 也是 true,所以两者实际上是一回事。
  • 根据您的查询值,您不需要使用这么远的日期。例如,您可以将最小值设置为 1900 年,将最大值设置为 2900 年。这完全取决于您的数据集。但是,如果您的数据库可以处理一年中如此大的值,那么使用上述方法就可以了。