2016-06-29 67 views
6
TimeZone.setDefault(TimeZone.getTimeZone("BET")); 
Locale.setDefault(Locale.ENGLISH); 

SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzzz"); 

Date d0 = sdf1.parse("2037-10-17 23:00:00.000"); 
Date d1 = sdf1.parse("2037-10-17 23:00:00.001"); 
Date d2 = sdf1.parse("2037-10-17 23:59:59.999"); 
Date d3 = sdf1.parse("2037-10-18 00:00:00.000"); 
Date d4 = sdf1.parse("2037-10-18 00:00:00.001"); 
Date d5 = sdf1.parse("2037-10-18 00:59:59.999"); 
Date d6 = sdf1.parse("2037-10-18 01:00:00.000"); 
Date d7 = sdf1.parse("2037-10-18 01:00:00.001"); 
Date d8 = sdf1.parse("2037-10-18 01:59:59.999"); 
Date d9 = sdf1.parse("2037-10-18 02:00:00.000"); 

System.out.println(sdf2.format(d0) + "(" + d0.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d0) + ", offset: " + TimeZone.getDefault().getOffset(d0.getTime())); 
System.out.println(sdf2.format(d1) + "(" + d1.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d1) + ", offset: " + TimeZone.getDefault().getOffset(d1.getTime())); 
System.out.println(sdf2.format(d2) + "(" + d2.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d2) + ", offset: " + TimeZone.getDefault().getOffset(d2.getTime())); 
System.out.println(sdf2.format(d3) + "(" + d3.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d3) + ", offset: " + TimeZone.getDefault().getOffset(d3.getTime())); 
System.out.println(sdf2.format(d4) + "(" + d4.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d4) + ", offset: " + TimeZone.getDefault().getOffset(d4.getTime())); 
System.out.println(sdf2.format(d5) + "(" + d5.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d5) + ", offset: " + TimeZone.getDefault().getOffset(d5.getTime())); 
System.out.println(sdf2.format(d6) + "(" + d6.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d6) + ", offset: " + TimeZone.getDefault().getOffset(d6.getTime())); 
System.out.println(sdf2.format(d7) + "(" + d7.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d7) + ", offset: " + TimeZone.getDefault().getOffset(d7.getTime())); 
System.out.println(sdf2.format(d8) + "(" + d8.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d8) + ", offset: " + TimeZone.getDefault().getOffset(d8.getTime())); 
System.out.println(sdf2.format(d9) + "(" + d9.getTime() + "), dst: " + TimeZone.getDefault().inDaylightTime(d9) + ", offset: " + TimeZone.getDefault().getOffset(d9.getTime())); 

l'out ha messoBrasilia Summer Time di transizione a 2037-10-18

2037-10-17 23:00:00.000 Brasilia Time(2139444000000), dst: false, offset: -10800000 
2037-10-17 23:00:00.001 Brasilia Time(2139444000001), dst: false, offset: -10800000 
2037-10-17 23:59:59.999 Brasilia Time(2139447599999), dst: false, offset: -10800000 
2037-10-18 01:00:00.000 Brasilia Summer Time(2139447600000), dst: true, offset: -7200000 
2037-10-18 00:00:00.001 Brasilia Time(2139447600001), dst: true, offset: -10800000 
2037-10-18 00:59:59.999 Brasilia Time(2139451199999), dst: true, offset: -10800000 
2037-10-18 01:00:00.000 Brasilia Summer Time(2139447600000), dst: true, offset: -7200000 
2037-10-18 00:00:00.001 Brasilia Time(2139447600001), dst: true, offset: -10800000 
2037-10-18 00:59:59.999 Brasilia Time(2139451199999), dst: true, offset: -10800000 
2037-10-18 02:00:00.000 Brasilia Summer Time(2139451200000), dst: true, offset: -7200000 

