¿Por qué la resta de estos dos tiempos (en 1927) da un resultado extraño?

Si ejecuto el siguiente programa que analiza dos cadenas de fecha, me refiero una vez cada 1 segundo y las comparo:

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

Conclusión:

353

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

Si cambié las fechas unos segundos 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:

27 июля '11 в 11:15 2011-07-27 11:15 Freewind se establece el 27 de julio de 2011 a las 11:15 2011-07-27 11:15
@ 8 respuestas

Este es un cambio de zona horaria el 31 de diciembre en Shanghai.

Para detalles, vea esta página para detalles de 1927 en Shanghai. Básicamente, a la medianoche de fines de 1927, el reloj volvió a 5 minutos y 52 segundos. Entonces, "1927-12-31 23:54:08" en realidad sucedió dos veces, y parece que Java lo analiza como un posible momento posterior para esta fecha / hora local, de ahí la diferencia.

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

EDITAR: dejar de hacer clic! Cambiar historial ...

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

Acabo de darme cuenta, porque recojo preguntas como esta en Noda Time, en forma de pruebas unitarias ... La prueba ahora se ha cambiado, pero solo muestra que incluso los datos históricos no son seguros.

EDITAR: La historia ha cambiado de nuevo ...

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

EDITAR: Para responder a la pregunta sobre la transición en 1900 ... parece que la implementación de la zona horaria de Java considera que todas las zonas horarias son simplemente estándar para cualquier punto de tiempo antes del comienzo de 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 muestra Windows en mi computadora. Por lo tanto, cualquier zona horaria que tenga un desplazamiento diferente de su estándar a principios de 1900 considerará esto como una transición. La propia TZDB tiene algunos datos devueltos anteriormente y no se basa en ninguna idea de un tiempo estándar "fijo" (lo que significa que getRawOffset se considera un concepto válido), por lo que otras bibliotecas no necesitan ingresar a esta transición artificial.

9853
27 июля '11 в 11:31 2011-07-27 11:31 Jon Skeet da la respuesta el 27 de julio de 2011 a las 11:31 2011-07-27 11:31

Te enfrentas a un intervalo de tiempo local :

Cuando llegó la hora estándar local, el domingo 1 de enero de 1928, a las 00:00:00, las horas regresaron a las 0:05:52 horas hasta el sábado 31 de diciembre de 1927, 23:54:08 en lugar de la hora local habitual.

border=0

Esto no es particularmente extraño y sucedió en todas partes en un momento u otro, cuando los intervalos de tiempo se cambiaron o cambiaron debido a acciones políticas o administrativas.

1481
27 июля '11 в 11:38 2011-07-27 11:38 Michael Borgwardt da la respuesta el 27 de julio de 2011 a las 11:38 2011-07-27 11:38

La moraleja de esta rareza es:

  • Utilice fechas y horas en UTC siempre que sea posible.
  • Si no puede mostrar la fecha o la hora en UTC, especifique siempre la zona horaria.
  • Si no puede ingresar la fecha / hora de entrada en UTC, necesita una zona horaria explícitamente especificada.
590
28 июля '11 в 14:50 2011-07-28 14:50 La respuesta se da a Raedwald el 28 de julio de 2011 a las 14:50 2011-07-28 14:50

Si aumenta el tiempo, debe regresar a UTC y luego sumar o restar. Use la hora local solo para mostrar.

Por lo tanto, puede pasar por cualquier período en que las horas o los minutos se produzcan dos veces.

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

324
30 июля '11 в 19:55 2011-07-30 19:55 La respuesta se da PatrickO 30 de julio de 2011 a las 19:55 2011-07-30 19:55

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

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

Y ver el resultado:

 1 
269
16 мая '12 в 8:31 2012-05-16 08:31 Rajshri da la respuesta el 16 de mayo de '12 a las 8:31 am 2012-05-16 08:31

Lamento decir eso, pero el intervalo de tiempo se ha movido un poco.

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

Lección para aprender: evite el tiempo sin UTC a toda costa, excepto, quizás, para la visualización.

192
18 февр. la respuesta la da el usuario 1050755 el 18 de febrero. 2014-02-18 01:44 '14 a la 1:44 2014-02-18 01:44

Como explican otros, hay un intervalo de tiempo. Para 1927-12-31 23:54:08 en Asia/Shanghai hay dos posibles compensaciones de línea de tiempo, pero solo un desplazamiento para 1927-12-31 23:54:07 . Por lo tanto, según el desplazamiento que se use, hay una diferencia de un segundo o una diferencia de 5 minutos y 53 segundos.

Este pequeño cambio en el desplazamiento, en lugar del habitual ahorro diario de una hora (horario de verano), con el que estamos acostumbrados, oscurece ligeramente el problema.

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

El nuevo paquete java.time en Java 8 le permite usarlo con mayor claridad y proporcionar herramientas para su procesamiento. 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, y 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 comparando 1927-12-31 23:59:59 de 1928-01-01 00:00:00 , aunque en este caso es un desplazamiento anterior, lo que conduce a una mayor divergencia, y es un anterior Una fecha que tiene dos posibles compensaciones.

Otra forma de abordar esto es verificar si se está produciendo la transición. Podemos hacer esto de la siguiente manera:

 // 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 este caso, hay más de un desplazamiento permitido para esta fecha / hora o un espacio). En este caso, esta fecha / hora no es válida para este identificador de zona, usando isOverlap() y isGap() en zot4 .

Espero que esto ayude a las personas a lidiar con este problema tan pronto como Java 8 esté disponible, o para aquellos que usan Java 7 que usan el backport JSR 310

171
03 янв. La respuesta fue dada por Daniel C. Sobral el 3 de enero. 2014-01-03 17:43 '14 a las 17:43 2014-01-03 17:43

En mi humilde opinión, la localización implícita en Java es uno de los mayores defectos de diseño. Puede diseñarse para interfaces de usuario, pero para ser honesto, quién usa realmente Java para las interfaces de usuario hoy en día, con la excepción de algunos IDE, donde la mayoría puede ignorar la localización, porque los programadores no son el público objetivo. Puedes arreglar esto (especialmente en servidores Linux):

  • exportar LC_ALL = C TZ = UTC
  • configurar el reloj del sistema a UTC
  • nunca use implementaciones localizadas si es absolutamente necesario (es decir, solo para visualización)

Como miembros de la comunidad de Java, recomiendo:

  • Los métodos localizados no son estándar, pero requieren que el usuario solicite explícitamente la localización.
  • use UTF-8 / UTC como el valor FIJO predeterminado, porque es el valor predeterminado hoy. No hay ninguna razón para hacer otra cosa, a menos que desee crear tales transmisiones.

Quiero decir, ¿no vamos a las variables estáticas globales del patrón anti-OO? Nada más es un valor predeterminado común dado por algunas variables de entorno elementales .......

142
26 нояб. La respuesta es dada por el usuario1050755 26 de noviembre. 2014-11-26 18:58 '14 a las 18:58 2014-11-26 18:58