¿Cómo puede una biblioteca de 1 año (java) realizar correctamente un formato de hora UTC, considerando un segundo salto recientemente introducido?

Una marca de tiempo expresada en milisegundos desde 1.1.1970 UTC es una forma común de almacenar marcas de tiempo, por ejemplo, en Java.

p.ej:

long timestampUtc = System.currentTimeMillis(); 

Tal marca de tiempo puede formarse en formato de tiempo de lectura humana, por ejemplo, utilizando este código

  SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); df.setTimeZone(TimeZone.getTimeZone("UTC")); String humanTimeUtc = df.format(new Date(timestampUtc)); System.out.println(humanTimeUtc); 

que da salida: 2014-02-14 14:58:05

Ahora imagine que hoy, a medianoche, la administración del tiempo introduce un nuevo salto UTC segundo. Si ejecuto el código arriba de mañana, el JRE de Java en mi sistema no puede saber la segunda introducción del salto, y daría formato incorrecto a la hora (en un segundo).

¿Es mi asunción correcta?
¿Cómo formatear correctamente la hora (por ejemplo, en un archivo de registro) en sistemas que no siempre pueden usar un JRE actualizado?

Información de fondo:
Esto se usa en un dispositivo integrado, que sincroniza el reloj de su sistema a través de GPS, con el número de segundos de salto de GPS compensado a UTC.

Java y la “época” de Unix (número de segundos desde el 1 de enero de 1970, 00:00:00 UTC) ignoran por completo los segundos de salto . Ambos suponen que cada día (medido en UTC) ha tenido exactamente 86400 segundos. Un simple bloque de código para verificar:

  Calendar c = Calendar.getInstance(); c.setTimeZone(TimeZone.getTimeZone("UTC")); c.set(2014, 0, 1, 0, 0, 0); c.set(Calendar.MILLISECOND, 0); System.out.println(c.getTimeInMillis()); 

Verá que la cantidad de segundos desde 1/1/1970 a 1/1/2014 es un múltiplo exacto de 86400 (en realidad es exactamente 44 años * 365.25 días / año * 86400 segundos / día); no debería ser, porque se han introducido 25 segundos de salto en ese intervalo.

Si necesita tomar en cuenta los segundos de salto, necesita encontrar una biblioteca que lo haga, o crear su propio ajuste.

El segundo manejo es difícil y Java no es particularmente malo en este aspecto.

La escala de tiempo NTP y Leap Seconds explican algunas de las rarezas:

La inserción de segundos de salto en UTC y posteriormente en NTP y POSIX afecta el reloj del sistema y, por lo tanto, la conversión entre la hora del reloj del sistema y la hora civil convencional en horas, minutos y segundos. Sin embargo, dado que la única memoria institucional disponible para determinar la conversión son los servicios de transmisión nacionales de UTC, la conversión se restablece en efecto a UTC a medida que se recibe cada código de tiempo de transmisión. Por lo tanto, cuando se inserta un segundo de salto en UTC y posteriormente en NTP o POSIX, se pierde el conocimiento de todos los segundos de salto anteriores .

Otra forma de describir esto es decir que hay tantas escalas de tiempo NTP o POSIX como segundos históricos de salto. En efecto, una nueva escala de tiempo se restablece después de cada nuevo salto de segundo. Por lo tanto, todos los segundos anteriores, sin mencionar el origen aparente de la escala de tiempo, retroceden un segundo a medida que se establece cada nueva escala de tiempo. Por ejemplo, si se usara un reloj sincronizado con UTC en 2005 para establecer la época UTC de un evento que ocurrió a principios de 1972 sin corrección, el evento aparecerá 22 segundos tarde. Como resultado, para la determinación más precisa de la época en relación con el calendario gregoriano histórico y la escala de tiempo UTC, el usuario debe sustraer de la época NTP o POSIX aparente el desplazamiento relevante proporcionado por el IERS. Esta es una característica de casi todos los mecanismos actuales de distribución de tiempo del día.

Los segundos de salto casi nunca son relevantes a menos que estés haciendo cálculos astronómicos u otra cosa que dependa de la posición de la Tierra y el Sol.

