L’importanza delle date e delle ore nel mondo è palese a tutti: calendari, impegni, scadenze, pianificazioni, ricorrenze e quant’altro sono all’ordine del giorno nella vita quotidiana di ognuno. Diventa, così, importante sapere come trattare questi oggetti nello sviluppo dei programmi.
L’argomento non è di semplice discussione: le date sono degli oggetti ostici, che assumono forme diverse a seconda del luogo geografico in cui ci troviamo o, semplicemente, a seconda del gusto di chi le deve trattare. E gli orari non sono certo da meno.
In questo articolo intendo trattare l’argomento Date e Ore dal punto di vista della programmazione Java, cercando di coprire tutti gli aspetti e mettendo in evidenza gli strumenti che tale linguaggio fornisce per il trattamento di questi oggetti.
Cominciamo questo percorso con una classe di fondamentale importanza, la cui documentazione deve sempre essere sotto mano quando si ha a che fare con le date e le ore: la classe java.util.Calendar. Questa è la classe “principale” per il trattamento di date e orari. Fino alla versione 1.1 del JDK, la classe preposta al trattamento delle date era java.util.Date, ma in seguito il suo utilizzo è stato deprecato a favore di Calendar. La classe è rimasta per questioni di compatibilità, ma solo due dei suoi costruttori sono sopravvissuti alla deprecazione: il costruttore vuoto e quello con un parametro long.
Cominciamo a vedere, quindi, la classe Calendar: innanzitutto notiamo che essa è una classe astratta, quindi non è istanziabile in prima battuta attraverso un costruttore. Quello che si deve fare per costruire un oggetto Calendar, quindi, è affidarci ad una sua sottoclasse, oppure ad uno dei suoi metodi getInstance() (questi metodi vengono detti “factory“, perchè consentono di ottenere un oggetto tramite un metodo statico della stessa classe, che si occupa della sua costruzione). A me, personalmente, piace lavorare con le sottoclassi. L’unica sottoclasse concreta conosciuta è GregorianCalendar, che offre tanta maneggevolezza, grazie ai suoi 7 costruttori. Vediamo, quindi, un esempio pratico su come ottenere un oggetto che rappresenti la data odierna:
GregorianCalendar gc = new GregorianCalendar();
Più facile di così, proprio si muore. Tramite questa semplicissima linea di codice abbiamo costruito un oggetto Calendar (nello specifico, un GregorianCalendar) contenente la data e l’ora attuali. Il problema, ora, è quello di poter lavorare con questo oggetto. Vediamo, ad esempio, di farci stampare a video la data e l’ora dell’oggetto “gc” appena costruito, nel formato classico “GG/MM/AAAA - HH:MM:SS”:
int anno = gc.get(gc.YEAR);
int mese = gc.get(gc.MONTH) + 1; // I mesi partono da 0
int giorno = gc.get(gc.DATE);
int ore = gc.get(gc.HOUR);
int min = gc.get(gc.MINUTE);
int sec = gc.get(gc.SECOND);
if (gc.get(gc.AM_PM) == gc.PM)
ore += 12; // voglio l'ora su 24 ore
String visualizza = "" + anno + "/";
if (mese < 10) {
visualizza += "0" + mese;
} else {
visualizza += "" + mese;
}
visualizza += "/";
if (giorno < 10) {
visualizza += "0" + giorno;
} else {
visualizza += "" + giorno;
}
visualizza += " - ";
if (ore < 10) {
visualizza += "0" + ore;
} else {
visualizza += "" + ore;
}
visualizza += ":";
if (min < 10) {
visualizza += "0" + min;
} else {
visualizza += "" + min;
}
visualizza += ":"
if (sec < 10) {
visualizza += "0" + sec;
} else {
visualizza += "" + sec;
}
System.out.println( visualizza );
Questo esempio, per quanto banale e migliorabile, ci fa capire tre cose:
Se da un lato, quindi, risulta estremamente semplice ottenere le informazioni che ci interessano (tramite il metodo get()), dall’altro non è per nulla comodo effettuare visualizzazioni o semplici trasformazioni di formato delle date e delle ore. A questo, comunque, c’è un rimedio che Java mette a disposizione: la classe java.text.SimpleDateFormat. Questa classe permette di trattare con le date nel formato che più ci piace. Vediamo come si trasforma l’esempio precedente, utilizzando tale classe:
SimpleDateFormat sdf =
new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss");
System.out.println( sdf.format( gc.getTime() ) );
Tutto un altro modo di vivere. Possiamo notare che siamo dovuti ricorrere al metodo getTime() di Calendar, che restituisce un oggetto di tipo Date. Anche questo è uno dei motivi di sopravvivenza di questa classe.
Se dobbiamo effettuare l’operazione inversa, allo stesso modo, possiamo utilizzare il nostro SimpleDateFormat per cercare di ottenere un oggetto Calendar. Se abbiamo una stringa, quindi, che rappresenta una data e la vogliamo “convertire” in un oggetto Calendar, le operazioni da effettuare sono le seguenti:
SimpleDateFormat sdf =
new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss");
String miaData = "06/05/1986";
Calendar c = ( sdf.parse(miaData) ).getCalendar();
Questa operazione potrebbe sollevare una ParseException, nel caso in cui la stringa passata al metodo parse() non rappresenti una data convertibile.
A questo punto possiamo dedicarci a qualcosa di più sostanzioso ed ostico: i confronti fra le date e le ore. La maggior parte dei programmi che trattano con le date e con le ore devono effettuare dei confronti tra queste entità: pensiamo ad un allarme che deve sollevare un evento ad una certa ora, oppure ad un programma che deve estrarre tutte le fatture di un certo periodo di tempo (i casi sono praticamente infiniti). Chi ha una minima esperienza in questi casi sa che l’operazione di confronto fra due date (ma anche fra due ore) non è affatto una cosa semplice: bisogna tener conto dell’anno, quindi del mese all’interno dell’anno, quindi del giorno all’interno del mese, considerare quanti giorni ha il mese di interesse, aggiungiamoci l’eventualità di un anno bisestile… insomma, non si finisce più di fare calcoli e controlli. Per venire incontro alle esigenze dei programmatori, la classe Calendar prevede due comodi metodi: after() e before() che ritornano, entrambi, un valore booleano. Il primo ritorna true se l’oggetto che sto confrontando viene dopo quello passato come parametro, il secondo fa il contrario. Vediamo come vengono impiegati questi metodi:
GregorianCalendar data1 = new GregorianCalendar(2007, 11, 01);
GregorianCalendar data2 = new GregorianCalendar(2006, 11, 05);
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
if ( data1.before(data2) ) {
System.out.println(sdf.format(data1.getTime()) +
" viene prima di " +
sdf.format(data2.getTime()));
} else {
System.out.println(sdf.format(data2.getTime()) +
" viene prima di " +
sdf.format(data1.getTime()) +
" oppure sono uguali");
}
if ( data1.after(data2) ) {
System.out.println(sdf.format(data1.getTime()) +
" viene dopo di " +
sdf.format(data2.getTime()));
} else {
System.out.println(sdf.format(data2.getTime()) +
" viene dopo di " +
sdf.format(data1.getTime()) +
" oppure sono uguali");
}
Tutto semplice e comodo. Ma se il nostro programma prevede di effettuare un’operazione 25 giorni dopo una determinata data, che non conosciamo a priori? O se dobbiamo effettuare delle operazioni su delle date, ma senza spostarci dall’anno attuale? Anche in questo caso, abbiamo dei metodi molto comodi, che ci vengono forniti dalla classe GregorianCalendar: add() e roll(). Vediamo un esempio che ci fa capire la loro importanza:
// Il SimpleDateFormat usato è quello degli esempi precedenti.
// La data qui sotto impostata è 28 Novembre 2008 (10 = novembre)
GregorianCalendar gc = new GregorianCalendar(2008, 10, 28);
System.out.println( sdf.format(gc.getTime()) );
gc.add(gc.DATE, +33); // Aggiungo 33 giorni
System.out.println( sdf.format(gc.getTime()) );
gc.add(gc.YEAR, -1); // Tolgo 1 anno
System.out.println( sdf.format(gc.getTime()) );
gc.roll(gc.MONTH, +1); // Rollo in avanti di un mese
System.out.println( sdf.format(gc.getTime()) );
Eseguendo il codice postato sopra vedremo questo output:
28/11/2008 31/12/2008 31/12/2007 31/01/2007
Come si può vedere, Java ha fatto tutto il lavoro correttamente e noi non ci dobbiamo preoccupare di niente (aggiungere 33 giorni ad una data non è semplice: bisogna controllare quanti giorni ha il mese in corso, se si è a fine anno, se per caso è febbraio e l’anno è bisestile…). Il metodo roll() può spiazzare un po’ chi lo incontra per la prima volta: esso effettua l’aggiunta nel campo di interesse, senza apportare variazioni ai campi di ordine più alto (come l’anno, nell’esempio). Questo crea un effetto di rotazione all’interno dello stesso anno.
Se poi ci interessano altre informazioni, come, ad esempio, sapere se l’anno è bisestile o meno, abbiamo una serie di metodi che ci aiutano:
Concludendo, quindi, abbiamo tanti strumenti che ci permettono di trattare le date e le ore: trasformazioni, visualizzazioni, calcoli e confronti non ci devono preoccupare perchè il corredo di classi e metodi a supporto è davvero enorme. Tutto quello che dobbiamo fare è concentrarci sul nostro lavoro e utilizzarli. Buona Programmazione a tutti.
Tags:bisestile Calendar data GregorianCalendar java minuti ore secondi
Una Risposta
seba
May 9th, 2008 at 11:05
1In riferimento all’articolo “Lavorare con date e ore in Java”
Calendar c = ( sdf.parse(miaData) ).getCalendar();
è sbagliato!
parse resituisce un oggetto Date, e l’oggetto Date non ha il metodo getCalendar()!
RSS Iscriviti ai Feed dei commenti di questo articolo · TrackBack URI
Lascia un commento... please!!!