Dlaczego ZoneOffset.UTC! = ZoneId.of („UTC”)?

127

Dlaczego

ZonedDateTime now = ZonedDateTime.now();
System.out.println(now.withZoneSameInstant(ZoneOffset.UTC)
        .equals(now.withZoneSameInstant(ZoneId.of("UTC"))));

wydrukować false?

Spodziewałbym się, że oba ZonedDateTimeprzypadki będą równe.

Johannes Flügel
źródło

Odpowiedzi:

181

Odpowiedź pochodzi z javadocZoneId (moje podkreślenie) ...

ZoneId służy do identyfikowania reguł używanych do konwersji między Instant a LocalDateTime. Istnieją dwa różne rodzaje identyfikatorów:

  • Stałe przesunięcia - w pełni rozwiązane przesunięcie względem UTC / Greenwich, które używa tego samego przesunięcia dla wszystkich lokalnych dat i godzin
  • Regiony geograficzne - obszar, na którym obowiązuje określony zestaw zasad dotyczących znajdowania przesunięcia względem czasu UTC / Greenwich

Większość stałych przesunięć jest reprezentowana przez ZoneOffset. Wywołanie normalized () na dowolnym ZoneId zapewni, że stały offset ID będzie reprezentowany jako ZoneOffset.

... i z javadocZoneId#of (moje podkreślenie):

Ta metoda analizuje identyfikator, tworząc ZoneId lub ZoneOffset. ZoneOffset jest zwracana, jeśli identyfikator to „Z” lub zaczyna się od „+” lub „-” .

Argument id jest określony jako "UTC", dlatego zwróci a ZoneIdz przesunięciem, które jest również przedstawione w postaci ciągu:

System.out.println(now.withZoneSameInstant(ZoneOffset.UTC));
System.out.println(now.withZoneSameInstant(ZoneId.of("UTC")));

Wyjścia:

2017-03-10T08:06:28.045Z
2017-03-10T08:06:28.045Z[UTC]

Używając tej equalsmetody do porównania, sprawdzasz równoważność obiektów . Ze względu na opisaną różnicę wynik oceny jest false.

Gdy normalized()metoda jest używana zgodnie z propozycją w dokumentacji, porównanie przy użyciu equalszwróci true, podobnie jak normalized()zwróci odpowiedni ZoneOffset:

Normalizuje identyfikator strefy czasowej, zwracając ZoneOffset, jeśli to możliwe.

now.withZoneSameInstant(ZoneOffset.UTC)
    .equals(now.withZoneSameInstant(ZoneId.of("UTC").normalized())); // true

Jak stwierdza dokumentacja, jeśli użyjesz "Z"lub "+0"jako identyfikatora wejściowego, ofzwróci ZoneOffsetbezpośrednio i nie ma potrzeby dzwonienia normalized():

now.withZoneSameInstant(ZoneOffset.UTC).equals(now.withZoneSameInstant(ZoneId.of("Z"))); //true
now.withZoneSameInstant(ZoneOffset.UTC).equals(now.withZoneSameInstant(ZoneId.of("+0"))); //true

Aby sprawdzić, czy przechowują tę samą datę i godzinę , możesz isEqualzamiast tego użyć metody:

now.withZoneSameInstant(ZoneOffset.UTC)
    .isEqual(now.withZoneSameInstant(ZoneId.of("UTC"))); // true

Próba

System.out.println("equals - ZoneId.of(\"UTC\"): " + nowZoneOffset
        .equals(now.withZoneSameInstant(ZoneId.of("UTC"))));
System.out.println("equals - ZoneId.of(\"UTC\").normalized(): " + nowZoneOffset
        .equals(now.withZoneSameInstant(ZoneId.of("UTC").normalized())));
System.out.println("equals - ZoneId.of(\"Z\"): " + nowZoneOffset
        .equals(now.withZoneSameInstant(ZoneId.of("Z"))));
System.out.println("equals - ZoneId.of(\"+0\"): " + nowZoneOffset
        .equals(now.withZoneSameInstant(ZoneId.of("+0"))));
System.out.println("isEqual - ZoneId.of(\"UTC\"): "+ nowZoneOffset
        .isEqual(now.withZoneSameInstant(ZoneId.of("UTC"))));

Wynik:

equals - ZoneId.of("UTC"): false
equals - ZoneId.of("UTC").normalized(): true
equals - ZoneId.of("Z"): true
equals - ZoneId.of("+0"): true
isEqual - ZoneId.of("UTC"): true
DVarga
źródło
4
Dokumenty mówią również „Jeśli identyfikator strefy jest równy„ GMT ”,„ UTC ”lub„ UT ”, wynikiem jest ZoneId z tym samym identyfikatorem i regułami równoważnymi z ZoneOffset.UTC”. Ten sam identyfikator i zasady, ale inne zachowanie. ZoneId.of("Z")daje, ZoneOffset.UTCale ZoneId.of("UTC")daje ZoneId(to nie jest ZoneOffset.UTC). To API jest co najmniej nieintuicyjne.
Adam Millerchip