I want to use MIN/MAX time values that can convert between ZonedDateTime
and Instant.toEpochMilli()
, to be used as sentinel values for a filter/query.
I tried:
OffsetDateTime.MIN.toInstant().toEpochMilli();
OffsetDateTime.MAX.toInstant().toEpochMilli();
but I get this exception:
java.lang.ArithmeticException: long overflow
at java.lang.Math.multiplyExact(Math.java:892)
at java.time.Instant.toEpochMilli(Instant.java:1237)
And then I tried this:
ZonedDateTime.ofInstant(Instant.MIN, ZoneId.systemDefault());
ZonedDateTime.ofInstant(Instant.MAX, ZoneId.systemDefault());
but then I get this exception:
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)
I also tried 'Z' ZoneId
:
ZonedDateTime.ofInstant(Instant.MIN, ZoneId.of("Z"))
but that returns the same exception as the last one.
Finally I tried the following, and it seems to work:
ZonedDateTime.ofInstant(Instant.EPOCH, ZoneId.of("Z"));
ZonedDateTime.ofInstant(Instant.EPOCH.plusMillis(Long.MAX_VALUE), ZoneId.of("Z"));
Is that the best solution?
Instant.EPOCH
is equivalent to 1970-01-01T00:00Z
, so I'm not sure if it's a good candidate for a minimum value (it depends on your needs, of course - if all your dates are after 1970, then it would be fine).
Converting Instant.MIN
and Instant.MAX
to ZonedDateTime
doesn't work all the time because depending on the offset, the fields can be set to values beyond boundaries (like happened with the year, in your case).
If you want far distant dates that can be converted to ZonedDateTime
and Instant
, you don't need to use the built-in MIN/MAX constants, because they use very far dates (from years like -1000000000 to 1000000000), and the equivalent epoch milli values for such far dates are much greater (or lower) than the limits of a long
value.
As you need to use toEpochMilli()
and it returns a long
, you must stay between the limits of a long
value:
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);
The respective values are:
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
And the respective values for epoch milli:
System.out.println("minInstant millis=" + minInstant.toEpochMilli());
System.out.println("maxInstant millis=" + maxInstant.toEpochMilli());
minInstant millis=-9223372036854775808
maxInstant millis=9223372036854775807
I used ZoneOffset.UTC
instead of some specific timezone, because those dates are so far in the past/future, that a few hours offset won't make much difference. And they would all be equivalent to the same millis instant anyway.
You could also convert the Instant
to OffsetDateTime
using atOffset
method (such as minInstant.atOffset(ZoneOffset.UTC)
).
Notes:
- instead of
ZoneId.of("Z")
, you can use the constantZoneOffset.UTC
. Actually, if you checkZoneId.of("Z").equals(ZoneOffset.UTC)
, the result istrue
.
EvenZoneId.of("Z") == ZoneOffset.UTC
is alsotrue
, so both are really the same thing. - depending on the values of your query, you don't need to use such extreme far dates. You could set the minimum to year 1900 and the maximum to 2900, for example. It all depends on your dataset. But using the approach above will be fine, if your database can handle such big values for the year.