Por lo general, las personas que piensan que necesitan segundos de salto realmente necesitan

  1. una forma estable (para el futuro previsible) de convertir una representación de tupla como (año, mes, día, …) a una representación escalar como millis-since-epoch o
  2. lidiar con una representación en lugar de mezclar y combinar.

Yo prefiero (2). La representación escalar es mejor para los cálculos científicos, y la representación de la tupla es mejor para aplicaciones empresariales y casuales.

La respuesta de dcsohl y la respuesta de Mike Samuel son correctas.

Los otros comentarios sobre la base de datos de tiempo de Java que se está actualizando son incorrectos. Sí, la base de datos se actualiza, pero no para segundos de salto. Los segundos de salto son ignorados por completo por Java, por Joda-Time y por otros sistemas de cronometraje orientados a Unix.

Tu pregunta asume un problema donde no existe ninguno.

Relojes de deriva

El reloj físico en casi todas las computadoras no es muy preciso. La deriva de un segundo o más por mes es común. Es por eso que prácticamente todos los sistemas operativos están predeterminados para conectarse a servidores de tiempo localmente o por Internet. Entonces, en términos prácticos, su computadora está modificando regularmente su propio tiempo actual. Debido a esto, es posible que ya encuentre anomalías en las secuencias de tiempo registradas en sus registros si lo observa detenidamente.

Insertar un segundo de salto tiene el mismo efecto que la imprecisión de su reloj. Su computadora está apagada por un segundo, y pronto será corregida por un registro con un servidor de tiempo.

Ignorando Leap Second

En términos de sus datos, tanto la clase java.util.Date incluida como el reemplazo popular, Joda-Time , ignoran el segundo salto. Entonces, piense en un segundo de salto como si se extendiera ese 59.º segundo en la última hora del día del evento de un segundo de salto. En términos de nuestro calendario y relojes de llamadas, no pasó nada. Para todas las aplicaciones relacionadas con el negocio y los propósitos más prácticos de la mayoría de los progtwigs, ignorar los segundos de salto no tiene efectos perjudiciales.

Sí, técnicamente hablando, los milisegundos desde la época utilizados por java.util.Date y Joda-Time son incorrectos. En este momento se informa 1392442622998 desde el comienzo del año 1970, mientras que en realidad se han insertado 25 saltos de segundo desde 1972, ese número “debería” ser 1,392,442,647,998 (25,000 más). Sí, el cálculo del tiempo transcurrido entre dos puntos de tiempo durante años será corto en unos pocos segundos. Pero para la mayoría de los propósitos, ¿a quién le importa? Hemos ajustado nuestros calendarios para que actúen como si no hubiera un segundo extra.

Si tienes una mente orientada a la precisión como lo hago yo, toma un poco de tiempo entender el hecho de que ignorar los segundos del salto no tiene ningún efecto práctico en el seguimiento del calendario. El problema básico:

  • La hora del día es la forma en que leemos el giro del planeta (que se está desacelerando ), mientras que la fecha / calendario es la forma en que leemos la órbita del planeta alrededor del Sol.
  • Esos dos, hora del día y fecha / calendario , no tienen nada que ver el uno con el otro. Son realidades independientes . Pero nosotros, los humanos, los mezclamos juntos para dar sentido al tiempo de la misma manera en que cocinamos juntos la mantequilla de maní y el plátano para hacer un sándwich. Y al igual que podríamos rociar miel entre la pb y el plátano para juntarlos, los segundos de salto traen el reloj y el calendario juntos de una manera unificada para nuestro consumo mental.

Un problema posible

El único problema práctico real es que, según se informa, algunos sistemas generan un 60º segundo real, un tiempo de 23:59:60. Ese valor de tiempo causa esgulps en algunos progtwigs escritos, aunque ignoran los segundos de salto. Dicho software supone incorrectamente que el valor es imposible y puede generar errores o fallar de otro modo. El software debidamente informado debe saber que (a) podemos tener segundos adicionales, por lo que 23:59:60 y 23:59:61 son valores legales, y (b) el salto puede ser negativo. (Hasta ahora, solo hemos tenido segundos positivos de un solo salto, recuerdo haber leído que es posible más de uno, pero no se espera. No puedo encontrar una fuente al respecto). Recuerde, este problema solo ocurre si un sistema operativo o proveedor de valores de tiempo en realidad está rastreando esos segundos de salto, pocos lo hacen, por lo que nunca ven un segundo número 60.

