Domanda Perché sottrarre queste due volte (nel 1927) dando uno strano risultato?


Se eseguo il seguente programma, che analizza due stringhe di data che fanno riferimento a tempi di 1 secondo e li confronta:

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

L'output è:

353

Perché è ld4-ld3 non 1 (come mi aspetterei dalla differenza di un secondo nei tempi), ma 353?

Se cambio le date a volte 1 secondo dopo:

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

Poi ld4-ld3 sarà 1.


Versione 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

6014
2017-07-27 08:15


origine


risposte:


È un cambio di fuso orario il 31 dicembre a Shanghai.

Vedere questa pagina  per i dettagli del 1927 a Shanghai. Fondamentalmente a mezzanotte alla fine del 1927, gli orologi tornarono indietro di 5 minuti e 52 secondi. Quindi "1927-12-31 23:54:08" in realtà è successo due volte, e sembra che Java lo stia analizzando come il dopo  possibile istante per quella data / ora locale - quindi la differenza.

Solo un altro episodio nel mondo spesso strano e meraviglioso dei fusi orari.

MODIFICARE:  Fermati! La storia cambia ...

La domanda originale non avrebbe più dimostrato lo stesso comportamento, se ricostruita con la versione 2013a di TZDB . Nel 2013a, il risultato sarebbe 358 secondi, con un tempo di transizione di 23:54:03 anziché di 23:54:08.

L'ho notato solo perché sto raccogliendo domande come questa in Noda Time, sotto forma di test unitari ... Il test ora è stato modificato, ma va solo a dimostrare - nemmeno i dati storici sono al sicuro.

MODIFICARE:  La storia è cambiata di nuovo ...

Nel TZDB 2014f, il tempo del cambiamento è passato a 1900-12-31, e ora è un semplice 343 secondi di cambiamento (quindi il tempo tra t e t+1 è 344 secondi, se vedi cosa intendo).

MODIFICARE:  Per rispondere a una domanda attorno a una transizione a 1900 ... sembra che l'implementazione del fuso orario Java sia valida tutti  i fusi orari come semplicemente essere nel loro tempo standard per qualsiasi istante prima dell'inizio del 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);
            }
        }
    }
}

Il codice sopra non produce output sulla mia macchina Windows. Quindi qualsiasi fuso orario che abbia un offset diverso da quello standard all'inizio del 1900, verrà considerato come una transizione. Lo stesso TZDB ha alcuni dati che risalgono a prima, e non si basa su alcuna idea di un tempo standard "fisso" (che è ciò che getRawOffset si presume che sia un concetto valido), quindi altre biblioteche non hanno bisogno di introdurre questa transizione artificiale.


9717
2017-07-27 08:31



Hai incontrato un discontinuità del tempo locale :

Quando l'ora standard locale stava per raggiungere domenica 1 ° gennaio 1928,   00:00:00 gli orologi sono stati girati indietro 0:05:52 ore a sabato 31.   Dicembre 1927, 23:54:08 ora standard locale

Questo non è particolarmente strano ed è successo praticamente dappertutto in un momento o in un altro quando i fusi orari sono stati cambiati o modificati a causa di azioni politiche o amministrative.


1461
2017-07-27 08:38



La morale di questa stranezza è:

  • Utilizza le date e le ore in UTC ovunque sia possibile.
  • Se non è possibile visualizzare una data o un'ora in UTC, indicare sempre il fuso orario.
  • Se non è possibile richiedere una data / ora di input in UTC, è necessario un fuso orario esplicitamente indicato.

578
2017-07-28 11:50



Quando si incrementa il tempo, è necessario riconvertire in UTC e quindi aggiungere o sottrarre. Utilizza l'ora locale solo per la visualizzazione.

In questo modo sarai in grado di attraversare tutti i periodi in cui le ore o i minuti si verificano due volte.

Se hai convertito in UTC, aggiungi ogni secondo e converti in ora locale per la visualizzazione. Dovresti passare alle 11:54:08 LMT  - 11:59:59 pm LMT e poi 11:54:08 pm CST  - 11:59:59 pm CST.


318
2017-07-30 16:55



Invece di convertire ogni data, si utilizza il seguente codice

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

E vedi il risultato è:

1

263
2018-05-16 05:31



Mi dispiace dirlo, ma la discontinuità nel tempo si è spostata un po '

JDK 6  due anni fa, e in JDK 7  solo di recente in aggiornamento 25 .

Lezione per imparare: evita i tempi non UTC a tutti i costi, tranne, forse, per la visualizzazione.


186
2018-02-17 22:44



Come spiegato da altri, c'è una discontinuità nel tempo lì. Ci sono due possibili offset del fuso orario per 1927-12-31 23:54:08 a Asia/Shanghai, ma solo un offset per 1927-12-31 23:54:07. Quindi, a seconda dell'offset utilizzato, c'è una differenza di un secondo o una differenza di 5 minuti e 53 secondi.

Questo leggero spostamento di offset, anziché la solita ora legale di un'ora (periodo estivo) a cui siamo abituati, oscura un po 'il problema.

Si noti che l'aggiornamento 2013a del database del fuso orario ha spostato questa discontinuità pochi secondi prima, ma l'effetto sarebbe ancora osservabile.

Il nuovo java.time pacchetto su Java 8, lascia che sia usato per vederlo più chiaramente e fornire gli strumenti per gestirlo. Dato:

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

Poi durationAtEarlierOffset sarà un secondo, mentre durationAtLaterOffset saranno cinque minuti e 53 secondi.

Inoltre, questi due offset sono gli stessi:

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

Ma questi due sono diversi:

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

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

Puoi vedere lo stesso problema confrontando 1927-12-31 23:59:59 con 1928-01-01 00:00:00, tuttavia, in questo caso, è l'offset precedente a produrre la divergenza più lunga, ed è la data precedente che ha due possibili offset.

Un altro modo per avvicinarsi a questo è controllare se c'è una transizione in corso. Possiamo farlo in questo modo:

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

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

Puoi verificare se la transizione è una sovrapposizione - nel qual caso c'è più di un offset valido per quella data / ora - o un divario - nel qual caso quella data / ora non è valida per quell'id della zona - utilizzando il isOverlap() e isGap() metodi su zot4.

Spero che questo aiuti le persone a gestire questo tipo di problema una volta che Java 8 diventa ampiamente disponibile, oa coloro che utilizzano Java 7 che adotta il backport JSR 310.


166
2018-01-03 14:43