util manejo horas fechas example ejemplo java java-8 java-time

fechas - manejo de horas en java



Problemas de Java SE 8 TemporalAccessor.from cuando se utiliza con un objeto java.time.Instant (3)

La clase Instant no tiene una zona horaria. Se imprime como si estuviera en la zona UTC porque debe imprimirse en alguna zona (no querría que se imprima en tic, ¿verdad?), Pero no es su zona horaria, como se muestra en el siguiente ejemplo:

Instant instant=Instant.now(); System.out.println(instant);//prints 2014-05-14T06:18:48.649Z System.out.println(instant.atZone(ZoneId.of("UTC")));//prints 2014-05-14T06:18:48.649Z[UTC]

Las firmas de ZoneOffset.from y OffsetDateTime.from aceptan cualquier objeto temporal, pero fallan para algunos tipos. java.time no parece tener una interfaz para temporarios que tengan una zona horaria o un desplazamiento. Dicha interfaz podría haber declarado getOffset y getZone . Como no tenemos dicha interfaz, estos métodos se declaran por separado en varios lugares.

Si tuvieras un ZonedDateTime , podrías llamarlo getOffset . Si tuviera un OffsetDateTime , también podría llamar a offset, pero estos son dos métodos getOffset diferentes, ya que las dos clases no obtienen ese método de una interfaz común. Eso significa que si tienes un objeto Temporal que podría ser cualquiera, deberías probar si es una instanceof ambos para obtener el desplazamiento.

OffsetDateTime.from simplifica el proceso al hacerlo por usted, pero como tampoco puede confiar en una interfaz común, tiene que aceptar cualquier objeto Temporal y lanzar una excepción para aquellos que no tienen un desplazamiento.

java.time tiene una clase Instant que encapsula una posición (o "momento") en la línea de tiempo. Si bien entiendo que este es un valor de segundos / nanosegundos, no está relacionado directamente con las zonas horarias ni con las compensaciones de tiempo, su toString devuelve una fecha y una hora con formato de fecha / hora UTC, por ejemplo, 2014-05-13T20: 05: 08.556Z. También anInstant.atZone(zone) y anInstant.atOffset(offset) producen un valor que es consistente con tratar al Instant como si tuviera una zona horaria UTC / offset ''cero'' implícita.

Por lo tanto, habría esperado:

  • ZoneOffset.from(anInstant) para producir un ZoneOffset ''cero''
  • OffsetDateTime.from(anInstant) para producir una fecha / hora con un desplazamiento ''cero''
  • ZoneId.from(anInstant) (probablemente) para producir un ZoneId UTC
  • ZonedDateTime.from(anInstant) (probablemente) para producir un ZonedDateTime con un ZoneId UTC

La documentación para ZonedDateTime.from , mientras lo leo, parece respaldar esto.

De hecho, ZoneOffset.from(anInstant) falla con DateTimeException , y supongo que por esa razón, OffsetDateTime.from(anInstant) también falla, al igual que los otros dos.

Es este el comportamiento esperado?


Solo un ejemplo de conversiones de wrt, creo que algunas personas obtendrán menos de la excepción

(java.time.DateTimeException: no se puede obtener LocalDateTime de TemporalAccessor: 2014-10-24T18: 22: 09.800Z de tipo java.time.Instant)

si lo intentan

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant());

para resolver el problema, por favor pase en la zona -

LocalDateTime localDateTime = LocalDateTime.from(new Date().toInstant().atZone(ZoneId.of("UTC")));


Respuesta corta:

Los diseñadores de JSR-310 no quieren que las personas realicen conversiones entre el tiempo de máquina y el tiempo de persona a través de estática desde (): métodos en tipos como ZoneId , ZoneOffset , OffsetDateTime , ZonedDateTime , etc. Esto se especifica explícitamente si estudias el javadoc con cuidado. En su lugar, utilice:

OffsetDateTime#toInstant():Instant ZonedDateTime#toInstant():Instant Instant#atOffset(ZoneOffset):OffsetDateTime Instant#atZone(ZoneId):ZonedDateTime

El problema con los métodos estáticos de () es que, de lo contrario, las personas pueden realizar conversiones entre un Instant y, por ejemplo, LocalDateTime sin pensar en la zona horaria.

Respuesta larga:

Ya sea para considerar un Instant como contador o como una tupla de campo, la respuesta dada por el equipo JSR-310 fue una separación estricta entre el llamado tiempo de máquina y el tiempo humano. De hecho, tienen la intención de tener una separación estricta - ver sus guidelines . Así que finalmente quieren que Instant se interprete como un contador de tiempo de máquina. Así que, intencionalmente, tienen un diseño en el que no se puede solicitar un Instant para campos como el año, la hora, etc.