Más información

La página de Wikipedia tiene más información sobre Leap Second.

Mi nueva biblioteca Time4J es capaz de manejar segundos de salto, por lo que esta es una de las muchas características únicas de esta biblioteca. No conozco ninguna otra biblioteca que pueda hacer el formateo de segundos de salto. Concretamente sobre su pregunta en detalle:

Tu código de ejemplo usando Java estándar

 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); df.setTimeZone(TimeZone.getTimeZone("UTC")); String humanTimeUtc = df.format(new Date(timestampUtc)); System.out.println(humanTimeUtc); 

Se ve en Time4J como:

 ChronoFormatter formatter = ChronoFormatter.setUp(Moment.class, Locale.US) .addPattern("uuuu-MM-dd HH:mm:ss", PatternType.CLDR) .build(); Moment timestampUTC = SystemClock.INSTANCE.currentTime(); System.out.println(formatter.format(timestampUTC)); // output: 2014-02-20 14:16:25 

a) La fuente de tiempo SystemClock se basa en System.currentTimeMillis() . Esta fuente nunca cuenta los segundos de salto y tampoco puede producir una marca de tiempo de segundo salto, suponiendo que el sistema operativo subyacente no se dé cuenta. Por lo tanto, la salida en este ejemplo nunca mostrará un valor de segundo de salto de 60.

b) Internamente, un objeto de tipo Moment contiene una marca de tiempo posix Y un bit de segundo paso en su estado. Por lo tanto, con la ayuda de una tabla externa de Leapsecond (que en realidad se guarda en un archivo pequeño en classpath), cada Moment se mostrará correctamente al mismo tiempo, incluso cuando un administrador del sistema actualizará el archivo de Leapsecond e insertará uno nuevo. Esto no afecta a ningún Moment fuera de los segundos de salto, por lo que no hay un segundo de error de apagado. => Si vuelve a ejecutar el código después de la inserción del nuevo salto de segundo, entonces la marca de tiempo del momento almacenado sigue siendo la misma. La salida formateada no cambia, lo cual es bueno.

c) Puede construir un Moment que represente un segundo de salto, ya sea eligiendo una fuente de tiempo especializada (en el futuro, entregaré un cliente SNTP que podría rastrear un segundo de salto), o aplicando un número adecuado de segundos SI. añadido a un Moment normal. La salida formateada para tal marca de tiempo mostrará un segundo valor de 60, siempre que la tabla de segundos de salto esté actualizada. Si transfiere este segundo intervalo a otra JVM mediante la serialización en la que la tabla secundaria no está actualizada, se manejará allí como un segundo apagado (y si la vuelve a serializar a una JVM debidamente actualizada o si el receptor -JVM se actualiza correctamente más tarde, luego se mostrará de nuevo el salto del segundo).

d) Time4J también soporta escala de tiempo GPS. Puede crear un Moment dando los segundos transcurridos desde la época del GPS (1980-01-06 medianoche al inicio) y especificando la escala de tiempo del GPS. Internamente, el Moment convierte los datos a un estado UTC que no tiene pérdidas, siempre que la tabla leapsecond esté actualizada. Por supuesto, si su configuración no está actualizada y la fuente de GPS emite una cantidad de segundos transcurridos que representan un evento de un segundo, entonces habrá un error de un segundo. Para evitar errores tan pequeños y raros debido a que no se administran adecuadamente las tablas de leapsecond en las JVM del cliente, puede ser una buena idea instalar otro mecanismo para la configuración. Time4J define una interfaz SPI para este propósito.

Nos topamos con esto con C # en Windows y también usamos la clase DateTime de .NET: no toma en cuenta los segundos de salto a pesar de que la documentación de MSDN parece implicar lo contrario.

Decidimos continuar con esto como un problema conocido. Un día, para solucionar este problema, planeamos regresar y agregar algo como una tabla de base de datos o un archivo de configuración que se pueda actualizar en junio / julio y diciembre / enero para admitir y permitir ajustes del valor de compensación para permitir Lo que sabemos hoy es exactamente tantos segundos, correctamente explicados por segundos de salto.

Si alguien tiene otras ideas shinys en C # o Java, nos encantaría escucharlas. Gracias.