La Classe Calendar di Java
La classe legacy java.util.Calendar e GregorianCalendar — come si relazionano con java.time.
java.util.Calendar è la seconda metà della coppia di data/ora legacy. Mentre java.util.Date è solo un wrapper attorno a un long di millisecondi epoch, Calendar è ciò che sa in quale anno, mese e giorno cade quel millisecondo. È stato aggiunto in Java 1.1 per estrarre l'aritmetica del calendario da Date — ecco perché la maggior parte degli accessor di campo di Date sono deprecati.
Nel codice moderno dovresti usare java.time. Ma Calendar appare ancora: nei vecchi percorsi di codice, nelle librerie scritte prima di Java 8 e nei driver JDBC che non sono stati aggiornati. Devi saperlo leggere, fare il bridge e andare avanti. Questa pagina spiega come creare un Calendar, leggere i suoi campi in modo sicuro (attenzione ai mesi a base zero), fare aritmetica con il calendario e — soprattutto — fare il bridge verso l'API moderna.
Calendar è astratto
Calendar stesso è una classe astratta. Non chiami mai new Calendar(...). Ottieni un'istanza tramite la factory:
Calendar cal = Calendar.getInstance();Questo restituisce una sottoclasse concreta — quasi sempre GregorianCalendar — precaricata con l'ora corrente, il fuso orario predefinito e il locale predefinito. Puoi essere esplicito se necessario:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);GregorianCalendar ha anche costruttori pubblici, ma la factory è la convenzione.
Costanti di campo
Calendar espone tutto attraverso costanti di campo intere e un generico get(int field):
| Campo | Significato | Intervallo |
|---|---|---|
Calendar.YEAR | Anno | anno completo, es. 2026 |
Calendar.MONTH | Mese | 0–11 (Gennaio = 0) |
Calendar.DAY_OF_MONTH | Giorno del mese | 1–31 |
Calendar.DAY_OF_WEEK | Giorno della settimana | 1–7 (Domenica = 1) |
Calendar.HOUR_OF_DAY | Ora | 0–23 |
Calendar.MINUTE | Minuto | 0–59 |
Calendar.SECOND | Secondo | 0–59 |
Calendar.MILLISECOND | Milli | 0–999 |
Due trappole si trovano in questa tabella: MONTH è a base zero (Calendar.JANUARY == 0) e DAY_OF_WEEK inizia dalla domenica. I bug di offset di uno nel codice legacy quasi sempre risalgono a queste.
Impostazione e aritmetica
set accetta un campo e un valore; add esegue un'aritmetica consapevole del calendario che trasferisce l'overflow nei campi più grandi:
Calendar cal = Calendar.getInstance();
cal.set(2026, Calendar.MAY, 29); // Y, M, D — month is zero-based
cal.add(Calendar.DAY_OF_MONTH, 5); // → 2026-06-03roll fa la stessa cosa campo per campo ma non riporta il riporto nei campi più grandi — roll(DAY_OF_MONTH, 5) sul 30 maggio dà il 4 maggio, non il 4 giugno. È raramente quello che si vuole.
Le istanze di Calendar sono mutabili, quindi passarne una a un metodo significa consegnare un handle attivo. Clonala prima di esporla, come faresti con un Date.
Il bridge verso java.time
L'unico motivo per cui oggi si tocca Calendar è per uscirne. Due metodi lo fanno:
Instant when = cal.toInstant();
ZoneId zone = cal.getTimeZone().toZoneId();
ZonedDateTime zdt = when.atZone(zone);Da ZonedDateTime hai a disposizione l'intera API moderna. Andando nella direzione opposta:
Calendar cal = GregorianCalendar.from(zdt); // Java 8+GregorianCalendar.from(ZonedDateTime) è la conversione supportata. Evita i round-trip attraverso Date.
Esempio pratico
L'esempio qui sotto mostra le due cose che farai con Calendar nel codice reale: leggere i campi da un'istanza legacy e fare il bridge verso java.time per qualsiasi cosa non banale.
Cosa ricavare dall'esecuzione:
Calendar.MONTHper maggio stampa4. I mesi a base zero sono la singola maggiore fonte di bug nel codice di date legacy.getInstance(TimeZone)lega l'istanza a una zona — diversamente daDate, che non ne ha.add(MONTH, 1)comprende la lunghezza dei mesi; ottieni il giorno corretto nel mese successivo anche se i mesi non sono tutti da 30 giorni.cal.toInstant().atZone(cal.getTimeZone().toZoneId())è il bridge in una riga versojava.time.GregorianCalendar.from(ZonedDateTime)ti permette di restituire unCalendara una vecchia API senza perdere la zona.
Cosa c'è dopo
Questo chiude la Parte 14 — Data e Ora. La prossima è la Parte 15, Multithreading e Concorrenza, che inizia con Multithreading in Java — il modello che ha plasmato ogni altra API in questo libro.