Formatage des dates en Java
Formatez des dates et heures Java en chaînes avec DateTimeFormatter et des motifs standard ou personnalisés.
Le formatage de date consiste à transformer une valeur date/heure en chaîne lisible par un humain. Ce chapitre couvre DateTimeFormatter — comment en construire un (intégré, localisé ou basé sur un motif), l'alphabet complet des motifs, la gestion des locales et des fuseaux horaires, et les pièges qui produisent une sortie incorrecte. Il fonctionne avec tous les types java.time : LocalDate, LocalTime, LocalDateTime, ZonedDateTime et Instant.
Chaque type java.time possède un toString() qui produit la représentation ISO-8601 : 2025-11-04, 14:30:00, 2025-11-04T14:30:00Z. C'est parfait pour les journaux et les échanges machine à machine. Pour l'affichage destiné aux humains (« November 4, 2025 » ou « 4 Nov, 14:30 »), vous avez besoin d'un formateur.
La classe est java.time.format.DateTimeFormatter. C'est le remplaçant moderne, thread-safe et immuable du legacy java.text.SimpleDateFormat (qui n'était pas thread-safe et causait des bugs subtils en production lorsqu'il était partagé). Mettez-en un en cache comme static final et réutilisez-le entre les threads indéfiniment — pas de synchronisation, pas de copie défensive.
Trois façons de construire un formateur
// 1. Built-in ISO formatters
DateTimeFormatter.ISO_LOCAL_DATE; // 2025-11-04
DateTimeFormatter.ISO_LOCAL_DATE_TIME; // 2025-11-04T14:30:00
DateTimeFormatter.ISO_OFFSET_DATE_TIME; // 2025-11-04T14:30:00-05:00
DateTimeFormatter.ISO_ZONED_DATE_TIME; // 2025-11-04T14:30:00-05:00[America/New_York]
DateTimeFormatter.ISO_INSTANT; // 2025-11-04T19:30:00Z
DateTimeFormatter.BASIC_ISO_DATE; // 20251104
// 2. Localised formatters
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG); // November 4, 2025 (en-US)
DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM); // Nov 4, 2025, 2:30:00 PM
// 3. Pattern-based formatters
DateTimeFormatter.ofPattern("dd MMM yyyy"); // 04 Nov 2025
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm zzz"); // 2025-11-04 14:30 ESTL'API de motifs est celle que vous utiliserez le plus. La version localisée est appropriée lorsque vous avez besoin d'un format adapté à une culture et que vous souhaitez que le JDK choisisse la mise en page pour vous.
Formatage
La forme d'appel est symétrique des deux côtés :
String s = formatter.format(temporal);
String s2 = temporal.format(formatter); // same thing, fluent styleLes deux fonctionnent. La plupart du code utilise la forme fluide.
LocalDate today = LocalDate.now();
String us = today.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")); // 11/04/2025
String iso = today.format(DateTimeFormatter.ISO_LOCAL_DATE); // 2025-11-04
String eu = today.format(DateTimeFormatter.ofPattern("dd.MM.yyyy")); // 04.11.2025L'alphabet des motifs
Le grand tableau — celui auquel vous reviendrez. Les lettres sont sensibles à la casse et le nombre de répétitions compte.
| Lettre | Signification | Exemple |
|---|---|---|
y | année | y → 2025, yy → 25, yyyy → 2025 |
M | mois | M → 11, MM → 11, MMM → Nov, MMMM → November |
d | jour du mois | d → 4, dd → 04 |
E | jour de la semaine | E → Tue, EEEE → Tuesday |
H | heure 0-23 | H → 14, HH → 14 |
h | heure 1-12 | h → 2, hh → 02 (à utiliser avec a) |
a | AM/PM | a → PM |
m | minute | m → 5, mm → 05 |
s | seconde | s → 9, ss → 09 |
S | fraction de seconde | SSS → 123 (millis) |
n | nanoseconde | nnnnnnnnn → 123456789 |
z | nom du fuseau horaire | z → EST, zzzz → Eastern Standard Time |
Z | décalage de fuseau horaire | Z → -0500, ZZ → -0500, ZZZZ → GMT-05:00 |
X | décalage ISO | X → -05, XX → -0500, XXX → -05:00 |
V | ID de fuseau horaire | VV → America/New_York |
Le texte littéral s'encadre entre guillemets simples :
DateTimeFormatter.ofPattern("EEEE, MMMM d 'at' h:mm a"); // Tuesday, November 4 at 2:30 PMPour un guillemet simple littéral, utilisez-en deux : ''.
La paire la plus source de confusion est m vs M (minuscule = minute, majuscule = mois) et H vs h (majuscule = 0-23, minuscule = 1-12). La plupart des bugs du type « l'heure est décalée de quelque chose de bizarre » proviennent d'une de ces fautes de frappe.
Localisation : Locale et withLocale
Un formateur utilise la locale par défaut de la JVM sauf indication contraire. Pour une sortie « toujours en anglais » ou « toujours en allemand », fixez la locale :
DateTimeFormatter english = DateTimeFormatter.ofPattern("EEEE, MMMM d", Locale.US);
DateTimeFormatter german = DateTimeFormatter.ofPattern("EEEE, d. MMMM", Locale.GERMAN);
DateTimeFormatter french = DateTimeFormatter.ofPattern("EEEE d MMMM", Locale.FRENCH);
today.format(english); // Tuesday, November 4
today.format(german); // Dienstag, 4. November
today.format(french); // mardi 4 novembrePour le contenu rendu côté serveur, passez toujours une locale. La « locale par défaut de la JVM » est imprévisible sur les serveurs de production et est la source des bugs du type « ça marche sur ma machine, mais pas sur le serveur ».
Affichage du fuseau horaire
ZonedDateTime et Instant sont les seuls types qui disposent d'informations de fuseau horaire. Formater un LocalDateTime avec un motif incluant z ou Z lève une exception — il n'y a pas de fuseau à afficher. Convertissez d'abord :
ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/New_York"));
zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z")); // 2025-11-04 14:30 ESTPour Instant, le formateur a également besoin d'un fuseau — Instant n'en a pas, donc les formateurs d'affichage incluant des champs dépendant du fuseau horaire nécessitent un withZone :
DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
.withZone(ZoneId.of("America/New_York"));
f.format(Instant.now()); // formatter supplies the zone for displaySans withZone, le formatage d'un Instant avec un motif de forme calendaire lève une exception.
Formateurs stylisés avec FormatStyle
Les fabriques localisées vous donnent quatre tailles canoniques :
DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT); // 11/4/25 (en-US), 04.11.25 (de-DE)
DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); // Nov 4, 2025
DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG); // November 4, 2025
DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL); // Tuesday, November 4, 2025Ces quatre tailles existent également pour ofLocalizedTime et ofLocalizedDateTime. Utilisez-les lorsque vous souhaitez que la mise en page suive la locale de l'utilisateur plutôt que d'imposer une forme fixe. Combinez-les avec .withLocale(...) pour fixer la locale.
Exemple concret : une date, six variantes d'affichage
Le programme ci-dessous formate un seul ZonedDateTime de six façons courantes : ISO pour les journaux machine, 12 heures américain pour les utilisateurs anglophones, 24 heures européen pour les utilisateurs germanophones, une forme localisée longue, un motif personnalisé avec texte littéral intégré, et un formateur Instant-via-withZone pour les horodatages bruts.
Ce qu'il faut retenir de l'exécution :
- Les champs
static final DateTimeFormattermis en cache ont la bonne forme.DateTimeFormatterest immuable et thread-safe ; créer une instance est peu coûteux mais pas gratuit, et réutiliser la même instance partout est le pattern recommandé par le JDK. Ne construisez pas un nouveau formateur dans une boucle chaude. - Le même
ZonedDateTimea produit six chaînes différentes selon le formateur. L'objet valeur n'a jamais changé ; le formateur est le seul élément qui contrôle la mise en page. C'est la séparation pour laquelleDateTimeFormatterexiste — gardez le type valeur propre, déléguez la présentation au formateur. - Le bloc « fautes de frappe courantes » a affiché
14:11pourHH:MMparce queMest le mois, pas la minute. Ces deux lettres sont la paire la plus source de confusion dans l'alphabet des motifs. Si l'heure affichée ressemble étrangement à un composant de date, vérifiez la casse dans le motif. - L'échelle
FormatStylea produit quatre chaînes de longueur croissante. UtilisezFormatStyle.MEDIUMcomme valeur par défaut sensée pour « afficher une date à un utilisateur sans trop réfléchir » ;LONGetFULLpour les contextes où l'année et le jour de la semaine doivent être sans ambiguïté ;SHORTpour les espaces d'interface restreints. LocalDateTimeavec un motif incluant un fuseau horaire a levé une exception — le formateur a besoin de données de fuseau horaire, etLocalDateTimen'en a pas. Le correctif consiste à convertir (ldt.atZone(zone)) ou à supprimer le champ lié au fuseau du motif. Dans tous les cas, le mode d'échec est clair à l'exécution.
Et ensuite
Le formatage est la direction valeur → chaîne. Le chapitre suivant, Analyse des dates Java, est l'inverse — chaîne → valeur — en utilisant les mêmes motifs DateTimeFormatter et les mêmes mises en garde. Les deux ensemble constituent la frontière d'E/S pour tout code qui échange des dates avec des utilisateurs, des configurations, des journaux ou des API distantes.