Search code examples
javadatetimedatetime-formatzoneddatetimejava-17

Why "-190732550-MM-ddTHH:mm:ss.SSSZ" datetime does not fail to parse


Consider the following snippet:

String dateTimeStr = "-190732550-05-25T15:14:51.136Z";
ZonedDateTime dateTime = ZonedDateTime.parse(dateTimeStr).withZoneSameInstant(ZoneOffset.UTC);

log.info("String: {}", dateTimeStr);
log.info("ZonedDateTime: {}", dateTime);
log.info("EpochMilli: {}", dateTime.toInstant().toEpochMilli());

Output:

String: -190732550-05-25T15:14:51.136Z
ZonedDateTime: -190732550-05-25T15:14:51.136Z
EpochMilli: -6019000079877908864

I expected the above would throw java.time.format.DateTimeParseException.

Edit

"190732550-05-25T15:14:51.136Z" throws java.time.format.DateTimeParseException. Shouldn't be valid too?


Solution

  • tl;dr

    This:

    Instant.parse( "-190732550-05-25T15:14:51.136Z" )
    

    … succeeds because your input of a few hundreds of millions of years lies within the range of +/- one billion years in that class.

    -1,000,000,000 < -190,732,550 < +1,000,000,000

    Use Instant class to parse an input string ending in Z, which means an offset from UTC of zero hours-minutes-seconds. No time zone involved, so no need for ZonedDateTime.

    Instant instant = Instant.parse( "-190732550-05-25T15:14:51.136Z" ) ;
    

    This input will indeed be parsed successfully. You are supplying a year that lies within the range of possible values for that data type.

    The minimum value for Instant is represented in the predefined constant Instant.MIN. To quote the Javadoc:

    The minimum supported Instant, '-1000000000-01-01T00:00Z'.

    That is the year -1,000,000,000 (one billion).

    You input year is only -190,732,550 (hundreds of millions). So your value fits within the accepted range of +/- one billion years.

    The Instant class is currently implemented as a pair of 64-bit long integers. One is for a number of whole seconds since the first moment of 1970 UTC. The other is for a count of nanoseconds in the fractional second. So this class is capable of supporting a wide range of values.

    Positivity

    Note that positive-numbered years with more than four digits must prepend a PLUS SIGN character.

    To quote the Javadoc for the predefined constant DateTimeFormatter named ISO_LOCAL_DATE, which is used by ISO_LOCAL_DATE_TIME, which is used by ISO_OFFSET_DATE_TIME, which is used by ISO_INSTANT when calling Instant.parse (emphasis mine):

    Four digits or more for the year. Years in the range 0000 to 9999 will be pre-padded by zero to ensure four digits. Years outside that range will have a prefixed positive or negative symbol.

    This succeeds:

    Instant.parse( "+190732550-05-25T15:14:51.136Z" ) 
    

    … whereas this fails:

    Instant.parse( "190732550-05-25T15:14:51.136Z" ) 
    

    See this code run live at IdeOne.com.