Reputation: 13663
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 Instant
s and ZonedDateTime
s.
Looking at the API documentation for Instant
and ZonedDateTime
, none of this is the case. I can compare Instant
s with Instant
s and ZonedDateTime
s with ZonedDateTime
s, but the two classes seem to be incompatible. What's more, third-party code like ThreeTen-Extra's Interval
seem to work exclusively with Instant
s.
Is there a reason why Instant
and ZonedDateTime
are not meant to be mixed?
Upvotes: 13
Views: 4034
Reputation: 1642
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 TemporalUnit
s 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.
Upvotes: 1
Reputation: 18163
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.
Upvotes: 7
Reputation: 63405
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);
Upvotes: 10