Compatibility between Instant and ZonedDateTime
Asked Answered
S

3

13

It is my understanding that a ZonedDateTime is really an enhanced version of an Instant. It has all the data an Instant has (precise value along UTC timeline), plus time zone information. So my naïve assumption was that a ZonedDateTime is-an Instant and that any method taking an Instant will happily take a ZonedDateTime instead. Furthermore, I expected isBefore(), isAfter() etc. to work seamlessly between Instants and ZonedDateTimes.

Looking at the API documentation for Instant and ZonedDateTime, none of this is the case. I can compare Instants with Instants and ZonedDateTimes with ZonedDateTimes, but the two classes seem to be incompatible. What's more, third-party code like ThreeTen-Extra's Interval seem to work exclusively with Instants.

Is there a reason why Instant and ZonedDateTime are not meant to be mixed?

Stirling answered 10/11, 2016 at 10:2 Comment(2)
Well, the documentation clearly says that it should be viewed as a LocalDateTime coupled with a zone, note as an Instant coupled with a zone. It has all the date-time fields, not an offset from a point in UTC. Why do you expect it to behave other than its documentation says?Paramagnetism
You can convert a ZonedDateTime to an Instant using .toInstant(), but to convert from an Instant to a ZonedDateTime, you must use ZonedDateTime.ofInstant and specify a ZoneId.Bulb
A
10

Instant and ZonedDateTime have different state - Instant is just a number of nanoseconds from the epoch, while ZonedDateTime consists of a LocalDateTime, ZoneId and ZoneOffset. As such, the two classes can be converted to/from each other, but are not the same (and you lose information when converting ZonedDateTime to Instant).

The behaviour of isBefore() and isAfter() matches exactly between the two however. Whereas the implementation of Comparable matches the recommended behaviour, which is that "It is strongly recommended (though not required) that natural orderings be consistent with equals." ie. compareTo() takes account of the local date-time, time-zone and offset, whereas isBefore() and isAfter() only consider the instant.

Writing a comparator to compare an Instant and a ZonedDateTime is relatively simple:

Comparator<TemporalAccessor> comparator =
    (a, b) -> Instant.from(a).compareTo(Instant.from(b));

which can also be written as:

Comparator<TemporalAccessor> comparator = Comparator.comparing(Instant::from);
Adulterine answered 10/11, 2016 at 10:44 Comment(2)
If Instant is just a number of nanoseconds from the epoch why does System.out.println(Instant.now()); output 2018-11-29T03:58:06.573Z? It seems like an Instance is a number of nanoseconds together with some default Time zone to me, would you agree?Airlee
A toString implementation that just prints out a number of nanoseconds is not very helpful. So, the toString prints out the time in UTC instead.Adulterine
V
7

Because the translation is not injective. Take Sunday, October 30th 2016 2:15 AM in Germany/Munich for example: Which Instant does this date/time represent? This is not independently answerable without some assumptions because you don't know if this time should be translated into the Instant before or after the offset for daylight saving time (DST) should be applied. Or Sunday, March 27th 2016 2:15 AM in Germany/Munich: This date/time combination should not exist, as the clock should be set to 3 AM when reaching 2 AM.

Without DST the three possible cases for translating a LocalDateTime into an Instant (exact match, summertime-gap, wintertime-overlap) would be reduced to one and the conversion would be injective, AFAIK.

Edit: "Hands on" this problem, when displaying date/time in our JSF based application, we always pass the offset calculated accordingly to the current state of the DST into the formatter.

Vinegarish answered 10/11, 2016 at 10:17 Comment(2)
The ZonedDateTime docs state: "This class stores ... a zone offset used to handle ambiguous local date-times." Doesn't that mean that there is an unambiguous mapping from ZonedDateTime to Instant?Stirling
I think this answer has mixed up LocalDateTime with ZonedDateTime. The orignal question is about ZonedDateTime, but this answer is about LocalDateTime.Antrum
A
1

Instant and ZonedDateTime have different rules for arithmetic. That is the only reason I can see for avoiding polymorphism for these two types. For example, adding n days to an Instant means adding n times 86400 seconds to it. Always. That would not be the case if ZonedDateTime was a sub-class of Instant, because of rules for daylight savings time.

My understanding is that an Instant is similar to a ZonedDateTime with time zone UTC, so you could argue that Instant is-a ZonedDateTime instead. However, this would make Instant more complicated than it is today, since it would allow arithmetic with TemporalUnits such as MONTHS. Arithmetic with months are time zone dependent, so Instant forces the programmer to select an explicit time zone before doing it.

Anyway, it is unfortunate that Instant and ZonedDateTime aren't more compatible, since they both represent time unambiguously. It looks like they lack a common base class or interface without any methods for arithmetic.

Antrum answered 16/7, 2019 at 7:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.