1. Contexte historique : comment on travaillait avec les dates avant Java 8
Dans un lointain passé (avant Java 8), on utilisait les classes java.util.Date, java.util.Calendar et pour le formatage — java.text.SimpleDateFormat. C’était l’un de ces cas où les programmeurs du monde entier soupiraient à l’unisson et, en grinçant des dents, écrivaient quelque chose comme :
import java.util.Date;
Date now = new Date();
System.out.println(now); // Affichera quelque chose d'étrange comme "Wed Jun 05 14:15:22 MSK 2025"
Ça a l’air simple, mais en pratique tout n’était pas si rose. Voici seulement quelques « joies » de l’ancien API :
- Date — un objet mutable. On pouvait le modifier par inadvertance, ce qui menait souvent à des bugs.
- Les mois dans Date et Calendar commençaient à zéro (janvier — c’est 0, décembre — 11), alors que les jours commençaient à un.
- SimpleDateFormat n’était pas thread-safe : si deux threads formataient une date simultanément, on pouvait obtenir des résultats inattendus.
- Une énorme quantité de méthodes étaient annotées @Deprecated (obsolètes), et l’IDE vous bombardait constamment d’avertissements jaunes.
- La gestion des fuseaux horaires était une vraie douleur : il était facile de confondre l’heure locale et l’UTC, et mieux valait ne pas penser aux changements heure d’été/hiver.
Exemple douloureux
import java.util.Date;
Date date = new Date(2025, 5, 1); // année 2025, mois 5 (juin ?), jour 1
System.out.println(date); // Pas ce à quoi vous vous attendez !
2. Apparition de java.time : une nouvelle approche
Vers 2014, il est devenu clair que l’ancien API n’était pas seulement peu pratique — il était dangereux. C’est pourquoi un nouveau package est apparu dans Java — java.time, implémentant la spécification JSR‑310. Cet API a été inspiré par la bibliothèque populaire Joda-Time et est devenu immédiatement un standard de facto.
Paquets et classes principaux
- java.time — le package principal, où vivent toutes les nouvelles classes de date et d’heure.
- java.time.format — pour le formatage et l’analyse des dates et des heures.
- java.time.temporal — pour des opérations temporelles plus avancées.
- java.time.zone — pour travailler avec les fuseaux horaires.
Voici les principaux protagonistes du nouvel API :
| Classe | À quoi ça sert ? | Exemple d’utilisation |
|---|---|---|
|
Date seule (année, mois, jour) | Anniversaire, sans heure |
|
Heure seule (heures, minutes, secondes) | Heure d’un rendez-vous, sans date |
|
Date et heure, sans fuseau horaire | Événement local |
|
Date et heure avec fuseau horaire | Réunion à Minsk à l’heure locale |
|
Instant absolu (UTC) | Horodatage dans un journal |
|
Intervalle de temps (heures, minutes, s) | Durée d’un appel |
|
Période (années, mois, jours) | Ancienneté, âge |
Exemple : création d’une date, nouvelle manière
import java.time.LocalDate;
LocalDate today = LocalDate.now();
System.out.println(today); // Par exemple, "2025-06-05"
3. Avantages du nouvel API
Immuabilité (immutable)
Toutes les classes de java.time sont immuables. Cela signifie : si vous créez un objet LocalDate, vous ne pouvez pas le modifier. Toute opération (par exemple, ajouter un jour) renvoie un nouvel objet.
LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plusDays(1);
System.out.println(today); // 2025-06-05
System.out.println(tomorrow); // 2025-06-06
Gestion explicite des fuseaux horaires
Dans l’ancien API, il était facile d’oublier dans quel fuseau horaire se trouvait la date. Avec java.time, tout est explicite : si vous avez besoin d’un fuseau horaire — utilisez ZonedDateTime, si ce n’est pas nécessaire — utilisez 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]
Méthodes pratiques pour les calculs et les comparaisons
LocalDate today = LocalDate.now();
LocalDate nextMonth = today.plusMonths(1);
boolean isAfter = LocalDate.now().plusDays(1).isAfter(today); // true
Formatage et analyse
Le formatage et l’analyse — via DateTimeFormatter (détails — à la prochaine leçon) :
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é : comment vivre avec l’ancien code
Dans le monde réel, il faut très souvent travailler avec des bibliothèques ou des systèmes anciens qui utilisent Date et Calendar. Heureusement, le nouvel API est amical avec l’héritage : on peut convertir les anciens types en nouveaux et inversement.
Conversion 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);
Conversion 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()));
Tableau : correspondance des anciennes et des nouvelles classes
| Ancienne classe | Nouvelle classe | Commentaire |
|---|---|---|
|
|
Temps absolu |
|
|
Date et heure avec fuseau horaire |
|
|
Formatage/analyse des dates |
5. Pratique : premiers pas avec java.time
Supposons que vous ayez un utilisateur avec une date de naissance. Enregistrons et affichons cette date :
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("Nom: " + name);
System.out.println("Date de naissance: " + birthDate);
}
}
public class Main {
public static void main(String[] args) {
UserProfile user = new UserProfile("Alisa", LocalDate.of(1998, 12, 25));
user.printProfile();
}
}
Sortie :
Nom: Alisa
Date de naissance: 1998-12-25
6. Comparaison : ancien API vs nouvel API
Exemple : ajouter une semaine à la date de naissance
Ancienne méthode (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()); // Laborieux et peu évident
Nouvelle méthode (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
Avec le nouvel API, le code est plus court, plus simple et plus sûr.
7. Erreurs courantes avec java.time
Erreur n°1 : oublier que les objets sont immuables.
Si vous appelez date.plusDays(1); sans conserver le résultat, la date initiale restera inchangée.
Erreur n°2 : confusion entre LocalDate et LocalDateTime.
LocalDate ne stocke que la date (année, mois, jour), tandis que LocalDateTime stocke aussi l’heure. Ne les confondez pas si vous devez gérer les heures et les minutes.
Erreur n°3 : utiliser les anciennes classes dans de nouveaux projets.
Si possible — utilisez toujours java.time. Les anciennes classes ne sont nécessaires que pour la compatibilité.
Erreur n°4 : mauvaise gestion des fuseaux horaires.
Si vous devez stocker un événement pertinent pour différentes régions, utilisez ZonedDateTime ou au moins Instant. LocalDateTime ne contient pas d’information de fuseau horaire !
Erreur n°5 : tenter de comparer directement LocalDate et LocalDateTime.
Ce sont des types de données différents, on ne peut pas les comparer directement. Commencez par les ramener à un même type.
GO TO FULL VERSION