I was comparing two dates which seem to be equal, but they contain a different name of zones: one is Etc/UTC
, another is UTC
.
According to this question: Is there a difference between the UTC and Etc/UTC time zones? - this two zones are the same. But my tests fail:
import org.junit.Test;
import java.sql.Timestamp;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import static org.junit.Assert.assertEquals;
public class TestZoneDateTime {
@Test
public void compareEtcUtcWithUtc() {
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(ZoneId.of("Etc/UTC"));
ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(ZoneId.of("UTC"));
// This is okay
assertEquals(Timestamp.from(zoneDateTimeEtcUtc.toInstant()), Timestamp.from(zoneDateTimeUtc.toInstant()));
// This one fails
assertEquals(zoneDateTimeEtcUtc,zoneDateTimeUtc);
// This fails as well (of course previous line should be commented!)
assertEquals(0, zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc));
}
}
The result:
java.lang.AssertionError:
Expected :2018-01-26T13:55:57.087Z[Etc/UTC]
Actual :2018-01-26T13:55:57.087Z[UTC]
More specifically, I would expect, that ZoneId.of("UTC")
would be equal to ZoneId.of("Etc/UTC")
, but they aren't!
As @NicolasHenneaux suggested, I should probably use compareTo(...)
method. That's good idea, but zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc)
returns -16
value, because of this implementation inside ZoneDateTime
:
cmp = getZone().getId().compareTo(other.getZone().getId());
Assertion result:
java.lang.AssertionError:
Expected :0
Actual :-16
So the problem lies somewhere in ZoneId
implementation. But I still would expect that if both zone ids are valid and both designate the same zone, then they should be equal.
My question is: is it a library bug, or I am doing something wrong?
UPDATE
Several people tried to convince me that it is a normal behaviour, and it is normal that the implementation of comparison methods uses String
id representation of the ZoneId
. In this case I should ask, why does the following test runs okay?
@Test
public void compareUtc0WithUtc() {
ZonedDateTime now = ZonedDateTime.now();
ZoneId utcZone = ZoneId.of("UTC");
ZonedDateTime zonedDateTimeUtc = now.withZoneSameInstant(utcZone);
ZoneId utc0Zone = ZoneId.of("UTC+0");
ZonedDateTime zonedDateTimeUtc0 = now.withZoneSameInstant(utc0Zone);
// This is okay
assertEquals(Timestamp.from(zonedDateTimeUtc.toInstant()), Timestamp.from(zonedDateTimeUtc0.toInstant()));
assertEquals(0, zonedDateTimeUtc.compareTo(zonedDateTimeUtc0));
assertEquals(zonedDateTimeUtc,zonedDateTimeUtc0);
}
If Etc/UTC
is the same as UTC
, then I see two options:
Zone.of(...)
is broken and should treat Etc/UTC
and UTC
as the same time zones. Otherwise I don't see why UTC+0
and UTC
work fine.
UPDATE-2 I have reported a bug, ID : 9052414. Will see what Oracle team will decide.
UPDATE-3 The bug report accepted (don't know will they close it as "won't fix" or not): https://bugs.openjdk.java.net/browse/JDK-8196398
You can convert the ZonedDateTime
objects to Instant
, as the other answers/comments already told.
ZonedDateTime::isEqual
Or you can use the isEqual
method, which compares if both ZonedDateTime
instances correspond to the same Instant
:
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(ZoneId.of("Etc/UTC"));
ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(ZoneId.of("UTC"));
Assert.assertTrue(zoneDateTimeEtcUtc.isEqual(zoneDateTimeUtc));