Comment formater les dates et nombres en Java?

Nous changeons maintenant de vitesse et parlons de comment formater des données pour les utilisateurs. Dans cette section, nous allons travailler avec des nombres, des dates et des heures. C’est particulièrement important dans la section suivante lorsque nous étendrons la personnalisation à différentes langues et locales. Vous voudrez peut-être revoir le Chapitre 4, “APIs de Base,” si vous avez besoin d’un rappel sur la création de divers objets date/heure.

Formatage des Nombres

Dans le Chapitre 4, vous avez vu comment contrôler la sortie d’un nombre en utilisant la méthode String.format. C’est utile pour les choses simples, mais parfois vous avez besoin d’un contrôle plus fin. Avec cela, nous introduisons l’interface NumberFormat, qui possède deux méthodes couramment utilisées :

public final String format(double number)
public final String format(long number)

Comme NumberFormat est une interface, nous avons besoin de la classe concrète DecimalFormat pour l’utiliser. Elle inclut un constructeur qui prend une chaîne de caractères de motif :

public DecimalFormat(String pattern)

Les motifs peuvent devenir assez complexes. Heureusement, vous n’avez besoin de connaître que deux caractères de formatage, présentés dans le Tableau 11.5.

SymboleSignificationExemples
#Omet la position si aucun chiffre n’existe pour elle.$2.2
0Met 0 dans la position si aucun chiffre n’existe pour elle.$002.20

Ces exemples devraient vous aider à comprendre comment ces symboles fonctionnent :

double d = 1234.567;
NumberFormat f1 = new DecimalFormat("###,###,###.0");
System.out.println(f1.format(d)); // 1,234.6

NumberFormat f2 = new DecimalFormat("000,000,000.00000");
System.out.println(f2.format(d)); // 000,001,234.56700

NumberFormat f3 = new DecimalFormat("Votre Solde $#,###,###.##");
System.out.println(f3.format(d)); // Votre Solde $1,234.57

La ligne 14 affiche les chiffres du nombre, arrondissant au 10ème près après la décimale. Les positions supplémentaires à gauche sont omises car nous avons utilisé #. La ligne 17 ajoute des zéros au début et à la fin pour que la sortie ait la longueur désirée. La ligne 20 montre un préfixe de caractère non formatant avec arrondi car moins de chiffres sont imprimés que disponibles. Notez que les virgules sont automatiquement supprimées si elles sont utilisées entre les symboles #.

Comme vous le verrez dans la section sur la localisation, il existe une deuxième classe concrète qui hérite de NumberFormat que vous devrez connaître.

Formatage des Dates et des Heures

Les classes de date et d’heure prennent en charge de nombreuses méthodes pour extraire des données.

LocalDate date = LocalDate.of(2022, Month.OCTOBRE, 20);
System.out.println(date.getDayOfWeek()); // JEUDI
System.out.println(date.getMonth()); // OCTOBRE
System.out.println(date.getYear()); // 2022
System.out.println(date.getDayOfYear()); // 293

Java fournit une classe appelée DateTimeFormatter pour afficher des formats standard.

LocalDate date = LocalDate.of(2022, Month.OCTOBRE, 20);
LocalTime time = LocalTime.of(11, 12, 34);
LocalDateTime dt = LocalDateTime.of(date, time);

System.out.println(date.format(DateTimeFormatter.ISO_LOCAL_DATE));
System.out.println(time.format(DateTimeFormatter.ISO_LOCAL_TIME));
System.out.println(dt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));

Le code ci-dessus imprime ce qui suit :

2022-10-20
11:12:34
2022-10-20T11:12:34

Le DateTimeFormatter lancera une exception s’il rencontre un type incompatible. Par exemple, chacun des exemples suivants produira une exception à l’exécution car il tente de formater une date avec une valeur de temps, et vice versa :

date.format(DateTimeFormatter.ISO_LOCAL_TIME); // RuntimeException
time.format(DateTimeFormatter.ISO_LOCAL_DATE); // RuntimeException

Personnalisation du Format Date/Heure

Si vous ne voulez pas utiliser l’un des formats prédéfinis, DateTimeFormatter prend en charge un format personnalisé en utilisant une chaîne de format de date.

var f = DateTimeFormatter.ofPattern("MMMM dd, yyyy 'à' hh:mm");
System.out.println(dt.format(f)); // Octobre 20, 2022 à 11:12

Analysons cela un peu. Java attribue à chaque lettre ou symbole une partie spécifique de date/heure. Par exemple, M est utilisé pour le mois, tandis que y est utilisé pour l’année. Et la casse est importante ! Utiliser m au lieu de M signifie qu’il renverra la minute de l’heure, pas le mois de l’année.

Qu’en est-il du nombre de symboles ? Le nombre dicte souvent le format de la partie date/heure. Utiliser M seul affiche le nombre minimum de caractères pour un mois, comme 1 pour janvier, tandis que MM affiche toujours deux chiffres, comme 01. De plus, utiliser MMM imprime l’abréviation de trois lettres, comme Juil pour Juillet, tandis que MMMM imprime le nom complet du mois.

Il est possible, bien qu’improbable, de rencontrer des questions utilisant SimpleDateFormat plutôt que le DateTimeFormatter plus utile. Si vous le voyez utilisé avec un ancien objet java.util.Date, sachez simplement que les formats personnalisés seront compatibles avec les deux.

Apprendre les Symboles Date/Heure Standard

Vous devriez être suffisamment familier avec les divers symboles pour pouvoir regarder une chaîne de date/heure et avoir une bonne idée de ce que sera la sortie. Le Tableau 11.6 inclut les symboles avec lesquels vous devriez être familier.

