La gestione delle date e orario in Java

La gestione delle date e degli orari è un argomento molto delicato ma di importanza fondamentale nelle applicazioni software. La possibilità di avere degli strumenti potenti e versatili è fondamentale nella OOP per semplificare il lavoro dello sviluppatore. In Java esistono diverse classi per la gestione delle date e dell’orario. La storica e più intuitiva sarebbe la classe Date che conteneva tutto per gestire sia le date che gli orari, ma è deprecata dalla versione 8 (diciamo la versione di Java spartiacque per numerose scelte tecnologiche di questo linguaggio), cioè ritenuta obsoleta e non più consigliata per essere usata nei nuovi progetti. I motivi dietro questa scelta sono diversi ma non li elenchiamo in questo articolo tecnico.

Finita l’era della classe Date, comincia quelle delle classi LocalDate, LocalTime e LocalDateTime incluse nella libreria java.time assieme anche a Instant che serve a gestire lo UTC. TimeUtil e Calendar. Java time riscrive interamente la libreria per la gestione di date e orari in modo decisamente più efficiente. Attenzione però! LocalDate e LocalTime sono date e orari secchi, completamente decontestualizzati da un calendario vero e proprio con i giorni feriali/festivi ad esempio e non contempla fuso orario.

La classe LocalDate

Partiamo da come costruire una variabile/attributo con questo tipo. Sembra strano ma quello della data è un tipo detto immutabile, non una vera classe ma rappresenta un valore senza dare altre informazioni a riguardo. Per creare, dichiarare e valorizzare una data si procede:

import java.time
...
LocalDate inizio = LocalDate.of(2022,11,05);

Dove tra parentesi andiamo a specificare gli interi corrispondenti ad anno, mese e giorno. Sono al contrario perché nei paesi anglofoni si usa così!

Come posso valorizzare una LocalDate alla data corrente? La API time mette a disposizione il metodo now() che possiamo utilizzare nel seguente modo:

LocalDate adess= LocalDate.now();

Ovviamente per la data ci sono tutti i metodi di accesso del caso che ci permettono di ripescare il giorno, mese o anno di una data assegnata in precedenza. Occhio al metodo di accesso del mese: ce n’è uno che restituisce il mese a lettere, uno il valore numerico da 1 a 12. Presentiamo anche la traduzione in italiano.

LocalDate inizio = LocalDate.of(2022, 11,1);       
System.out.println(inizio.getYear());   //stampa 2022
System.out.println(inizio.getMonth());   //stampa NOVEMBER
 System.out.println(inizio.getMonth().getDisplayName(TextStyle.FULL, Locale.ITALIAN)); //stampa novembre
System.out.println(inizio.getMonthValue());  //stampa  11
System.out.println(inizio.getDayOfMonth());   //stampa  1

Se avessi bisogno di conoscere la differenza in giorni tra due date? Devo ricorrere ad un metodo che converte le date in un numero di giorni, un conteggio che parte da una data zero per poi effettuare la differenza. Ad aiutarci c’è il metodo toEpochDay() che restituisce un valore long, un intero più grande a 64bit invece di 32, che altro non è che il numero di giorni dalla data posta nell’oggetto e la data zero che è intesa al 1 gennaio 1970, 1/1/1970.

LocalDate inizio = LocalDate.of(2022, 11,1);
LocalDate fine = LocalDate.of(2022, 11,11);

long differenza = fine.toEpochDay() - inizio.toEpochDay();
System.out.println(differenza);   //stampa 10

Altre funzionalità molto interessanti e comode ci permettono di reperire in modo semplice informazioni aggiuntive su una data a cominciare da quale giorno dell’anno corrisponda quello indicato rispetto ai 365 giorni canonici della anno solare, il giorno della settimana corrispondete, sia in formato testuale che numerico. Si possono fare poi conti semplici per individuare mesi ed anni relativi ai giorni individuati.

Più articolato è se volessimo conoscere in modo diretto, senza conti, il tempo intercorso tra due date direttamente in anni, mesi e giorni. Dobbiamo ricorrere ad una classe di supporto Period.

