Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8196398

ZoneId's "Etc/UTC" and "UTC" don't return expected result for equals and compareTo

XMLWordPrintable

      FULL PRODUCT VERSION :
      java version "1.8.0_152"
      Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
      Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Darwin Kernel Version 17.2.0

      A DESCRIPTION OF THE PROBLEM :
      According to TZ specification, "Etc/UTC" represents "UTC", so these two time zones are essentially the same:

      ftp://ftp.iana.org/tz/data/etcetera

      https://en.wikipedia.org/wiki/Tz_database#Area

      So it is expectable, that the two ZoneId's created as:

              ZoneId etcUtcZone = ZoneId.of("Etc/UTC");
              ZoneId utcZone = ZoneId.of("UTC");

      will be equal in all comparison related operations.

      In fact these two objects has the same ZoneRules, what is correct. However, because ZomeId equality is based on ZoneId:

       return getId().equals(other.getId());

      and ChronoZonedDateTime.compareTo is based on comparing zone ids:

      cmp = getZone().getId().compareTo(other.getZone().getId());

      these all breaks a quite expectable logic, that these two ZonedDateTimes (zoneDateTimeEtcUtc and zoneDateTimeUtc):
              
              ZonedDateTime now = ZonedDateTime.now();
              ZoneId etcUtcZone = ZoneId.of("Etc/UTC");
              ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(etcUtcZone);
              ZoneId utcZone = ZoneId.of("UTC");
              ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(utcZone);

      Should be effectively equal:

       zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc) should be 0 and
       zoneDateTimeEtcUtc.equals(zoneDateTimeUtc) should be true.

      However they aren't due to the reasons described above.

      The technical reason for this behaviour are clear, but it seems to be bug in the design of implementation compareTo and equals methods. Otherwise this is a bug in documentation, because it is not expectable that these two dates build in the same timezone would be different.

      From the other hand when using "UTC" and "UTC+0" timezones dates become equal:

              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));
              // This one fails
              assertEquals(zonedDateTimeUtc,zonedDateTimeUtc0);

      It is clear why it is so, but the general behaviour still seems not proper.

      See also this question on StackOverflow: https://stackoverflow.com/questions/48462758/zoneddatetime-comparison-expected-etc-utc-but-was-utc


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      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();
              ZoneId etcUtcZone = ZoneId.of("Etc/UTC");
              ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(etcUtcZone);
              ZoneId utcZone = ZoneId.of("UTC");
              ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(utcZone);

              // This is okay
              assertEquals(Timestamp.from(zoneDateTimeEtcUtc.toInstant()), Timestamp.from(zoneDateTimeUtc.toInstant()));

              assertEquals(0, zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc));
              // This one fails
              assertEquals(zoneDateTimeEtcUtc, zoneDateTimeUtc);
          }
      }

      Compile and run this code.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The test should run without fails.
      ACTUAL -
      The test fails.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      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();
              ZoneId etcUtcZone = ZoneId.of("Etc/UTC");
              ZonedDateTime zoneDateTimeEtcUtc = now.withZoneSameInstant(etcUtcZone);
              ZoneId utcZone = ZoneId.of("UTC");
              ZonedDateTime zoneDateTimeUtc = now.withZoneSameInstant(utcZone);

              // This is okay
              assertEquals(Timestamp.from(zoneDateTimeEtcUtc.toInstant()), Timestamp.from(zoneDateTimeUtc.toInstant()));

              assertEquals(0, zoneDateTimeEtcUtc.compareTo(zoneDateTimeUtc));
              // This one fails
              assertEquals(zoneDateTimeEtcUtc, zoneDateTimeUtc);
          }
      }
      ---------- END SOURCE ----------

            naoto Naoto Sato
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: