java timezone - Perché sottrarre queste due volte(nel 1927)dando uno strano risultato?




convert date (8)

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";  

Quindi 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

Answers

IMHO la pervasiva, implicita localizzazione in Java è il suo difetto di progettazione più grande. Può essere inteso per le interfacce utente, ma francamente, chi usa realmente Java per le interfacce utente oggi eccetto per alcuni IDE in cui si può sostanzialmente ignorare la localizzazione perché i programmatori non sono esattamente il target di riferimento. Puoi sistemarlo (specialmente sui server Linux):

  • esporta LC_ALL = C TZ = UTC
  • imposta il tuo orologio di sistema su UTC
  • non utilizzare mai implementazioni localizzate se non assolutamente necessario (ad es. solo per la visualizzazione)

Ai membri del processo della comunità Java consiglio:

  • rendere i metodi localizzati non predefiniti, ma richiedere all'utente di richiedere esplicitamente la localizzazione.
  • utilizzare UTF-8 / UTC come predefinito FIXED, perché è semplicemente l'impostazione predefinita oggi. Non c'è motivo di fare qualcos'altro, tranne se vuoi produrre discussioni come questa.

Voglio dire, dai, non sono variabili statiche globali un modello anti-OO? Nient'altro è quei default pervasivi dati da alcune variabili di ambiente rudimentali .......


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

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

Vedi 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 la stia analizzando come l'istante più tardi possibile per quella data / ora locale, quindi la differenza.

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

MODIFICA: basta premere! 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 TZDB ... Il test ora è stato modificato, ma va solo a dimostrare - nemmeno i dati storici sono al sicuro.

EDIT: la storia è cambiata di nuovo ...

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

EDIT: per rispondere a una domanda intorno a una transizione a 1900 ... sembra che l'implementazione del fuso orario Java consideri tutti i fusi orari semplicemente nel loro tempo standard per qualsiasi istante prima dell'inizio di UTC 1900:

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 è quello che getRawOffset assume come concetto valido), quindi altre librerie non hanno bisogno di introdurre questa transizione artificiale.


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.

Hai riscontrato una discontinuità nell'ora 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.


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 pm LMT - 11:59:59 pm LMT e poi 11:54:08 CST - 11:59:59 pm CST.


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

JDK 6 due anni fa, e in JDK 7 solo recentemente nell'aggiornamento 25 .

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


tl; dr

java.time.temporal.ChronoUnit.DAYS.between( 
    earlier.truncatedTo( ChronoUnit.DAYS )  , 
    later.truncatedTo( ChronoUnit.DAYS ) 
)

…o…

java.time.temporal.ChronoUnit.HOURS.between( 
    earlier.truncatedTo( ChronoUnit.HOURS )  , 
    later.truncatedTo( ChronoUnit.HOURS ) 
)

java.time

Cordiali saluti, il progetto Joda-Time è ora in modalità di manutenzione , con il team che consiglia la migrazione alle classi java.time .

L'equivalente di Joda-Time DateTime è ZonedDateTime .

ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
ZonedDateTime now = ZonedDateTime.now( z ) ;

A quanto pare, vuoi contare i giorni per data, nel senso che vuoi ignorare l'ora del giorno. Ad esempio, l'avvio di un minuto prima di mezzanotte e la conclusione di un minuto dopo la mezzanotte dovrebbe comportare un singolo giorno. Per questo comportamento, estrai un LocalDate dal tuo ZonedDateTime . La classe LocalDate rappresenta un valore di sola data senza ora del giorno e senza fuso orario.

LocalDate localDateStart = zdtStart.toLocalDate() ;
LocalDate localDateStop = zdtStop.toLocalDate() ;

Usa l' ChronoUnit per calcolare i giorni trascorsi o altre unità.

long days = ChronoUnit.DAYS.between( localDateStart , localDateStop ) ;

Troncare

Per quanto riguarda la possibilità di fare un conteggio più generale in cui si è interessati al delta di ore come ore del giorno anziché a ore complete come span-of-time di sessanta minuti, utilizzare il metodo truncatedTo .

Ecco il tuo esempio di 14:45 alle 15:12 dello stesso giorno.

ZoneId z = ZoneId.of( "America/Montreal" ); 
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 14 , 45 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 17 , 15 , 12 , 0 , 0 , z );

long hours = ChronoUnit.HOURS.between( start.truncatedTo( ChronoUnit.HOURS ) , stop.truncatedTo( ChronoUnit.HOURS ) );

1

Per un conteggio di giorni per data, troncare su ChronoUnit.DAYS . Ecco un esempio che va oltre la mezzanotte da cinque minuti prima a cinque minuti dopo, per i giorni trascorsi di 1 .

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime start = ZonedDateTime.of( 2017 , 1 , 17 , 23 , 55 , 0 , 0 , z );
ZonedDateTime stop = ZonedDateTime.of( 2017 , 1 , 18 , 00 , 05 , 0 , 0 , z );

long days = ChronoUnit.DAYS.between( start.truncatedTo( ChronoUnit.DAYS ) , stop.truncatedTo( ChronoUnit.DAYS ) );

1

A proposito di java.time

Il framework java.time è integrato in Java 8 e java.time successive. Queste classi soppiantano le fastidiose vecchie lezioni di data e ora come java.util.Date , Calendar e SimpleDateFormat .

Il progetto Joda-Time , ora in modalità di manutenzione , consiglia la migrazione alle classi java.time .

Per saperne di più, consultare l' esercitazione Oracle . E cerca per molti esempi e spiegazioni. La specifica è JSR 310 .

È possibile scambiare oggetti java.time direttamente con il proprio database. Utilizzare un driver JDBC compatibile con JDBC 4.2 o successivo. Nessuna necessità di stringhe, nessuna necessità per le classi java.sql.* .

Dove ottenere le classi java.time?

  • Java SE 8 , Java SE 9 , Java SE 10 e versioni successive
    • Built-in.
    • Parte dell'API Java standard con un'implementazione in bundle.
    • Java 9 aggiunge alcune funzionalità e correzioni minori.
  • Java SE 6 e Java SE 7
    • Gran parte della funzionalità java.time è back-porting su Java 6 e 7 in ThreeTen-Backport .
  • Android
    • Versioni successive di implementazioni di bundle Android delle classi java.time.
    • Per Android precedente (<26), il progetto ThreeTenABP adatta ThreeTen-Backport (menzionato sopra). Vedi Come usare ThreeTenABP ....

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un terreno di prova per possibili aggiunte future a java.time. Puoi trovare alcune classi utili come Interval , YearWeek , YearQuarter e more .

Il progetto ThreeTen-Extra estende java.time con classi aggiuntive. Questo progetto è un terreno di prova per possibili aggiunte future a java.time. Puoi trovare alcune classi utili come Interval , YearWeek , YearQuarter e more .





java date timezone