¿Por qué ZoneOffset.UTC! = ZoneId.of (“UTC”)?

125

Por que

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

imprimir false?

Esperaría que ambas ZonedDateTimeinstancias fueran iguales.

Johannes Flügel
fuente

Respuestas:

180

La respuesta viene del javadoc deZoneId (énfasis mío) ...

Un ZoneId se usa para identificar las reglas que se usan para convertir entre Instant y LocalDateTime. Hay dos tipos distintos de identificación:

  • Desplazamientos fijos: un desplazamiento completamente resuelto de UTC / Greenwich, que utiliza el mismo desplazamiento para todas las fechas y horas locales
  • Regiones geográficas: un área donde se aplica un conjunto específico de reglas para encontrar el desplazamiento de UTC / Greenwich

La mayoría de las compensaciones fijas están representadas por ZoneOffset. Llamar a normalized () en cualquier ZoneId garantizará que un ID de desplazamiento fijo se represente como ZoneOffset.

... y del javadoc deZoneId#of (énfasis mío):

Este método analiza el ID que produce un ZoneId o ZoneOffset. Se devuelve un ZoneOffset si el ID es 'Z' o comienza con '+' o '-' .

El id del argumento se especifica como "UTC", por lo tanto, devolverá un ZoneIdcon un desplazamiento, que también se presenta en forma de cadena:

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

Salidas:

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

A medida que utiliza el equalsmétodo de comparación, comprueba la equivalencia de objetos . Debido a la diferencia descrita, el resultado de la evaluación es false.

Cuando normalized()se utilice el método propuesto en la documentación, equalsse devolverá la comparación utilizando true, ya normalized()que devolverá el correspondiente ZoneOffset:

Normaliza el ID de la zona horaria, devolviendo un ZoneOffset siempre que sea posible.

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

Como indica la documentación, si usa "Z"o "+0"como ID de entrada, ofdevolverá el ZoneOffsetdirectamente y no es necesario llamar normalized():

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

Para verificar si almacenan la misma fecha y hora , puede usar el isEqualmétodo en su lugar:

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

Muestra

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

Salida:

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
fuente
4
Los documentos también dicen "Si el ID de zona es igual a 'GMT', 'UTC' o 'UT', el resultado es un ZoneId con el mismo ID y reglas equivalentes a ZoneOffset.UTC". Misma identificación y reglas, pero comportamiento diferente. ZoneId.of("Z")te da ZoneOffset.UTCpero te ZoneId.of("UTC")da un ZoneId(que no es ZoneOffset.UTC). Esta API no es intuitiva, por decir lo menos.
Adam Millerchip