SymboleSignificationExemples
yAnnée22, 2022
MMois1, 01, Jan, Janvier
dJour5, 05
hHeure9, 09
mMinute45
SSeconde52
aa.m./p.m.AM, PM
zNom du fuseau horaireHeure Normale d’Europe Centrale, CET
ZDécalage du fuseau horaire-0400

Essayons quelques exemples. Que pensez-vous que les éléments suivants impriment ?

var dt = LocalDateTime.of(2022, Month.OCTOBRE, 20, 6, 15, 30);

var formatter1 = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss");
System.out.println(dt.format(formatter1)); // 10/20/2022 06:15:30

var formatter2 = DateTimeFormatter.ofPattern("MM_yyyy_-_dd");
System.out.println(dt.format(formatter2)); // 10_2022_-_20

var formatter3 = DateTimeFormatter.ofPattern("h:mm z");
System.out.println(dt.format(formatter3)); // DateTimeException

Le premier exemple imprime la date, avec le mois avant le jour, suivi de l’heure. Le deuxième exemple imprime la date dans un format étrange avec des caractères supplémentaires qui sont simplement affichés dans la sortie.

Le troisième exemple lance une exception à l’exécution car le LocalDateTime sous-jacent n’a pas de fuseau horaire spécifié. Si ZonedDateTime était utilisé à la place, le code s’exécuterait avec succès et imprimerait quelque chose comme 06:15 CET, selon le fuseau horaire.

Comme vous l’avez vu dans l’exemple précédent, vous devez vous assurer que le format String est compatible avec le type de date/heure sous-jacent. Le Tableau 11.7 montre quels symboles vous pouvez utiliser avec chacun des objets date/heure.

SymboleLocalDateLocalTimeLocalDateTimeZonedDateTime
y 
M 
d 
h 
m 
s 
a 
z   
Z   

Assurez-vous de savoir quels symboles sont compatibles avec quels types de date/heure. Par exemple, essayer de formater un mois pour un LocalTime ou une heure pour un LocalDate entraînera une exception à l’exécution.

Sélection d’une Méthode format()

Les classes date/heure contiennent une méthode format() qui prendra un formateur, tandis que les classes de formateur contiennent une méthode format() qui prendra une valeur date/heure. Le résultat est que l’une ou l’autre des options suivantes est acceptable :

var dateTime = LocalDateTime.of(2022, Month.OCTOBRE, 20, 6, 15, 30);
var formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss");

System.out.println(dateTime.format(formatter)); // 10/20/2022 06:15:30
System.out.println(formatter.format(dateTime)); // 10/20/2022 06:15:30

Ces instructions impriment la même valeur à l’exécution. La syntaxe que vous utilisez dépend de vous.

Ajout de Valeurs de Texte Personnalisées

Et si vous voulez que votre format inclue des valeurs de texte personnalisées ? Si vous les tapez simplement dans la chaîne de format, le formateur interprétera chaque caractère comme un symbole de date/heure. Dans le meilleur des cas, il affichera des données étranges basées sur des symboles supplémentaires que vous avez entrés. Dans le pire des cas, il lancera une exception car les caractères contiennent des symboles invalides. Aucun n’est souhaitable !

Une façon d’aborder cela serait de diviser le formateur en plusieurs plus petits et ensuite de concaténer les résultats.

var dt = LocalDateTime.of(2022, Month.OCTOBRE, 20, 6, 15, 30);
var f1 = DateTimeFormatter.ofPattern("MMMM dd, yyyy ");
var f2 = DateTimeFormatter.ofPattern(" hh:mm");
System.out.println(dt.format(f1) + "à" + dt.format(f2));

Cela imprime Octobre 20, 2022 à 06:15 à l’exécution.

Bien que cela fonctionne, cela pourrait devenir difficile si beaucoup de valeurs de texte et de symboles de date sont mélangés. Heureusement, Java inclut une solution beaucoup plus simple. Vous pouvez échapper le texte en l’entourant d’une paire de guillemets simples (‘). L’échappement de texte indique au formateur d’ignorer les valeurs à l’intérieur des guillemets simples et de les insérer simplement dans la valeur finale.

var f = DateTimeFormatter.ofPattern("MMMM dd, yyyy 'à' hh:mm");
System.out.println(dt.format(f)); // Octobre 20, 2022 à 06:15

Mais que faire si vous devez également afficher un guillemet simple dans la sortie ? Bienvenue dans le monde amusant des caractères d’échappement ! Java prend cela en charge en mettant deux guillemets simples l’un à côté de l’autre.

Nous concluons notre discussion sur le formatage des dates avec quelques exemples de formats et leurs sorties qui s’appuient sur des valeurs de texte, présentés ici :

var g1 = DateTimeFormatter.ofPattern("MMMM dd', Fête''s à' hh:mm");
System.out.println(dt.format(g1)); // Octobre 20, Fête's à 06:15

var g2 = DateTimeFormatter.ofPattern("'Format système, hh:mm: 'hh:mm");
System.out.println(dt.format(g2)); // Format système, hh:mm: 06:15

var g3 = DateTimeFormatter.ofPattern("'NOUVEAU! 'yyyy', youpi!'");
System.out.println(dt.format(g3)); // NOUVEAU! 2022, youpi!

Si vous n’échappez pas les valeurs de texte avec des guillemets simples, une exception sera lancée à l’exécution si le texte ne peut pas être interprété comme un symbole de date/heure.

DateTimeFormatter.ofPattern("L'heure est hh:mm"); // Exception lancée

Cette ligne lance une exception car L est un symbole inconnu. On pourrait également vous présenter une séquence d’échappement incomplète.

DateTimeFormatter.ofPattern("'L'heure est: hh:mm: "); // Exception lancée

L’échec de terminer une séquence d’échappement déclenchera une exception à l’exécution.