6

I have some legacy KML documents which includes a time stamp entry. Why is the below date not valid when using Instant to parse? Both methods are suppose to parse ISO 8601 formatted dates.

String dateString = "2017-12-04T08:06:60Z"

Using

java.time.Instant.parse(dateString)

throws an error

"DateTimeParseException Text 2017-12-04T08:06:60Z could not be parsed at index 0."

However, when using

Date myDate =   javax.xml.bind.DatatypeConverter.parseDateTime( dateString )

myDate is parsed correctly....

2
  • 60 is not a valid number of seconds, is it? Also, class java.util.Calendar is lenient by default so I guess it accepts 60 seconds. Commented Mar 22, 2019 at 16:48
  • It can be for a leap second. But I don't think java.time supports leap seconds. (And I don't believe that's a valid leap second.) Commented Mar 22, 2019 at 16:55

2 Answers 2

6
  1. 60 seconds isn't a valid time. Meaning that this is invalid 2017-12-04T08:06:60Z, if it was 60 seconds then the minute should have incremented and your time would be 2017-12-04T08:07:00Z
  2. Using a valid date and then parsing the String would work just fine:

    String date = "2017-12-04T08:07:00Z";
    System.out.println(Instant.parse(date));
    

Also java.time ignores leap seconds. From the docs:

Implementations of the Java time-scale using the JSR-310 API are not required to provide any clock that is sub-second accurate, or that progresses monotonically or smoothly. Implementations are therefore not required to actually perform the UTC-SLS slew or to otherwise be aware of leap seconds. JSR-310 does, however, require that implementations must document the approach they use when defining a clock representing the current instant. See Clock for details on the available clocks.

Sign up to request clarification or add additional context in comments.

5 Comments

"60 seconds isn't a valid time" - well, that all depends on your view of leap seconds. But I don't think this particular date/time is valid even if you include leap seconds, as that wasn't a time that a leap second was observed.
i get ya...but it seems like having 60Z is valid in the IISO 8601, and thus javax.xml.bind.DatatypeConverter works and doesn't throw an exception...and automatically increments to 7:00Z.
There's no need to use a specific DateTimeFormatter for this pattern - Instant.parse is fine for ISO-8601 values with a "Z" zone offset.
@JonSkeet: Point taken. Thanks.
DatatypeConverter automatically increments the date...to 2017-12-04T08:07:00Z...ok. i am processing KML so can't adjust the data so will have to continue to use this class.
3

The accepted answer is fine. I just have two things to add:

  1. You can parse the string with the invalid second value of 60 by using ResolverStyle.LENIENT.
  2. Since Jon Skeet in a comment mentioned a possible leap second: It’s not a valid leap second. java.time does support the parsing of a (valid) leap second.

Parsing your string

    DateTimeFormatter lenientFormatter
            = DateTimeFormatter.ISO_OFFSET_DATE_TIME
                    .withResolverStyle(ResolverStyle.LENIENT);
    String dateString = "2018-12-04T08:06:60Z";
    Instant myInstant = lenientFormatter.parse(dateString, Instant::from);
    System.out.println(myInstant);

Output:

2018-12-04T08:07:00Z

So the overflowing second value of 60 has been rolled into a full minute.

By the way, javax.xml.bind.DatatypeConverter.parseDateTime parses into a Calendar (not a Date), which is how the returned object can in fact hold a second value of 60. It seems that it generally accepts a second value of 60, but throws an exception on 61.

Parsing a valid leap second

This does in no way answer your question, but I thought that it might be useful for future readers. A leap second is always the last second of the day, so 23:59:60. An Instant cannot hold this value, but you can query whether one was parsed. It’s supported via DateTimeFormatterBuilder.appendInstant(), and DateTimeFormatter.parsedLeapSecond().

    DateTimeFormatter leapSecondFormatter = new DateTimeFormatterBuilder()
            .appendInstant()
            .toFormatter();
    Instant myInstant
            = leapSecondFormatter.parse("2018-12-04T23:59:60Z", Instant::from);
    System.out.println(myInstant);

    TemporalAccessor parsed = leapSecondFormatter.parse("2018-12-04T23:59:60Z");
    System.out.println("Instant: " + parsed.query(Instant::from));
    System.out.println("Was a leap second parsed? "
            + parsed.query(DateTimeFormatter.parsedLeapSecond()));

Output:

2018-12-04T23:59:59Z
Instant: 2018-12-04T23:59:59Z
Was a leap second parsed? true

I don’t know why it had to be this complicated, but it works.

Link: Documentation of DateTimeFormatter.parsedLeapSecond

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.