DWilches
DWilches

Reputation: 23015

Java Instant's bug? `DateTimeException: Invalid value for Year`

According to the documentation for Instant, the minimum Instant is -1000000000-01-01T00:00Z, so the year is -1000000000 and the timezone is UTC. so I expected this program to work, and the atOffset to be a noop:

import java.time.*;

public class A {
   public static void main(String[] args) {
      Instant i = Instant.MIN;
      System.out.println(i);
      System.out.println(i.atOffset(ZoneOffset.UTC));
   }
}

But instead it throws this exception on the atOffset:

-1000000000-01-01T00:00:00Z
Exception in thread "main" java.time.DateTimeException: Invalid value for Year (valid values -999999999 - 999999999): -1000000000
        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.OffsetDateTime.ofInstant(OffsetDateTime.java:328)
        at java.time.Instant.atOffset(Instant.java:1195)
        at A.main(A.java:7)

Is that a bug? According to that validation's message the minimum year is -999999999 but the documentation says it is -1000000000.

Upvotes: 0

Views: 3360

Answers (2)

Yassin Hajaj
Yassin Hajaj

Reputation: 21975

This is not a bug, actually the action of invoking

i.atOffset(ZoneOffset.UTC)

triggers the creation of a OffsetDateTime object which the javadoc clearly states the following

The minimum supported OffsetDateTime, '-999999999-01-01T00:00:00+18:00'.

So basically if you keep it an Instant it's ok, but trying to change it may cause a problem

Upvotes: 2

Compass
Compass

Reputation: 5937

atOffset returns an OffsetDateTime which has a different Min/Max.

The minimum supported OffsetDateTime, '-999999999-01-01T00:00:00+18:00'.

The maximum supported OffsetDateTime, '+999999999-12-31T23:59:59.999999999-18:00'.

The javadoc mentions that these min/max values are derived from LocalDateTime along with the max zone offsets, which appears to be why they don't match up to Instant.

The reason Instant has an extra year both ways, per documentation of Instant.

This is one year earlier than the minimum LocalDateTime. This provides sufficient values to handle the range of ZoneOffset which affect the instant in addition to the local date-time. The value is also chosen such that the value of the year fits in an int.

Covers the edge case where a LocalDateTime + Offset results in a time from the previous year.

The max practical value would be the minimum OffsetDateTime converted to an Instant, as everything prior to that date is a buffer for which there is no equivalent OffsetDateTime.

Upvotes: 5

Related Questions