Почему ZoneOffset.UTC! = ZoneId.of («UTC»)?

127

Почему

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

распечатать false?

Я ожидал, что оба ZonedDateTimeэкземпляра будут равны.

Йоханнес Флюгель
источник

Ответы:

181

Ответ исходит из javadocZoneId (выделено мной) ...

ZoneId используется для идентификации правил, используемых для преобразования между Instant и LocalDateTime. Есть два различных типа идентификаторов:

  • Фиксированные смещения - полностью разрешенное смещение от UTC / Гринвича, которое использует то же смещение для всех локальных дат и времени.
  • Географические регионы - регион, в котором применяется определенный набор правил для определения смещения от UTC / Гринвича.

Большинство фиксированных смещений представлено ZoneOffset. Вызов normalized () для любого ZoneId гарантирует, что фиксированный идентификатор смещения будет представлен как ZoneOffset.

... и из javadocZoneId#of (выделено мной):

Этот метод анализирует идентификатор, создавая ZoneId или ZoneOffset. ZoneOffset возвращается, если идентификатор равен «Z» или начинается с «+» или «-» .

Идентификатор аргумента указан как "UTC", поэтому он вернет a ZoneIdсо смещением, которое также представлено в строковой форме:

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

Выходы:

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

Используя этот equalsметод для сравнения, вы проверяете эквивалентность объектов . Из-за описанной разницы результат оценки равен false.

Когда normalized()метод используется так, как предлагается в документации, equalsбудет возвращено использование сравнения true, так же как normalized()и соответствующие ZoneOffset:

Нормализует идентификатор часового пояса, возвращая ZoneOffset, где это возможно.

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

Как указано в документации, если вы используете "Z"или в "+0"качестве входного идентификатора, ofвернет ZoneOffsetнапрямую, и нет необходимости вызывать normalized():

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

Чтобы проверить, сохраняют ли они одинаковую дату и время , вы можете isEqualвместо этого использовать метод:

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

Образец

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"))));

Вывод:

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
источник
4
В документации также говорится: «Если идентификатор зоны равен« GMT »,« UTC »или« UT », то результатом является ZoneId с тем же идентификатором и правилами, эквивалентными ZoneOffset.UTC». Тот же идентификатор и правила, но другое поведение. ZoneId.of("Z")дает вам, ZoneOffset.UTCно ZoneId.of("UTC")дает вам ZoneId(это не так ZoneOffset.UTC). Этот API, мягко говоря, не интуитивно понятен.
Адам Миллерчип,