Questo codice stampa i tempi di data intorno "2037/10/18 00: 00: 000 Brasilia Time", il risultato mostra che "2037-10-18 00: 00: 000 Brasilia Time" dovrebbe essere "2037-10-18 01: 00: 00.000 Brasilia Summer Time" che significa che Brasilia è entrata nell'ora legale in quel momento.

La mia domanda è perché tra "2037-10-18 00: 00: 00.001 Brasilia Time" e "2037-10-18 00: 59: 59.999 Brasilia Time" l'offset del fuso orario continua a utilizzare l'offset orario standard. È un bug dei dati del fuso orario di JDK o questo fuso orario funziona in questo modo.

Il mio codice utilizzando offset per decidere se c'è una transizione dst tra due date. Ovviamente "2037-10-18 01: 00: 00.000 Brasilia Summer Time" e "2037-10-18 00: 59: 59.999 Brasilia Time" quelle due date non funzionano qui.

Posso modificare l'uso di "TimeZone.getDefault(). InDaylightTime (Date date)" per decidere se c'è una transizione, ma voglio ancora sapere se si tratta di un bug di JDK.

+0

Se c'è un bug è probabilmente nel parser dato che 'd3' e' d6' (così come 'd4/7' e' d5/8') sono analizzati allo stesso valore lungo. – Thomas

+0

Quale versione di Java e versione di Olson TZDB? –

+0

Potrebbe essere un bug - l'API java.time sembra dare [i risultati corretti] (http://ideone.com/XHmdwY). – assylias

risposta

0

Ho provato a passare il codice con un debugger e sembra essere un problema con il fuso orario e l'interruttore stesso: da BRT a BRST passa da 00:00:00 a 01:00:00 a mezzanotte, il che significa che l'ora tra in realtà non esiste.

Dal mio debug del problema sembra essere in GregorianCalendar#computeTime() soprattutto nella riga seguente:

millis -= zoneOffsets[0] + zoneOffsets[1]; 

Prima che la linea millis è il tempo dal periodo che è stato calcolato a partire dalla data analizzati e che è diversa per 00:00:00 (213946800000) e 01:00:00 (2139440400000). In entrambi i casi, zoneOffsets[0] è -10800000, che è lo scostamento grezzo a UTC.

La differenza è in zoneOffsets[1]: per 00:00:00 è 0 ma per 01:00:00 è 3600000, vale a dire 1 ora. La ragione di ciò sembra essere una chiamata interna a inDaylightTime(new Date(millis)) che restituisce false per 00:00:00 (pre-dst) ma true per 01:00:00 (che è la prima ora di dst). Da qui il tempo finale sarà lo stesso dato si aggiunge sempre 10800000 millis ma sottrarre 3600000 Millis dal maggior valore che è superiore di 3600000 millis :)

Alla fine si ottiene un Date con lo stesso tempo milli.

Quando formattazione della data di nuovo sembra che il formattatore controllerà il tempo milli contro il fuso orario e ogni volta che corrisponderebbe a 00:00:00,000 - 59:59:59,999, cioè che potrebbe essere in entrambe le zone di tempo verranno assunte avere un dstOffset di 0 anziché 3600000 e quindi diversi fusi orari vengono stampati.

Edit: quando si confrontano 01:00:00.000 e 01:00:00.001 sembra come se ci potrebbe essere un bug nel ZoneInfo.getOffsets(time, offsets, type) che restituisce un offset di 360000 per il primo e 0 per i secondi, mentre quando compilando i campi del calendario all'interno del formattatore dst entrambi assumeranno dst.

Modifica 2: Quando si modifica il formato del parser per accettare le scorciatoie del fuso orario, è possibile osservare lo stesso comportamento, ad es.00:00:00.000 BRT e 01:00:00.000 BRST vengono analizzati a 2139447600000 e formattati di nuovo in 01:00:00.000 BRST mentre 00:00:00.001 BRT e 01:00:00.001 BRST vengono analizzati in 2139447600001 e formattati in 00:00:00.001 BRT - che di per sé è corretto.