La classe Calendar en Java
La classe legacy java.util.Calendar et GregorianCalendar — leur rôle et leur relation avec java.time.
java.util.Calendar est la seconde moitié du duo date/heure historique. Là où java.util.Date n'est qu'un wrapper autour d'un long de millisecondes depuis l'époque, Calendar est ce qui sait à quelle année, quel mois et quel jour correspond cette milliseconde. Elle a été ajoutée dans Java 1.1 pour extraire l'arithmétique calendaire de Date — c'est pourquoi la plupart des accesseurs de champs de Date sont dépréciés.
Dans le code moderne, vous devriez vous tourner vers java.time. Mais Calendar apparaît encore : dans d'anciens chemins de code, dans des bibliothèques écrites avant Java 8, et dans des drivers JDBC qui n'ont pas été mis à jour. Vous devez savoir la lire, la convertir et passer à autre chose. Cette page explique comment créer un Calendar, lire ses champs en toute sécurité (attention au mois basé sur zéro), effectuer de l'arithmétique calendaire et — surtout — le convertir vers l'API moderne.
Calendar est abstraite
Calendar elle-même est une classe abstraite. Vous n'appelez jamais new Calendar(...). Vous obtenez une instance via la fabrique :
Calendar cal = Calendar.getInstance();Cela renvoie une sous-classe concrète — presque toujours GregorianCalendar — préchargée avec l'heure actuelle, le fuseau horaire par défaut et la locale par défaut. Vous pouvez être explicite si nécessaire :
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);GregorianCalendar possède également des constructeurs publics, mais la fabrique est la convention.
Constantes de champs
Calendar expose tout via des constantes de champs entiers et un get(int field) générique :
| Champ | Signification | Plage |
|---|---|---|
Calendar.YEAR | Année | année complète, ex. 2026 |
Calendar.MONTH | Mois | 0–11 (Janvier = 0) |
Calendar.DAY_OF_MONTH | Jour du mois | 1–31 |
Calendar.DAY_OF_WEEK | Jour de la semaine | 1–7 (Dimanche = 1) |
Calendar.HOUR_OF_DAY | Heure | 0–23 |
Calendar.MINUTE | Minute | 0–59 |
Calendar.SECOND | Seconde | 0–59 |
Calendar.MILLISECOND | Millisecondes | 0–999 |
Deux pièges se cachent dans ce tableau : MONTH est basé sur zéro (Calendar.JANUARY == 0) et DAY_OF_WEEK commence au dimanche. Les bugs de décalage d'un dans le code legacy se tracent presque toujours ici.
Définition et arithmétique
set prend un champ et une valeur ; add effectue une arithmétique calendaire qui propage les débordements vers les champs plus grands :
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 fait la même chose champ par champ mais ne propage pas vers les champs plus grands — roll(DAY_OF_MONTH, 5) sur le 30 mai donne le 4 mai, pas le 4 juin. C'est rarement ce que vous voulez.
Les instances de Calendar sont mutables, donc passer une instance à une méthode revient à donner un accès direct à l'objet. Clonez avant d'exposer, de la même façon que vous le feriez avec un Date.
Le pont vers java.time
L'intérêt de manipuler Calendar aujourd'hui est d'en sortir. Deux méthodes permettent cela :
Instant when = cal.toInstant();
ZoneId zone = cal.getTimeZone().toZoneId();
ZonedDateTime zdt = when.atZone(zone);À partir de ZonedDateTime, vous avez accès à toute l'API moderne. Dans l'autre sens :
Calendar cal = GregorianCalendar.from(zdt); // Java 8+GregorianCalendar.from(ZonedDateTime) est la conversion supportée. Évitez de passer par Date comme intermédiaire.
Exemple pratique
L'exemple ci-dessous illustre les deux choses que vous ferez avec Calendar dans du vrai code : lire des champs depuis une instance legacy, et faire le pont vers java.time pour toute opération non triviale.
Ce qu'il faut retenir de l'exécution :
Calendar.MONTHpour mai affiche4. Les mois basés sur zéro sont la principale source de bugs dans le code de dates legacy.getInstance(TimeZone)lie l'instance à un fuseau horaire — différent deDate, qui n'en possède pas.add(MONTH, 1)tient compte de la durée des mois ; vous obtenez le bon jour dans le mois suivant, même si les mois n'ont pas tous 30 jours.cal.toInstant().atZone(cal.getTimeZone().toZoneId())est le pont en une ligne versjava.time.GregorianCalendar.from(ZonedDateTime)vous permet de retourner unCalendarà une ancienne API sans perdre le fuseau horaire.
La suite
Cela clôt la Partie 14 — Date et Heure. La suite est la Partie 15, Multithreading et Concurrence, qui commence par Le multithreading en Java — le modèle qui a influencé toutes les autres API de ce livre.