2015-07-03 20 views
7

Vedere il seguente pezzo di codice:SimpleDateFormat si comporta in modo incoerente

String timeString = "1980-01-01T14:00:00+0300"; 
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"); 
Date date2 = sdf.parse(timeString); 

// sdf.getCalendar().get(Calendar.ZONE_OFFSET); 
System.out.println(sdf.format(date2)); 

Ora, io sono in un paese che ha +2h offset, +1 legale (al momento). Se corro questo codice così com'è, verrà stampata

1980-01-01T13:00:00+0200 

Se io commento dalla riga che chiede circa il calendario di offset, l'output del programma è

1980-01-01T14:00:00+0300 

Qualsiasi idea del perché è che accada e come posso ottenere un risultato coerente?

Per evitare altre cose non chiare: Poiché sto gestendo un codice legacy, java 8 non è un'opzione. E sì, il punto chiave qui è WHY, e non quali sono i workaround? e ci sono 2 i perché:

  1. Perché mi passano un TZ 0300 e per impostazione predefinita ricevo un 0200 uno? (SimpleDateFormat deve sempre utilizzare TimeZone.getDefault a meno che non sia specificato ).
  2. Perché dà una risposta diversa solo perché chiamo un getter sull'istanza del calendario.
+3

che cosa è incoerente in esso? quando non usi l'offset, ti dà la data così com'è e quando lo usi cambia in base al fuso orario. –

+1

@RamanShrivastava Chiamare un getter una volta non dovrebbe cambiare l'output della funzione di formattazione. – wonderb0lt

risposta

2

Il problema è, che Calendar#get non è un semplice getter, sembra che questo:

public int get(int field) 
{ 
    complete(); 
    return internalGet(field); 
} 

dove complete fa questo, secondo il Javadoc:

riempimenti a qualsiasi unset campi nei campi del calendario. Innanzitutto, viene chiamato il metodo computeTime() se il valore del tempo (offset millisecondo da Epoch) non è stato calcolato dai valori del campo del calendario. Quindi, viene chiamato il metodo computeFields() per calcolare tutti i valori dei campi del calendario.

Ho cercato la fonte here, ma il codice è lo stesso anche per la versione più recente di Java.

+0

Sì, lo sapevo. Ma è solo un po '"lo fa perché lo fa". Per me sembra decisamente un insetto. –

+0

Direi, questa è una decisione progettuale discutibile. C'è una ragione per cui Java 8 ha una nuova data e ora API :) – meskobalazs

+0

Questa risposta sarebbe perfetta con un piccolo frammento di codice su come OP potrebbe cambiare il suo codice per evitare che 'complete()' rompa il suo codice (ad esempio impostando alcuni campi specifici su l'istanza del calendario, ecc.) – wonderb0lt

0

sdf.parse cambiamenti di fuso calendario interno del formattatore compensato a +0300

System.out.println(sdf.getCalendar()); 
    sdf.parse(timeString); 
    System.out.println(sdf.getCalendar()); 

si può vedere la differenza alla fine di linee di uscita

... ,ZONE_OFFSET=7200000,DST_OFFSET=0] 
... ,ZONE_OFFSET=10800000,DST_OFFSET=0] 

sdf.getCalendar().get(Calendar.ZONE_OFFSET); ripristina La differenza di fuso di eventi Torna alla fuso orario corrente

+1

La domanda è: perché lo fa? La mia impressione è la seguente: Il calendario ha un riferimento a un oggetto fuso orario. Durante l'analisi della data, non è possibile determinare il fuso orario da "+0300". Memorizza l'offset per 3 ore nel campo 'ZONE_OFFSET' di Calendar. Il metodo 'get()' calcola internamente tutti i campi per un calendario quando non lo ha ancora fatto per tutti i campi, incluso il campo 'ZONE_OFFSET'. Il calendario utilizza il fuso orario che conosce per calcolare l'offset e determina un diverso offset. – gogognome

+0

Sì, presumo che questa sia la risposta più esatta. In realtà, dove sono ora, come ho scritto anche nel post iniziale, sono in un offset di +2, +1 zona dst. Questo non è reprezentabile in alcun modo in un modello SimpleDateFormat. Ecco perché lo combiniamo come +3. Ma poi ancora, da qualche parte c'è un bug all'interno dell'intera elaborazione interna che fanno Calendar e SimpleDateFormat, dal momento che il comportamento di trasformazione (+3 +0) in (+2 +1) o lasciarlo com'era dovrebbe essere coerente e non influenzato da un get. Dato che non sto modificando sdf in alcun modo dopo la creazione, sdf.format (sdf.parse (txt)) dovrebbe sempre essere txt. –

0

SimpleDateFormate utilizza Calendar per creare la data. L'oggetto Date viene creato con il Timestamp, che viene calcolato in java.util.GregorianCalendar.computeTime(). Nella riga 2789 viene utilizzato il fuso orario iniziale (che è stato impostato java.text.SimpleDateFormat.initializeCalendar(Locale)). Ecco perché vedi il fuso orario sbagliato.

Quando si chiama get(Calendar.ZONE_OFFSET) viene chiamato il metodo java.util.GregorianCalendar.computeFields(), che utilizza il fuso orario impostato inizialmente.

I Indovina che questo è un Bug.

Problemi correlati