1. Contesto storico: come si lavorava con date e orari prima di Java 8
Nei tempi antichi (prima di Java 8) per lavorare con date e orari si usavano le classi java.util.Date, java.util.Calendar e per la formattazione — java.text.SimpleDateFormat. Era uno di quei casi in cui i programmatori di tutto il pianeta sospiravano all’unisono e, digrignando i denti, scrivevano qualcosa del tipo:
import java.util.Date;
Date now = new Date();
System.out.println(now); // Stamperà qualcosa di strano come "Wed Jun 05 14:15:22 MSK 2025"
Sembra semplice, ma in realtà non era così roseo. Ecco solo alcune delle «gioie» delle vecchie API:
- Date è un oggetto mutabile (mutable). Si poteva modificarlo accidentalmente e questo spesso portava a bug.
- I mesi in Date e Calendar partivano da zero (gennaio è 0, dicembre — 11), mentre i giorni partivano da uno.
- SimpleDateFormat non era thread-safe: se due thread formattavano una data contemporaneamente, si potevano ottenere risultati inattesi.
- Un’enorme quantità di metodi era contrassegnata come @Deprecated (obsoleti), e l’IDE vi spaventava continuamente con avvisi gialli.
- La gestione dei fusi orari era un vero dolore: era facile confondere l’ora locale con l’UTC, e del passaggio all’ora legale/solare è meglio non parlare.
Esempio doloroso
import java.util.Date;
Date date = new Date(2025, 5, 1); // anno 2025, mese 5 (giugno?), giorno 1
System.out.println(date); // Non quello che ti aspetti!
2. Comparsa di java.time: un nuovo approccio
Entro il 2014 era chiaro: le vecchie API non erano solo scomode — erano pericolose. Per questo in Java è nato il nuovo package — java.time, che implementa la specifica JSR‑310. Questa API è stata ispirata dalla popolare libreria Joda-Time ed è diventata subito lo standard de facto.
Principali package e classi
- java.time — il package principale, dove vivono tutte le nuove classi per date e orari.
- java.time.format — per formattare e fare il parsing di date e orari.
- java.time.temporal — per operazioni temporali più avanzate.
- java.time.zone — per lavorare con i fusi orari.
Ecco i protagonisti della nuova API:
| Classe | A cosa serve? | Esempio di utilizzo |
|---|---|---|
|
Solo data (anno, mese, giorno) | Compleanno, senza orario |
|
Solo ora (ore, minuti, secondi) | Orario di un incontro, senza data |
|
Data e ora, senza fuso orario | Evento locale |
|
Data e ora con fuso orario | Incontro a Minsk all’ora locale |
|
Punto temporale assoluto (UTC) | Timestamp di un evento nel log |
|
Intervallo di tempo (ore, minuti, sec.) | Durata di una chiamata |
|
Periodo (anni, mesi, giorni) | Anzianità lavorativa, età |
Esempio: creare una data nel nuovo modo
import java.time.LocalDate;
LocalDate today = LocalDate.now();
System.out.println(today); // Ad esempio, "2025-06-05"
3. Vantaggi del nuovo API
Immutabilità (immutable)
Tutte le classi di java.time sono immutabili. Significa che se create un oggetto LocalDate, non potete modificarlo. Ogni operazione (ad esempio, aggiungere un giorno) restituisce un nuovo oggetto.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-06-05
System.out.println(tomorrow); // 2025-06-06
Gestione esplicita dei fusi orari
Con le vecchie API era facile dimenticare in quale fuso orario si trovasse una data. In java.time è tutto esplicito: se serve il fuso orario — usate ZonedDateTime, se non serve — usate LocalDateTime.
import java.time.ZonedDateTime;
import java.time.ZoneId;
ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
System.out.println(MinskTime); // 2025-06-05T14:23:45+03:00[Europe/Minsk]
Metodi comodi per calcoli e confronti
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plusMonths(1);
boolean isAfter = LocalDate.now().plusDays(1).isAfter(today); // true
Formattazione e parsing
Formattazione e parsing — tramite DateTimeFormatter (in dettaglio — nella prossima lezione):
import java.time.format.DateTimeFormatter;
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy");
String formatted = today.format(formatter); // "05.08.2025"
4. Compatibilità: come convivere con il codice vecchio
Nel mondo reale molto spesso bisogna lavorare con librerie o sistemi legacy che usano Date e Calendar. Per fortuna, la nuova API è amichevole con l’eredità: si possono convertire i tipi vecchi in nuovi e viceversa.
Conversione Date ↔ Instant
import java.util.Date;
import java.time.Instant;
// Date → Instant
Date legacyDate = new Date();
Instant instant = legacyDate.toInstant();
// Instant → Date
Date dateBack = Date.from(instant);
Conversione Calendar ↔ ZonedDateTime
import java.util.Calendar;
import java.time.ZonedDateTime;
import java.time.ZoneId;
import java.util.Date;
// Calendar → ZonedDateTime
Calendar calendar = Calendar.getInstance();
ZonedDateTime zdt = ZonedDateTime.ofInstant(
calendar.toInstant(),
calendar.getTimeZone().toZoneId()
);
// ZonedDateTime → Calendar
Calendar calBack = Calendar.getInstance();
calBack.setTime(Date.from(zdt.toInstant()));
Tabella: mappatura tra classi vecchie e nuove
| Classe vecchia | Classe nuova | Commento |
|---|---|---|
|
|
Tempo assoluto |
|
|
Data e ora con fuso orario |
|
|
Formattazione/parsing delle date |
5. Pratica: primi passi con java.time
Supponiamo di avere un utente con una data di nascita. Salviamo e stampiamo questa data:
import java.time.LocalDate;
public class UserProfile {
private String name;
private LocalDate birthDate;
public UserProfile(String name, LocalDate birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public void printProfile() {
System.out.println("Nome: " + name);
System.out.println("Data di nascita: " + birthDate);
}
}
public class Main {
public static void main(String[] args) {
UserProfile user = new UserProfile("Alisa", LocalDate.of(1998, 12, 25));
user.printProfile();
}
}
Output:
Nome: Alisa
Data di nascita: 1998-12-25
6. Confronto: vecchio API vs nuovo API
Esempio: aggiungere una settimana alla data di nascita
Metodo vecchio (Date/Calendar):
import java.util.Calendar;
Calendar cal = Calendar.getInstance();
cal.set(1998, Calendar.DECEMBER, 25);
cal.add(Calendar.WEEK_OF_YEAR, 1);
System.out.println(cal.getTime()); // Macchinoso e poco chiaro
Metodo nuovo (java.time):
import java.time.LocalDate;
LocalDate birthDate = LocalDate.of(1998, 12, 25);
LocalDate nextWeek = birthDate.plusWeeks(1);
System.out.println(nextWeek); // 1999-01-01
Con il nuovo API il codice è più corto, più semplice e più sicuro.
7. Errori tipici nell’uso di java.time
Errore n. 1: dimenticare che gli oggetti sono immutabili.
Se chiamate date.plusDays(1); e non salvate il risultato, la data originale rimarrà invariata.
Errore n. 2: confusione tra LocalDate e LocalDateTime.
LocalDate contiene solo la data (anno, mese, giorno), mentre LocalDateTime include anche l’ora. Non confondeteli se dovete gestire ore e minuti.
Errore n. 3: usare le classi vecchie nei nuovi progetti.
Se possibile — usate sempre java.time. Le classi vecchie servono solo per compatibilità.
Errore n. 4: gestione errata dei fusi orari.
Se dovete memorizzare un evento rilevante per regioni diverse, usate ZonedDateTime o almeno Instant. LocalDateTime non contiene informazioni sul fuso orario!
Errore n. 5: tentare di confrontare direttamente LocalDate e LocalDateTime.
Sono tipi diversi; non si possono confrontare direttamente. Prima convertiteli allo stesso tipo.
GO TO FULL VERSION