9 Pregunta: ¿Por qué restar estas dos veces (en 1927) da un resultado extraño?

pregunta creada en Fri, Nov 16, 2018 12:00 AM

Si ejecuto el siguiente programa, que analiza dos cadenas de fecha que hacen referencia a intervalos de 1 segundo y las compara:

 
public static void main(String[] args) throws ParseException {
    SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    String str3 = "1927-12-31 23:54:07";  
    String str4 = "1927-12-31 23:54:08";  
    Date sDt3 = sf.parse(str3);  
    Date sDt4 = sf.parse(str4);  
    long ld3 = sDt3.getTime() /1000;  
    long ld4 = sDt4.getTime() /1000;
    System.out.println(ld4-ld3);
}

La salida es:

  

353

¿Por qué ld4-ld3 no es 1 (como lo esperaría de la diferencia de un segundo en el tiempo), pero 353?

Si cambio las fechas a tiempos 1 segundo después:

 
String str3 = "1927-12-31 23:54:08";  
String str4 = "1927-12-31 23:54:09";  

Entonces ld4-ld3 será 1.


Versión de Java:

 
java version "1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)

Timezone(`TimeZone.getDefault()`):

sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN
    
6461
  1. ¿Realmente te topaste con esa situación exacta en un escenario de la vida real o esta pregunta solo pretendía ser un rompecabezas, solo por el gusto de hacerlo?
    2011-07-27 08: 42: 29Z
  2. @ Costi Ciudatu: FWIW, fácilmente puedo imaginar esto como resultado de reducir un error más grande, es decir, "¿Por qué estas dos fechas no están separadas por un año? exactamente un año aparte? "
    2011-07-30 01: 43: 46Z
  3. La respuesta real es siempre, siempre use segundos desde una época para el registro, como la época de Unix, con una representación de enteros de 64 bits (firmada, si desea permitir sellos) antes de la época). Cualquier sistema de tiempo real tiene algún comportamiento no lineal, no monotónico, como horas de salto o horario de verano.
    2012-07-12 08: 34: 06Z
  4. originalmente publicado como Oracle Bug ID 7070044 el 23 de julio `11
    2012-08-04 10: 55: 55Z
  5. Como @Costi Ciudatu, realmente me pregunto de qué informe de error detectó el OP este. También estoy bastante seguro de que, aparte de ser un enigma, esto no es útil para 99.9 (agregar lotes de 9 dígitos) por ciento de usuarios. Obtiene la medalla para el "juego" de reputación.
    2013-10-11 10: 32: 39Z
9 Respuestas                              9                         

Es un cambio de zona horaria el 31 de diciembre en Shanghai.

Consulte esta página para obtener detalles de 1927 en Llevar a la fuerza. Básicamente, a medianoche de fines de 1927, los relojes retrocedieron 5 minutos y 52 segundos. Así que "1927-12-31 23:54:08" en realidad sucedió dos veces, y parece que Java lo está analizando como el posible instante más tarde para esa fecha /hora local, de ahí la diferencia.

Otro episodio en el mundo de las zonas horarias, a menudo extraño y maravilloso.

EDITAR: ¡Deja de presionar! La historia cambia ...

La pregunta original ya no mostrará el mismo comportamiento, si se reconstruye con la versión 2013a de TZDB . En 2013a, el resultado sería 358 segundos, con un tiempo de transición de 23:54:03 en lugar de 23:54:08.

Solo noté esto porque estoy recopilando preguntas como esta en Noda Time, en la forma de pruebas de unidad ... La prueba ahora se ha cambiado, pero solo se muestra, ni siquiera los datos históricos son seguros.

EDITAR: el historial ha cambiado nuevamente ...

En TZDB 2014f, el tiempo del cambio se ha movido a 1900-12-31, y ahora es solo un cambio de 343 segundos (por lo que el tiempo entre t y t+1 es de 344 segundos, si ves lo que quiero decir).

EDITAR: para responder una pregunta sobre una transición en 1900 ... parece que la implementación de la zona horaria de Java trata a todas las zonas horarias de simplemente como si estuvieran en su tiempo estándar para Cualquier instante antes del inicio de las 1900 UTC:

 
import java.util.TimeZone;

public class Test {
    public static void main(String[] args) throws Exception {
        long startOf1900Utc = -2208988800000L;
        for (String id : TimeZone.getAvailableIDs()) {
            TimeZone zone = TimeZone.getTimeZone(id);
            if (zone.getRawOffset() != zone.getOffset(startOf1900Utc - 1)) {
                System.out.println(id);
            }
        }
    }
}

El código anterior no produce resultados en mi máquina con Windows. Así que cualquier zona horaria que tiene unaUn desplazamiento distinto al estándar al comienzo de 1900 contará eso como una transición. La propia TZDB tiene algunos datos anteriores a eso, y no se basa en ninguna idea de un tiempo estándar "fijo" (que es lo que getRawOffset asume como un concepto válido), por lo que otras bibliotecas no necesitan introducir esta transición artificial. /p>     

10363
2019-02-07 09: 29: 50Z
  1. @ Gareth: No, pero revisar los detalles de las transiciones de la zona horaria de Shanghai en ese período fue mi primer puerto de escala. Y he estado trabajando en transiciones de zona horaria para Noda Time recientemente, por lo que la posibilidad de ambigüedad está prácticamente al frente de mis pensamientos de todos modos ...
    2011-07-27 08: 35: 08Z
  2. @ Johannes: Para que sea una zona horaria más normal a nivel mundial, creo que el desplazamiento resultante es UTC + 8. París hizo lo mismo en 1911, por ejemplo: timeanddate. com /worldclock /clockchange.html? n = 195 & year = 1911
    2011-07-27 13: 25: 02Z
  3. @ Charles: en ese entonces, los viajeros sabían que la hora local sería diferente en todas partes (porque lo era). Además, los relojes eran mecánicos y se desviaban rápidamente, por lo que las personas solíamos ajustarlos según la torre del reloj local cada dos días, incluso si no viajaban. Entonces, ¿cómo se pusieron los relojes de la torre (que también se desviaron)? Más fácilmente configurándolos a las 12:00 cuando el sol alcanzó su pico diario ... que era diferente en cada lugar y no en la misma longitud. Esta era la norma prácticamente en todas partes hasta que los horarios de los ferrocarriles requerían algún tipo de estandarización.
    2011-07-28 12: 58: 39Z
  4. Pero entonces: ¿cómo en la Tierra este tipo de conocimiento ha sobrevivido a las edades, de modo que hace casi un siglo, se implementa en el software? En 2011, cualquiera que mencione las rarezas de la zona horaria a un ingeniero que no sea de software es visto como un nerd. (Y realmente, la gente espera que todo el software lo abstraiga, y no les importa si es ambiguo, cuando dicen 'mediodía', nosotros el ingeniero de software debería lidiar con eso). Pero, para imaginar a alguien en Shangai, en diciembre de 1927, pensando que sería relevante anotar algo así, y que de alguna manera esta información nunca se perdió, se eliminó, nada ... la mente está perdida.
    2011-07-30 16: 28: 45Z
  5. @ EdS. En realidad, solo le tomó 15 minutos, la razón por la que muestra una diferencia de 16 minutos se debe a una ligera discrepancia en la zona horaria el 27 de julio de 2011 causada por lo increíble que es Jon Skeet.
    2013-10-18 16: 37: 09Z

Ha encontrado una discontinuidad de la hora local :

  

Cuando la hora estándar local estaba a punto de llegar al domingo 1 de enero de 1928,   00:00:00 los relojes se atrasaron 0:05:52 horas hasta el sábado 31.   Diciembre de 1927, 23:54:08 hora estándar local en su lugar

Esto no es particularmente extraño y ha ocurrido en casi todas partes en un momento u otro debido a que las zonas horarias se cambiaron o cambiaron debido a acciones políticas o administrativas.

    
1541
2011-07-27 08: 38: 51Z
  1. @ Jason: Para la lectura de la hora de dormir, sugeriría que (ahora) la base de datos de zona horaria de la IANA (previamente administrada por un tipo encantador llamado Olson, creo) sería una gran recurso: iana.org/time-zones . Por lo que sé, la mayoría del mundo de código abierto (bibliotecas así mencionadas) lo utilizan como su fuente principal de datos de zona horaria.
    2012-03-1121: 25: 45Z

La moraleja de esta extrañeza es:

  • Use fechas y horas en UTC siempre que sea posible.
  • Si no puede mostrar una fecha u hora en UTC, siempre indique la zona horaria.
  • Si no puede requerir una fecha /hora de entrada en UTC, requiera una zona horaria explícitamente indicada.
623
2011-07-28 11: 50: 08Z
  1. La conversión /almacenamiento en UTC realmente no ayudaría para el problema descrito, ya que encontraría la discontinuidad en la conversión a UTC.
    2011-07-30 04: 28: 55Z
  2. @ Mark Mann: si su programa usa UTC internamente en todas partes, convirtiendo a /desde una zona horaria local solo en la UI, no le importaría sobre tales discontinuidades.
    2011-08-26 11: 50: 42Z
  3. @ Raedwald: Claro que sí. ¿Cuál es la hora UTC de 1927-12-31 23:54:08? (Ignorando, por el momento, que UTC ni siquiera existía en 1927). En algunos puntos, esta hora y fecha están ingresando a su sistema, y ​​usted tiene que decidir qué hacer con él. Al decirle al usuario que tiene que ingresar el tiempo en UTC, solo le pasa el problema al usuario, no lo elimina.
    2012-02-19 22: 39: 53Z
  4. Me siento vindicado por la cantidad de actividad en este hilo, ya que he estado trabajando en la refactorización de una aplicación grande por fecha y hora durante casi un año. Si está haciendo algo como el calendario, no puede "simplemente" almacenar UTC, ya que las definiciones de las zonas horarias en las que se pueden representar cambiarán con el tiempo. Almacenamos el "tiempo de intención del usuario", la hora local del usuario y su zona horaria, y UTC para buscar y clasificar, y cada vez que se actualiza la base de datos de IANA, recalcularemos todos los tiempos UTC.
    2012-12-07 22: 34: 54Z

Al aumentar el tiempo, debe volver a convertir a UTC y luego sumar o restar. Utilice la hora local solo para mostrar.

De esta manera, podrá recorrer los períodos en los que las horas o los minutos pasan dos veces.

Si convirtió a UTC, agregue cada segundo y conviértalo a la hora local para su visualización. Pasarías por 11:54:08 p.m. LMT - 11:59:59 p.m. LMT y luego 11:54:08 p.m. CST - 11:59:59 p.m. CST.

    
345
2011-12-27 20: 04: 04Z

En lugar de convertir cada fecha, utiliza el siguiente código

 
long difference = (sDt4.getTime() - sDt3.getTime()) / 1000;
System.out.println(difference);

Y verás el resultado es:

 
1
    
290
2018-04-02 11: 47: 10Z
  1. Me temo que ese no es el caso. Puede probar mi código en su sistema, generará 1, porque tenemos diferentes configuraciones regionales.
    2012-05-16 05: 39: 51Z
  2. Eso solo es cierto porque no ha especificado la configuración regional en la entrada del analizador. Eso es un mal estilo de codificación y una gran falla de diseño en Java: su localización inherente. Personalmente, puse "TZ = UTC LC_ALL = C" en todos los lugares donde uso Java para evitar eso. Además, debe evitar todas las versiones localizadas de una implementación a menos que esté interactuando directamente con un usuario y lo desee explícitamente. No realice NINGÚN cálculo, incluidas las localizaciones, use siempre las zonas horarias Locale.ROOT y UTC a menos que sea absolutamente necesario.
    2014-11-26 15: 53: 42Z

Lamento decirlo, pero la discontinuidad del tiempo se ha movido un poco hacia adentro

JDK 6 hace dos años, y en JDK 7 recientemente en actualización 25 .

Lección para aprender: evite los tiempos no UTC a toda costa, excepto tal vez para la visualización.

    
211
2018-12-17 00: 31: 22Z
  1. Esto es incorrecto. La discontinuidad no es un error, es solo que una versión más reciente de TZDB tiene datos ligeramente diferentes. Por ejemplo, en mi máquina con Java 8, si cambia el código muy ligeramente para usar "1927-12-31 23:54:02" y "1927-12-31 23:54:03" todavía verá una discontinuidad, pero de 358 segundos ahora, en lugar de 353. Incluso las versiones más recientes de TZDB tienen otra diferencia, vea mi respuesta para más detalles. No hay ningún error real aquí, solo una decisión de diseño sobre cómo se analizan los valores de texto de fecha /hora ambiguos.
    2014-10-30 17: 50: 27Z
  2. El problema real es que los programadores no entienden que la conversión entre el tiempo local y universal (en cualquier dirección) no es y no puede ser 100% confiable. Para las marcas de tiempo antiguas, los datos que tenemos sobre qué hora local era inestable en el mejor de los casos. Para las marcas de tiempo futuras, las acciones políticas pueden cambiar a qué hora universal se asigna una hora local determinada. Para las marcas de tiempo actuales y pasadas, puede tener el problema de que el proceso de actualización de la base de datos tz y el despliegue de los cambios pueden ser más lentos que el programa de implementación de las leyes.
    2017-03-14 16: 06: 29Z

Como explicaron otros, hay una discontinuidad de tiempo allí. Hay dos posibles compensaciones de zona horaria para 1927-12-31 23:54:08 en Asia/Shanghai, pero solo una compensación para 1927-12-31 23:54:07. Entonces, dependiendo de la compensación que se use, hay una diferencia de un segundo o una diferencia de 5 minutos y 53 segundos.

Este ligero cambio de compensaciones, en lugar del habitual horario de verano de una hora (horario de verano) al que estamos acostumbrados, oculta un poco el problema.

Tenga en cuenta que la actualización 2013a de la base de datos de zona horaria movió esta discontinuidad unos segundos antes, pero el efecto aún sería observable.

El nuevo paquete java.time en Java 8 le permite ver esto con más claridad y proporciona herramientas para manejarlo. Dado:

 
DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

String str3 = "1927-12-31 23:54:07";  
String str4 = "1927-12-31 23:54:08";  

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

Luego, durationAtEarlierOffset será de un segundo, mientras que durationAtLaterOffset será de cinco minutos y 53 segundos.

Además, estas dos compensaciones son las mismas:

 
// Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

Pero estos dos son diferentes:

 
// +08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

// +08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

Puede ver el mismo problema al comparar 1927-12-31 23:59:59 con 1928-01-01 00:00:00, aunque, en este caso, es el desplazamiento anterior el que produce la divergencia más larga, y es la fecha anterior la que tiene dos posibles compensaciones.

Otra forma de abordar esto es verificar si hay una transición en marcha. Podemos hacer esto así:

 
// Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

// An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

Puede verificar si la transición es una superposición, en cuyo caso hay más de un desplazamiento válido para esa fecha /hora, o un intervalo, en cuyo caso esa fecha /hora no es válida para esa identificación de zona, usando la isOverlap() y isGap() métodos en zot4.

Espero que esto ayude a las personas a manejar este tipo de problemas una vez que Java 8 esté disponible para todos, o para aquellos que usan Java 7 que adoptan el backport JSR 310.

    
187
2014-02-05 18: 56: 57Z
  1. Hola Daniel, he ejecutado tu parte del código pero no está dando el resultado esperado. like durationAtEarlierOffset y durationAtLaterOffset son solo de 1 segundo y también zot3 y zot4 son nulos. He configurado solo copiado y ejecuto este código en mi máquina. ¿Hay algo que necesita ser done aqui Déjame saber si quieres ver un pedazo de código. Aquí está el código tutorialspoint.com/… puede hacerme saber lo que está pasando aquí.
    2017-06-15 10: 30: 00Z
  2. @ vineeshchauhan Depende de la versión de Java, porque esto ha cambiado en tzdata, y diferentes versiones de JDK agrupan diferentes versiones de tzdata. En mi propia Java instalada, los tiempos son 1900-12-31 23:54:16 y 1900-12-31 23:54:17, pero eso no funciona en el sitio que compartiste, por lo que están usando una versión de Java diferente a la que yo
    2017-07-21 00: 38: 05Z

En mi humilde opinión, la localización omnipresente, implícita en Java es su principal defecto de diseño. Puede estar destinado a interfaces de usuario, pero francamente, que realmente utiliza Java para las interfaces de usuario hoy en día, excepto en algunos IDE en los que básicamente puede ignorar la localización porque los programadores no son exactamente el público objetivo. Puede arreglarlo (especialmente en servidores Linux) por:

  • exportar LC_ALL = C TZ = UTC
  • configura el reloj de tu sistema en UTC
  • nunca use implementaciones localizadas a menos que sea absolutamente necesario (es decir, solo para visualización)

A los miembros del Proceso de la Comunidad Java que recomiendo:

  • hace que los métodos localizados no sean los predeterminados, pero requieren que el usuario solicite explícitamente la localización.
  • use UTF-8 /UTC como el valor predeterminado de FIXED porque ese es simplemente el valor predeterminado hoy. No hay ninguna razón para hacer otra cosa, excepto si desea producir hilos como este.

Quiero decir, vamos, ¿no son las variables estáticas globales un patrón anti-OO? Nada más son los valores predeterminados generalizados dados por algunas variables de entorno rudimentarias .......

    
154
2018-07-24 14: 29: 48Z

Es un cambio de hora en 1927 en Shanghai, como han mencionado otros.

Básicamente, ese 23:54:07 en Shanghai, la hora estándar local, después de un momento, pasó al día siguiente en 00:00:00. Pero luego volvió a 23:54:08 para la hora estándar local. Por eso, la diferencia es de 343 segundos, no de 1 segundo.

Esto también puede desordenar otros lugares como los Estados Unidos. En Estados Unidos tienen horario de verano. Cuando comienza el horario de verano, la hora avanza 1 hora. Pero después de un tiempo, el horario de verano finaliza, retrocede 1 hora hasta la zona horaria estándar. Así que a veces, al comparar tiempos en los EE. UU., La diferencia es de aproximadamente 3600 segundos, no 1 segundo.

Es mejor usar UTC donde la hora no cambia a menos que sea necesario para usar la hora no UTC como en la pantalla.

    
5
2019-02-10 21: 47: 35Z
fuente colocada aquí