I'm parsing the same date in two slightly different formats and I encountered an error that I don't understand.
Parsing a string in the standard ISO format is successful:
String s = "2018-04-17T22:57:29";
LocalDateTime date = LocalDateTime.parse(s, DateTimeFormatter.ISO_DATE_TIME); // OK
However when I suffix a "Z"
and use ISO_INSTANT
thus:
s = "2018-04-17T22:57:29Z";
date = LocalDateTime.parse(s, DateTimeFormatter.ISO_INSTANT); // Fails
I get the following exception:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2018-04-17T22:57:29Z' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
at java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:1920)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1855)
at java.time.LocalDateTime.parse(LocalDateTime.java:492)
at iplus.fwk.manifest.Test.main(Test.java:37)
Caused by: java.time.DateTimeException: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
at java.time.LocalDateTime.from(LocalDateTime.java:461)
at java.time.format.Parsed.query(Parsed.java:226)
at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1851)
... 2 more
Caused by: java.time.DateTimeException: Unable to obtain LocalDate from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
at java.time.LocalDate.from(LocalDate.java:368)
at java.time.LocalDateTime.from(LocalDateTime.java:456)
... 4 more
My reading of the definition of ISO_INSTANT suggests that the second parse should succeed. What am I doing wrong?
I believe that the sound way of thinking of it is this: Conceptually an instant is not a date and time. It’s just that, an instant. It doesn’t have a time zone or offset and therefore cannot have date and time of day. Since a LocalDateTime
needs date and time of day, parsing as an instant does not give you what you need.
Yes I know: The implementation of Instant
uses time since the epoch, and the by far most common definition of the epoch uses UTC. I also know that the string you were parsing holds a date and a time of day, and that both DateTimeFormatter.ISO_INSTANT
and Instant.toString
produce similar strings. This is interface, though, and doesn’t tell you what the concept of an instant is.
My reading of the definition of ISO_INSTANT suggests that the second parse should succeed.
I understand that the definition could be read this way. I think that this is worth noting, though:
…the instant is converted from ChronoField.INSTANT_SECONDS and ChronoField.NANO_OF_SECOND…
While nano of second is shared with other date-time types like LocalDateTime
, the two fields mentioned are not enough to get you the data for a LocalDateTime
. So this is where it breaks for you. To get a LocalDateTime
we would need to assume a time zone or offset. Even though the quote goes on:
…using the UTC offset.
— this only means that the UTC offset is used for calculating the instant seconds and nanos, not in the further processing.
We may also try to study your error message a bit closer:
Exception in thread "main" java.time.format.DateTimeParseException: Text '2018-04-17T22:57:29Z' could not be parsed: Unable to obtain LocalDateTime from TemporalAccessor: {MicroOfSecond=0, NanoOfSecond=0, MilliOfSecond=0, InstantSeconds=1524005849},ISO of type java.time.format.Parsed
This also says that all we got from parsing was instant seconds (that is seconds since the epoch) and fractions of seconds (and the ISO chronology). And that this was not enough to obtain a LocalDateTime
.
You’re not the first (nor the last) to be surprised. The behaviour you observed is by design, though.
A fix
The definition of ISO_INSTANT
also mentions:
The
ISO_OFFSET_DATE_TIME
…
So this works:
String s = "2018-04-17T22:57:29Z";
LocalDateTime date = LocalDateTime.parse(s, DateTimeFormatter.ISO_OFFSET_DATE_TIME);
The result is 2018-04-17T22:57:29
(as expected).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With