Pero de hecho, el equipo JSR-310 no es consistente en absoluto. Han implementado el método Instant.toString() como una vista de tupla de campo que incluye el año, ..., hora, ... y el símbolo de desplazamiento Z (para zona horaria UTC) (nota al pie de página: fuera de JSR-310, esto es bastante común para tener un aspecto de campo en dichos tiempos de máquina (consulte, por ejemplo, en Wikipedia o en otros sitios sobre TAI y UTC ). Una vez que el líder de especificaciones, S. Colebourne dijo en un comentario sobre un threeten-github-issue :

"Si fuéramos una línea muy dura, el toString of a Instant sería simplemente el número de segundos desde 1970-01-01Z. Elegimos no hacer eso y generamos un toString más amigable para ayudar a los desarrolladores, pero no cambia. el hecho básico de que un Instant es solo un conteo de segundos, y no se puede convertir en un año / mes / día sin una zona horaria de algún tipo ".

A la gente le puede gustar esta decisión de diseño o no (como yo), pero la consecuencia es que no puede pedir un Instant por año, ..., hora, ... y compensación. Véase también la documentación de los campos soportados:

NANO_OF_SECOND MICRO_OF_SECOND MILLI_OF_SECOND INSTANT_SECONDS

Aquí es interesante lo que falta, sobre todo falta un campo relacionado con la zona. Como razón, a menudo escuchamos la afirmación de que los objetos como Instant o java.util.Date no tienen zona horaria. En mi opinión, esta es una visión demasiado simplista. Si bien es cierto que estos objetos no tienen un estado de zona horaria internamente (y tampoco es necesario tener ese valor interno), estos objetos DEBEN estar relacionados con la zona horaria UTC porque esta es la base de todos los cálculos de desplazamiento de la zona horaria y la conversión a tipos locales. . Entonces, la respuesta correcta sería: Un Instant es un contador de máquina que cuenta los segundos y nanosegundos desde la época de UNIX en la zona horaria UTC (por especificación). La última parte, la relación con la zona UTC, no está bien especificada por el equipo JSR-310, pero no pueden negarlo. Los diseñadores quieren suprimir el aspecto de la zona horaria de Instant porque parece estar relacionado con el tiempo humano. Sin embargo, no pueden abolirlo por completo porque es una parte fundamental de cualquier cálculo de compensación interna. Así que su observación con respecto a

"También un Instant.atZone(zone) y un Instant.atOffset(offset) producen un valor que es consistente con tratar al Instant como si tuviera una zona horaria UTC / offset ''cero'' implícita".

es correcto.

Si bien puede ser muy intuitivo que ZoneOffset.from(anInstant) pueda producir ZoneOffset.UTC , lanza una excepción porque su método desde () busca un OFFSET_SECONDS-field no existente. Los diseñadores de JSR-310 han decidido hacer eso en la especificación por la misma razón, es decir, hacer que la gente piense que un Instant tiene nada que ver con la zona horaria UTC, es decir, "no tiene zona horaria" (pero internamente deben aceptar este hecho básico). en todos los cálculos internos!).

Por la misma razón, OffsetDateTime.from(anInstant) y ZoneId.from(anInstant) fallan.

Acerca de ZonedDateTime.from(anInstant) ZonedDateTime.from :

"La conversión primero obtendrá un ZoneId del objeto temporal, volviendo a un ZoneOffset si es necesario. Luego intentará obtener un Instant, volviendo a un LocalDateTime si es necesario. El resultado será la combinación de ZoneId o ZoneOffset con Instant o LocalDateTime ".

Por lo tanto, esta conversión volverá a fallar debido a los mismos motivos, ya que ni ZoneId ni ZoneOffset se pueden obtener de un Instant . El mensaje de excepción se lee como:

"No se puede obtener ZoneId de TemporalAccessor: 1970-01-01T00: 00: 00Z de tipo java.time.Instant"

Finalmente, vemos que todos los métodos estáticos de () sufren por no poder realizar una conversión entre el tiempo humano y el tiempo de la máquina, incluso si esto parece intuitivo. En algunos casos, es cuestionable una conversión entre, digamos, LocalDate e Instant . Este comportamiento está especificado, pero predigo que su pregunta no es la última de este tipo y muchos usuarios seguirán confundidos.

El verdadero problema de diseño en mi opinión es que:

a) No debe haber una separación marcada entre el tiempo humano y el tiempo de la máquina. Los objetos temporales como Instant deberían comportarse mejor como ambos. Una analogía en la mecánica cuántica: puedes ver un electrón como una partícula y una onda.

b) Todos los métodos estáticos de () son demasiado públicos. En mi opinión, es demasiado fácil acceder a él y debería haber sido mejor eliminado de la API pública o usar argumentos más específicos que TemporalAccessor . La debilidad de estos métodos es que las personas pueden olvidarse de pensar en zonas horarias relacionadas en tales conversiones porque inician la consulta con un tipo local. Considere por ejemplo: LocalDate.from(anInstant) (en qué zona horaria ???). Sin embargo, si solicita directamente a un Instant su fecha como instant.getDate() , personalmente consideraría la fecha en la zona horaria UTC como una respuesta válida porque aquí la consulta comienza desde una perspectiva UTC.

c) En conclusión: comparto absolutamente con el equipo JSR-310 la buena idea de evitar conversiones entre tipos locales y tipos globales como Instant sin especificar una zona horaria. Solo difiero en lo que respecta al diseño de la API para evitar que los usuarios realicen una conversión sin huso horario . Mi forma preferida hubiera sido restringir los métodos de (), en lugar de decir que los tipos globales no deberían tener ninguna relación con los formatos de hora humana como fecha de calendario o tiempo de pared o desplazamiento de zona horaria UTC.

De todos modos, este diseño (no consecuente) de separación entre el tiempo de la máquina y el tiempo humano ahora está grabado en piedra debido a la preservación de la compatibilidad con versiones anteriores, y todos los que quieran usar el nuevo java.time-API deben vivir con él.

Lo siento por una respuesta larga, pero es bastante difícil explicar el diseño elegido de JSR-310.