LocalDate i = LocalDate.of(2021, 9,1);
LocalDate f = LocalDate.of(2022, 11,11);
Period p = Period.between(i, f);
System.out.println("Differenza: " + p.getYears() + " anni, " + p.getMonths() + " mesi, " + p.getDays() + " giorni");
//Stampa "Differenza: 1 anni, 2 mesi, 10 giorni"
LocalDate test = LocalDate.of(2022, 11,6);        
System.out.println(test.getDayOfYear());  //stampa 310
System.out.println(test.getDayOfWeek());   //stampa SUNDAY, domenica in inglese
System.out.println(test.getDayOfWeek().getValue());   //restituisce 7 inteso come settimo giorno della settimana

Oggettivamente un po’ meno intuitivo e complesso da realizzare è riscrivere la data in un formato “italiano” dove il giorno della settimana è tradotto e anche la disposizione dei valori non ricalca la versione anglofona anno mese giorno, ma al contrario giorno mese anno.

System.out.println(test.format(DateTimeFormatter.ofPattern("EEEE dd/MM/yyyy", Locale.ITALIAN))); //domenica 06/11/2022

Altri metodi che possono tornare utili sono quelli is che permettono di confrontare la data chiamante con una passata per parametro e capire quale sia posteriore, antecedente o uguale attraverso il ritorno boolean vero/falso

test.isAfter(test);
test.isBefore(test);
test.isEqual(test);

La classe LocalTime

Non è un mistero se alcune dinamiche vista nella LocalDate le ritroviamo anche nel LocalTime. Cominciamo dai costruttori, di due tipi uno che contempla orario secco ore/minuti, probabilmente il più utilizzato, ed uno anche con i secondi.

LocalTime orarioInizio = LocalTime.of(22,17);
LocalTime orarioFine = LocalTime.of(23,57, 50);

Anche qui ovviamente abbiamo i metodi di accesso alle componenti di un orario già dichiarato

orarioFine.getHour();   //torna intero 23
orarioFine.getMinute();  //torna intero 57
orarioFine.getSecond(); //torna intero 50, 0 se non inizializzati i secondi

Interessante è la sottrazione tra due orari per valutare il tempo trascorso. Come per le date, occorre trasformare in secondi le singole date anche se questa volta non serve uno stratagemma della data zero artificiale poiché l’ora 0 o meglio 00:00:00 esiste! Il metodo è toSecondOfDay(). Possiamo fare un giochino in più: i secondi che troviamo li ritrasformiamo in un orario così da avere istantaneamente il numero di secondi, minuti e ore di differenza.

LocalTime orarioInizio = LocalTime.of(22,17);
LocalTime orarioFine = LocalTime.of(23,57, 50);        
int secondi = orarioFine.toSecondOfDay() - orarioInizio.toSecondOfDay();
System.out.println(secondi);
System.out.println(LocalTime.ofSecondOfDay(secondi));

E se partissimo dai secondi e volessimo convertire in giorni, ore, minuti e secondi corrisponde quel conteggio? Ci occorre la classe TimeUtil che permette agilmente di convertire secondi, minuti, ore tra di loro. Con una serie di sottrazioni successive, possiamo quindi convertire una grandezza in secondi

       long durataTot = 12345678;

        long giorni = TimeUnit.SECONDS.toDays(durataTot);
        long ore = TimeUnit.SECONDS.toHours(durataTot) - TimeUnit.DAYS.toHours(giorni);
        long minuti = TimeUnit.SECONDS.toMinutes(durataTot) - TimeUnit.HOURS.toMinutes(ore) - TimeUnit.DAYS.toMinutes(giorni);
        long secondi= durataTot - TimeUnit.MINUTES.toSeconds(minuti) -TimeUnit.HOURS.toSeconds(ore) - TimeUnit.DAYS.toSeconds(giorni);

LocalDateTime

Vista la gestione delle date e degli orari, è quasi scontata la combinazione delle due in un unico tipo.

Ultima modifica 13 Novembre 2022