2010-05-31 46 views
14

Ho appena incontrato uno strano comportamento con la classe GregorianCalendar e mi chiedevo se stavo davvero facendo qualcosa di male.Strano comportamento con GregorianCalendar

Questo si aggiunge solo quando il mese della data di inizializzazione ha un massimo effettivo massimo del mese in cui imposterò il calendario.

Ecco il codice di esempio:

// today is 2010/05/31 
    GregorianCalendar cal = new GregorianCalendar(); 

    cal.set(Calendar.YEAR, 2010); 
    cal.set(Calendar.MONTH, 1); // FEBRUARY 

    cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); 
    cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); 
    cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); 
    cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); 
    cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); 

    return cal.getTime(); // => 2010/03/03, wtf 

So che il problema è causato dal fatto che la data di inizializzazione del calendario è un mese 31 giorni (maggio), che scherza con il mese impostato a febbraio (28 giorni). La soluzione è semplice (basta impostare day_of_month su 1 prima di impostare l'anno e il mese), ma mi chiedevo se questo fosse davvero il comportamento desiderato. Qualche idea ?

risposta

14

Sta ottenendo i massimi effettivi della data/ora corrente. Maggio ha 31 giorni, 3 in più rispetto al 28 febbraio e passerà quindi al 3 marzo.

è necessario chiamare Calendar#clear() dopo aver ottenuto/crearlo:

GregorianCalendar cal = new GregorianCalendar(); 
cal.clear(); 
// ... 

Questo si traduce in:

Sun Feb 28 23:59:59 GMT-04:00 2010 

(che è corretto come per il mio fuso orario)

Come detto in uno delle risposte, java.util.Calendar e Date sono errori epici. Considerare JodaTime quando si eseguono operazioni intensive di data/ora.

+0

Questo funziona. Grazie!! –

2

Sì, questo è il modo in cui è destinato a funzionare. Se inizi da un GregorianCalendar con una data precisa e lo modifichi rendendolo incoerente, non dovresti fidarti dei risultati che ottieni.

Secondo la documentazione su getActualMaximum(..) si afferma:

Ad esempio, se la data di questa istanza è 1 febbraio 2004, il valore massimo effettivo del campo DAY_OF_MONTH è 29, perché il 2004 è un anno bisestile e se la data di questa istanza è il 1 febbraio 2005, è 28.

Quindi è necessario che funzioni, ma è necessario alimentarlo con valori coerenti. 31 febbraio 2010 non è corretto e l'applicazione di cose che si basano sul valore della data (come getActualMaximum) non può funzionare. Come dovrebbe risolverlo da solo? Decidendo che quel mese è sbagliato? o che il giorno è sbagliato?

A proposito, come tutti si afferma sempre utilizzare JodaTime .. :)

+1

Se si imposta il mese a febbraio, un'API normale si renderà conto che ciò invalida il giorno e deve essere regolato. Ma 'java.util.Calendar' è molte cose, ma non una normale API. Vedere il primo commento qui per come fa JodaTime: http://blog.smart-java.nl/blog/index.php/2010/01/20/java-util-calendar-getactualmaximum-returns-strange-results/ – Yishai

1

Forse setLenient(boolean lenient) sarà risolvere la cosa per voi. Ottengo un'eccezione quando eseguo il codice qui sotto.

In caso contrario, Joda è una risposta migliore.

import java.util.Calendar; 

public class CalTest 
{ 
    public static void main(String[] args) 
    { 
     // today is 2010/05/31 
     Calendar cal = Calendar.getInstance(); 
     cal.setLenient(false); 

     cal.set(Calendar.YEAR, 2010); 
     cal.set(Calendar.MONTH, 1); // FEBRUARY 

     cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH)); 
     cal.set(Calendar.HOUR_OF_DAY, cal.getActualMaximum(Calendar.HOUR_OF_DAY)); 
     cal.set(Calendar.MINUTE, cal.getActualMaximum(Calendar.MINUTE)); 
     cal.set(Calendar.SECOND, cal.getActualMaximum(Calendar.SECOND)); 
     cal.set(Calendar.MILLISECOND, cal.getActualMaximum(Calendar.MILLISECOND)); 

     System.out.println(cal.getTime()); 
    } 
} 
+0

Sfortunatamente anche un rigido calendario è di utilità limitata. Avrebbe generato un'eccezione a 'getTime' ma nient'altro. http://blog.smart-java.nl/blog/index.php/2010/01/20/java-util-calendar-getactualmaximum-returns-strange-results/ – Yishai

+0

D'accordo - questo è quello che osservo pure. – duffymo

2

Sono sicuro che non si desidera un comportamento. Sono altrettanto sicuro che nessuno abbia mai pensato di usare il caso quando hanno fatto la lezione. Il fatto è che Calendar ha un grosso problema con lo stato interno e come gestisce tutte le transizioni potenziali in tutti i metodi impostati.

Se non è possibile utilizzare JodaTime o JSR-310 nel progetto, l'unità esegue un test intensivo quando si utilizza la classe Calendar. Come puoi vedere in questo caso, il codice di Calendar si comporta in modo diverso a seconda del giorno del mese (o dell'ora del giorno) in cui esegui il codice.

0

Il motivo dovrebbe essere che MONTH ha una struttura logica simile a un elenco. Puoi facilmente riempire e leggere Array/Collezioni/Liste. A causa dell'internazionalizzazione deve essere enumerabile (accesso indiretto). DAY è solo un numero intero accessibile direttamente. Questa è la differenza.

0

Il calendario inizia con il giorno corrente - 31 maggio 2010 nell'esempio. Quando si imposta il mese a febbraio, la data cambia al 31 febbraio 2010, normalizzata fino al 3 marzo 2010, quindi cal.getActualMaximum (Calendar.DAY_OF_MONTH) restituisce 31 per marzo.

Calendar c = Calendar.getInstance(); 
c.set(Calendar.YEAR, 2010); 
c.set(Calendar.MONTH, Calendar.MAY); 
c.set(Calendar.DAY_OF_MONTH, 31); 
System.out.println(c.getTime()); 
c.set(Calendar.MONTH, Calendar.FEBRUARY); 
System.out.println(c.getTime()); 

uscita:

Mon May 31 20:20:25 GMT+03:00 2010 
Wed Mar 03 20:20:25 GMT+03:00 2010 

il fissaggio di codice, è possibile aggiungere cal.clear(); oppure impostare il giorno 1..28 prima di impostare il mese