Gestion et affichage des dates et des nombres

Chapitres traités   

Dans cette étude, nous nous intéressons à l'affichage des informations suivant le pays où nous nous situons, ce qui s'appelle l'internationalisation. Les valeurs critiques sur ce sujet sont l'affichage des dates ainsi que les valeurs monétaires. Nous allons donc utiliser des classes spécialisées qui vont nous permettre de gérer l'affichage de ces informations comme nous le désirons.


Je profite de cette étude, au dela de l'affichage, comment mettre en oeuvre une date de façon correcte, et comment la gérer ultérieurement.
§

Choix du chapitre Internationalisation

La machine virtuelle Java permet de développer du code qui s'exécute de la même façon quelque soit la plate-forme Java. Une question fondamentale reste posée : le contenu et les données de l'application seront-ils compréhensibles dans le monde entier ? Vos utilisateurs doivent-ils parler l'anglais pour pouvoir utiliser votre application ? La réponse est que Java offre un support complet à l'adaptation des composants linguistiques de votre application, pour les langues et les dialectes les plus courants.

Affichage des nombres

Si vous examinez une application adaptée pour le marché international, la différence la plus évidente à vos yeux est la langue. Les menus, les intitulés de boutons et les messages devront alors être traduits dans la langue locale. Il existe toutefois bien d'autres différences plus subtiles ; les nombres, par exemple, ont un format différent en anglais et en français.

Ainsi, le nombre en anglais :

123,456.78

s'écrit en français :

123 456,78

En réalité, les rôles du point et de la virgule décimale sont inversés !

Affichage de la date

Des variations similaires existent dans l'affichage des dates. Aux Etats-Unis, les dates sont assez bizarrement affichées sous la forme mois/jour/année. La France, par exemple, utilise l'ordre plus logique de jour/mois/année, alors qu'en Chine on écrira année/mois/jour.

Par conséquent, la date au format américain :

3/22/61

se présente en français sous la forme suivante :

22/03/1961

Bien entendu, si les noms de mois sont écrits explicitement, la différence entre les deux langues devient évidente. L'anglais :

March 22, 1961

se présente en français sous la forme suivante :

22 mars 1961

La classe Locale

La programmation d'une internationalisation trouve sa solution dans la classe Locale. La classe proprement dite est très simple ; elle encapsule le code d'un pays, le code d'un langage, et un code de variante rarement utilisé.

Par exemple, aux Etats-Unis, vous utiliserez un paramètre régional avec :

language=English, location=United States.

La suisse a quatre langues officielles (l'allemand, le français, l'italien et le romanche). Un germanophone en suisse utilisera :

language=German, location=Switzerland.

Codes de la norme ISO

Pour exprimer la langue et l'emplacement géographique d'une manière concise et standardisée, le langage Java utilise des codes définis par la norme ISO. La langue locale est exprimée sous la forme d'un code de deux lettres minuscules, conforme à la norme ISO-639 et le code pays est exprimé sous la forme d'un code de deux lettres majuscules conforme à la norme ISO-3166.

Langue Code (langue) Pays Code (pays)
Allemand de Allemagne DE
Anglais en Autriche AT
Chinois zh Belgique BE
Coréen ko Canada CA
Danois da Chine CN
Espagnol sp Corée KR
Français fr Danemark DK
Finnois fi Espagne ES
Grec el Etats-Unis US
Hollandais nl Finlande FI
Italien it Grande-Bretagne GB
Japonais ja Grèce GR
Norvégien no Irlande IE
Potugais pt Italie IT
Suédois sv Japon JP
Turc tr Norvège NO
  Pays-Bas NL
  Portugal PT
  Suède SE
  Suisse CH
  Taiwan TW
  Turquie TR

Implémentation de la classe Locale

Pour décrire un paramétrage régional, vous concaténez la langue, le code pays et la variante éventuelle et passez cette chaîne au constructeur de la classe Locale.

Locale allemand = new Locale("de");
Locale allemandAllemagne = new Locale("de", "DE");
Locale allemandSuisse = new Locale("de", "CH");

Pour vous faciliter la tâche, le JDK prédéfini un certain nombre de paramètres régionaux de pays. Ainsi, les langues et les pays couramment utilisés sont définis sous la forme de constantes dans la classe Locale.

Pays Langue
Locale.CANADA Locale.CHINESE
Locale.CANADA_FRENCH Locale.ENGLISH
Locale.CHINA Locale.FRENCH
Locale.FRANCE Locale.GERMAN
Locale.GERMANY Locale.ITALIAN
Locale.ITALY Locale.JAPANESE
Locale.JAPAN Locale.KOREAN
Locale.KOREA Locale.SIMPLIFIED_CHINESE
Locale.PRC Locale.TRADITIONAL_CHINESE
Locale.TAIWAN  
Locale.UK  
Locale.US  

Méthodes de la classe Locale

La classe Locale dispose d'un certain nombre de méthodes qui nous fournissent les renseignement nécessaires sur les paramètres régionaux.

Méthode Desciption
getDefault() Renvoie les paramètres régionaux par défaut.
getDisplayName() Renvoie un nom décrivant les paramètres régionaux, exprimé dans la langue locale courante.
getLanguage() Renvoie le code langue, sour la forme du code ISO-639 en minuscule sur deux lettres.
getDisplayLanguage() Renvoie le code de la langue, exprimé dans la langue locale courante.
getCountry() Renvoie le coe pays, sous la forme du code ISO-3166 en majuscules sur deux lettres.
getDisplayCountry() Renvoie le nom du pays, exprimé dans la langue locale courante.

La méthode statique getDefault() de la classe Locale extrait initialement les paramètres régionaux par défaut stockés dans le système d'exploitation.

Lorsque vous avez vos paramètres régionaux, que pouvez-vous en faire ? En réalité pas grand chose. Les seules méthodes utiles dans la classe Locale sont celles permettant d'identifier les codes de langue et de pays. La plus importante est la méthode getDisplayName(). Elle renvoie une chaîne décrivant les paramètres régionaux. Cette chaîne ne contient pas les codes ISO sur deux lettres, mais elle se présente sous une forme compréhensible par l'utilisateur.

Résultats

Par défaut :
FR
France
fr
français
français (France)

Pour l'Italie :
IT
Italie
it
italien
italien (Italie)

 
package test;

import java.util.Locale;

public class Principal {
   public static void main(String[] args) {
      // paramètres régionaux par défaut
      Locale français = Locale.getDefault();
      System.out.println("Par défaut :");	
      System.out.println(français.getCountry());
      System.out.println(français.getDisplayCountry());
      System.out.println(français.getLanguage());
      System.out.println(français.getDisplayLanguage());
      System.out.println(français.getDisplayName()+"\n");
      
      // paramètres régionaux par défaut
      Locale italie = Locale.ITALY;
      System.out.println("Pour l'Italie :");
      System.out.println(italie.getCountry());
      System.out.println(italie.getDisplayCountry());
      System.out.println(italie.getLanguage());
      System.out.println(italie.getDisplayLanguage());
      System.out.println(italie.getDisplayName());      
   }
}

 

Choix du chapitre Formater les nombres pour l'affichage

Le paquetage java.text contient notamment un jeu de classes conçues pour générer et analyser des chaînes représentant des objets. Dans ce paragraphe, nous nous intéressons à la classe NumberFormat qui est spécialisée dans l'affichage des nombres. Il existe toutefois d'autres classes spécialisée qui formate des objets sous forme de texte. Il s'agit des classes ChoiceFormat (texte personnalisé), MessageFormat (message paramétré) et DateFormat (affichage de date).

Formatage d'un texte dans le système de présentation adapté à votre choix

La classe NumberFormat peut donc être utilisée pour formater et analyser des devises, des pourcentages ou simplement de bons vieux nombres suivant le pays où nous nous situons ou suivant le pays que nous désirons représenter. En réalité, la classe NumberFormat est une classe abstraite, mais elle contient plusieurs méthodes de fabrication utiles, qui produisent des modules de formatage de nombre de différents types. Le tableau ci-dessous nous précise les méthodes que nous pouvons utiliser directement à partir de la classe NumberFormat :

Méthode Desciption
getCurrencyInstance() Renvoie un texte formaté et analysé suivant une valeur monétaire.
getCurrencyInstance(Locale pays) Renvoie un texte formaté et analysé suivant une valeur monétaire en précisant le pays.
getInstance() Renvoie un texte formaté et analysé suivant une valeur numérique réelle en plaçant des séparateurs (espaces pour la France) pour les milliers ainsi que le séparateur entre la valeur entière et la valeur décimale (virgule pour la France).
getInstance(Locale pays) Renvoie un texte formaté et analysé suivant une valeur numérique réelle en plaçant des séparateurs (espaces pour la France) pour les milliers ainsi que le séparateur entre la valeur entière et la valeur décimale (virgule pour la France). Ici, nous choisissons la localité.
getNumberInstance() Renvoie un texte formaté et analysé suivant une valeur numérique réelle en plaçant des séparateurs (espaces pour la France) pour les milliers ainsi que le séparateur entre la valeur entière et la valeur décimale (virgule pour la France).
getNumberInstance(Locale pays) Renvoie un texte formaté et analysé suivant une valeur numérique réelle en plaçant des séparateurs (espaces pour la France) pour les milliers ainsi que le séparateur entre la valeur entière et la valeur décimale (virgule pour la France). Ici, nous choisissons la localité.
getIntegerInstance() Renvoie un texte formaté et analysé suivant une valeur numérique entière en plaçant des séparateurs (espaces pour la France) pour les milliers.
getPercentInstance() Renvoie un texte formaté et analysé suivant une valeur en pourcentage.
getPercentInstance(Locale pays) Renvoie un texte formaté et analysé suivant une valeur en pourcentage en précisant le pays.
setMinimumFractionDigits(int n) Précise le nombre minimal de décimales que l'on doit afficher. Des zéros peuvent compléter au cas où.
setMaximumFractionDigits(int n) Précise le nombre maximal de décimales que l'on peut afficher.
setMinimumIntegerDigits(int n) Précise le nombre minimal de chiffres de la partie entière que l'on doit afficher. Des zéros peuvent compléter au cas où.
setMaximumIntegerDigits(int n) Précise le nombre maximal de chiffres de la partie entière que l'on peut afficher.
format(double x)
format(long x)
Renvoient la chaîne de caractère résultant du formatage du nombre entier ou du nombre en virgule flottante.
Number parse(String valeur) Effectue l'opération inverse des méthodes précédentes et renvoie un nombre à partir de la chaîne formatée suivant la localité saisie. La classe Number encapsule les différents types primitifs. Il suffit alors de prendre la méthode adaptée de cette classe pour retrouver le type que vous désirez : byteValue(), doubleValue(), floatValue(), intValue() et longValue().

Pour réaliser votre traitement, vous devez suivre une certaine démarche. Vous précisez d'abord le type de formatage que vous désirez réaliser en faisant appel à une des méthodes concernées par le traitement souhaité : getCurrencyInstance(), getNumberInstance() et getPercentInstance(). Vous pouvez ensuite spécifier le nombre de chiffre avant et après la virgule que vous souhaitez également prendre en compte à l'aide des méthodes setXXX(). Enfin, pour terminer, vous effectuez le traitement au moyen de la méthode format().

Résultats

----------------------------- En France
Valeur en Franc : 1 234,89 €
Valeur en pourcentage : 78%
Valeur réelle : 1 234,89
Valeur réelle : 1 234,89
------------------------ Aux Etats-Unis
Valeur en Franc : $1,234.89
Valeur en pourcentage : 78%
Valeur réelle : 1,234.89
Valeur réelle : 1,234.89
---------- Affichage de valeurs réelles
0 000 000 012,457
0 000 001 238,500
0 000 789 456,790

 
package test;

import java.text.NumberFormat;
import java.util.Locale;

public class Principal {
   public static void main(String[] args) {
      // Valeurs numériques gérées et affichées pour la France
      final double salaire = 1234.89;
      final double taux = 0.775;
      System.out.println("----------------------------- En France");
      System.out.println("Valeur en Franc : "+NumberFormat.getCurrencyInstance().format(salaire));
      System.out.println("Valeur en pourcentage : "+NumberFormat.getPercentInstance().format(taux));
      System.out.println("Valeur réelle : "+NumberFormat.getInstance().format(salaire));
      System.out.println("Valeur réelle : "+NumberFormat.getNumberInstance().format(salaire));

      // Valeurs numériques gérées et affichées pour les Etats-Unis
      final Locale us = Locale.US;
      System.out.println("------------------------ Aux Etats-Unis");
      System.out.println("Valeur en Franc : "+NumberFormat.getCurrencyInstance(us).format(salaire));
      System.out.println("Valeur en pourcentage : "+NumberFormat.getPercentInstance(us).format(taux));
      System.out.println("Valeur réelle : "+NumberFormat.getInstance(us).format(salaire));
      System.out.println("Valeur réelle : "+NumberFormat.getNumberInstance(us).format(salaire));

      // Gestion de l'affichage nombres décimaux
      NumberFormat réels = NumberFormat.getInstance();
      réels.setMinimumFractionDigits(3);
      réels.setMinimumIntegerDigits(10);
      double[] valeurs = {12.4569, 1238.5, 789456.789654};
      System.out.println("---------- Affichage de valeurs réelles");
      for (double valeur: valeurs) 
         System.out.println(réels.format(valeur));
   }
}

Retrouver une valeur primitive à partir d'un texte formaté dans le pays en cours

Inversement, pour lire un nombre qui a été saisie ou stocké avec les conventions d'un certain paramétrage régional, nous pouvons alors utiliser la méthode parse(). Effectivement, la méthode parse() peut traiter les points, les virgules décimales, ainsi que les chiffres dans d'autres langues ou dans le système de présentation sélectionné, et finalement retourner la valeur numérique dans le type primitif choisi.

Si le texte du nombre n'a pas la forme correcte, la méthode lance une ParseException.
.

Vous avez ci-dessous un programme qui récupére une valeur en €uro saisie au clavier et la transforme en une valeur réelle équivalente :

Résultats

Premier essai :

Valeur en €uro ?
1235,45 €
Valeur réelle correspondante : 1235.45
Valeur en €uro : 1 235,45 €

Deuxième essai :

Valeur en €uro ?
1 235,45 €
Attention : 1 235,45 € n'est pas une bonne valeur

 
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);     
      System.out.println("Valeur en €uro ?");
      String saisie = clavier.nextLine();
      NumberFormat nombre = NumberFormat.getCurrencyInstance();
      try {
         double valeur = nombre.parse(saisie.trim()).doubleValue();
         System.out.println("Valeur réelle correspondante : "+valeur);
         System.out.println("Valeur en €uro : "+nombre.format(valeur));
      } 
      catch (ParseException ex) {
         System.err.println("Attention : "+saisie+" n'est pas une bonne valeur");
      }
   }
}

Attention : cette méthode parse() est spécialement concue pour être utiliser après une saisie réalisée par l'opérateur. Du coup, il ne faut surtout pas placer des espaces quand vous effectuez la saisie dans la valeur monétaire de votre choix. Je rappelle que la méthode trim() de la classe String permet de supprimer les espaces que nous aurions pu saisir avant la valeur elle-même.

Format de nombre personnalisé

Il existe une classe DecimalFormat qui est la classe concrète de type Format utilisée par NumberFormat avec tous les paramètres locaux qui utilisent des nombres en base 10. Cette classe permet de personnaliser à notre convenance le format du nombre à afficher.

La plupart des applications n'ont pas besoin d'utiliser cette classe de manière directe. En effet, comme nous l'avons vu plus haut, elles peuvent utiliser les méthodes statiques de NumberFormat, comme getInstance(), getCurrencyInstance(), etc. qui délivrent, en réalité, des objets de type DecimalFormat. Ensuite, vous pouvez effectuer sur ces objets des personnalisations mineures indépendantes de ces paramètres.

Toutefois, il arrive que nous ayons besoin de réaliser un formatage et une analyse de nombres de façon totalement personnalisé. Dans ce cas là, il est plutôt judiceux de prendre directement un objet DecimalFormat en transmettant un motif adapté au constructeur de la classe. Plus tard, nous pouvons modifier le motif proposé à l'aide de la méthode applyPattern().

Voici ci-dessous le tableau de caractères utilisés pour la création du motif désiré :

Caractère Signification
# Un chiffre ; les zéros ne sont pas représentés.
0 Un chiffre ; les zéros sont représentés par 0.
. Le séparateur décimal selon les paramètres locaux (point).
, Le séparateur des milliers selon les paramètres locaux (virgule).
- Le préfixe négatif selon les paramètres locaux (moins).
% Affiche les valeurs sous forme de pourcentages.
; Sépare le format des nombres positifs (à gauche) du format optionnel appliqué aux nombres négatifs (à droite).
' Les apostrophes simples représente un caractère réservé, aussi apparaît-il de manière littérale dans le résultat (apostrophe). Il faut donc placer votre texte entre apostrophes.
autre Apparaît littéralement dans le résultat.

Voici un exemple qui permet de représenter une monnaie en Franc. Dans ce programme, vous saisissez la valeur en €uro. Vous avez ensuite l'affichage de la valeur saisie en €uro suivi de la même valeur mais transformée en Franc.

Résultats

Valeur en €uro ?
1201 €
Valeur en €uro : 1 201,00 €
Valeur en franc : 7 878,26 F

 
import java.text.*;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);     
      System.out.println("Valeur en €uro ?");
      String saisie = clavier.nextLine();
      NumberFormat nombre = NumberFormat.getCurrencyInstance();
      try {
         double valeur = nombre.parse(saisie.trim()).doubleValue();
         DecimalFormat franc = new DecimalFormat("#,##0.00 F");
         System.out.println("Valeur en €uro : "+nombre.format(valeur));
         System.out.println("Valeur en franc : "+franc.format(valeur*6.55975));
      } 
      catch (ParseException ex) {
         System.err.println("Attention : "+saisie+" n'est pas une bonne valeur");
      }
   }
}

 

Choix du chapitre Création et gestion des dates

Dans ce chapitre, nous allons voir comment créer des dates et les gérer au delà de leurs affichages. Travailler avec la date et l'heure sans outils appropriés peut s'avérer très laborieux. Depuis Java 1.1, trois classes sont chargées du travail pénible. La classe java.util.Date encapsule un instant de temps. La classe GregorianCalendar, qui descent de la classe abstraite java.util.Calendar, fait la conversion entre un instant de temps et des champs comme le mois, le jour, et l'année. Enfin, la classe java.text.DateFormat (ainsi d'ailleurs que java.text.SimpleDateFormat) sait générer et analyser des représentations pour l'affichage de dates et heures sous forme de chaîne de caractères. L'affichage des dates sera traitée lors du chapitre suivant.

Millisecondes - System.currentTimeMillis() et nanosecondes - System.nanoTime()

Java permet de représenter et de manipuler les dates et les heures sous trois formes : avec des valeurs long, avec des objets java.util.Date ou avec des objets java.util.Calendar. Au niveau le plus bas, les dates et les heures sont représentées avec un long contenant le nombre positif ou négatif de millisecondes écoulées depuis minuit le 1 janvier 1970.

Cette date et heure particulière porte le nom anglais "epoch" (c'est-à-dire, époque ou période en français) et se mesure en temps GMT (Greenwich Mean Time) ou UTC (Universal Time).

Pour obtenir l'heure actuelle dans cette représentation en millisecondes, utilisez la méthode currentTimeMillis() de la classe System :

long maintenant = System.currentTimeMillis();

A partir de java 5.0, vous pouvez utiliser la méthode nanoTime() de la classe System afin d'obtenir l'heure en nanosecondes. Cette méthode retourne un nombre long de nanosecondes. Contrairement à la méthode currentTimeMillis(), la méthode nanoTime() ne retourne pas le temps relatif à une époque prédéfinie. nanoTime() se prète donc bien à la mesure du temps relatif ou écoulé, mais pas à celle du temps absolue (pour autant que le laps de temps ne dépasse pas 292 ans).

Résultat

Temps écoulé : 2 186 546 055 ns

public static void main(String[] args)  {
  long début = System.nanoTime();
  for (int i=0; i<Integer.MAX_VALUE; i++);
  long fin = System.nanoTime();
  long tempsEcoulé = fin - début;
  System.out.println("Temps écoulé : "+NumberFormat.getInstance().format(tempsEcoulé)+" ns");
}

La classe Date

La classe java.util.Date est une encapsulation d'une valeur long contenant le nombre de millisecondes écoulées depuis le 1 janvier 1970 à minuit, heure GMT. L'utilisation d'un objet Date plutôt que l'une des valeurs long permet d'obtenir une chaîne de caractères non localisée avec la méthode toString().

Egalement, deux objets Date peuvent être comparés avec la méthode equals() ou ordonnés avec les méthodes compareTo(), before() et after(). La version sans argument du constructeur de Date crée un objet représentant l'heure courante. Vous pouvez aussi passer un nombre long de millisecondes pour créer un objet Date représentant une heure différente. Puisque, nous sommes toujours sur les dates en millisecondes, la méthode getTime() retourne la représentation en millisecondes de l'objet Date actuel.

La classe Date possède un certain nombre de méthodes pour lire et définir l'année, le mois, le jour, l'heure, la minute et la seconde de la date enregistrée. Toutes ces méthodes sont cependant devenues obsolètes, au profit de la classe Calendar décrite au prochain paragraphe.

Résultats

Fri Feb 16 07:11:26 CET 2007
Fri Feb 16 07:11:26 CET 2007
Comparaison : 0
Egalité : true
Avant : true
Après : false
Temps écoulé : 2 203 ms

 
import java.text.NumberFormat;
import java.util.Date;
import static java.lang.System.*;

public class Principal {
   public static void main(String[] args) {
      long now = System.currentTimeMillis();
      Date maintenant = new Date();
      Date date = new Date(now);
      out.println(maintenant);
      out.println(date);
      for (int i=0; i<Integer.MAX_VALUE; i++);
      Date après = new Date();
      out.println("Comparaison : "+maintenant.compareTo(date));
      out.println("Egalité : "+maintenant.equals(date));
      out.println("Avant : "+maintenant.before(après));
      out.println("Après : "+maintenant.after(après));
      long tempsEcoulé = après.getTime() - now; 
      out.println("Temps écoulé : "+NumberFormat.getInstance().format(tempsEcoulé)+" ms");
   }
}

Les classes Calendar et GregorianCalendar

La classe java.util.Calendar est une classe abstraite qui définit des méthodes qui utilisent l'arithmétique des dates et des heures. Elle inclut également des méthodes convertissant les dates et les heures dans un format machine basé sur les millisecondes et vice-versa. Par ailleurs, cette classe travaille avec des unités plus utiles pour l'être humain, par exemple avec les minutes, les heures, les jours, les semaines, les mois et les années.

En tant que classe abstraite, la classe Calendar ne peut pas être directement instanciée. Par contre, elle propose des méthodes statiques getInstance() qui retournent des instances d'une sous-classe de Calendar (généralement GregorianCalendar) concues pour être utilisées selon les paramètres locaux par défaut ou spécifiés, dans le fuseau horaire désiré.

GregorianCalendar est une sous-classe concrète de la classe Calendar. Elle implémente le calendrier standard, avec les années comptées depuis la naissance du Christ. Ce calendrier est le plus utilisé à travers le globe. En réalité, mise à part ses constructeurs, cette classe concrète GregorianCalendar propose très peu de nouvelles méthodes par rapport à la classe abstraite Calendar. Finalement, nous pouvons tout-à-fait utiliser directement cette dernière sans passer par GregorianCalendar.

Il existe une discontinuité dans le calendrier grégorien qui représente le passage historique entre calendrier julien et le calendrier grégorien. Par défaut, la classe GregorianCalendar présuppose que ce changement a eu lieu le 15 octobre 1582. La plupart des programmes ne doivent cependant pas être concerné par ce changement.

Nous pourrions raisonnablement définir d'autres classes filles de Calendar, comme par exemple JulianCalendar ou encore LunarCalendar. Si le coeur vous en dit, ne vous génez pas !

Calendar définit un certain nombre de constantes utiles. Certaines constantes représentent les jours de la semaine et les mois de l'année. D'autres constantes, par exemple HOUR et DAY_OF_WEEK, représentent divers champs constitutifs d'une date ou d'une heure. Ces constantes sont transmises à plusieurs méthodes de la classe Calendar, telles que get() et set(), afin d'indiquer quel champ particulier d'une date ou d'une heure est concernée.

Voici ci-dessous le tableau représentant les attributs et les méthodes de la classe Calendar :

Contantes publiques Desciption
JUNUARY...DECEMBER Les mois de l'année.
MONDAY...SUNDAY Les jours de la semaine.
DATE La date.
YEAR L'année.
MONTH Le mois.
HOUR L'heure.
MINUTE La minute.
SECOND La seconde.
MILLISECOND La milliseconde.
DAY_OF_MONTH Le jour du mois.
DAY_OF_WEEK Le jour de la semaine.
DAY_OF_WEEK_IN_MONTH Le jour de la semaine dans le mois.
DAY_OF_YEAR Le jour de l'année.
HOUR_OF_DAY L'heure du jour.
WEEK_OF_MONTH La semaine dans le mois.
WEEK_OF_YEAR La semaine dans l'année.
Méthodes Description
Calendar getInstance() Construit un objet GregorianCalendar qui correspond à la date de l'instant présent.
add(int champ, int décalage) Propose un décalage par rapport à la date en cours. Avec le paramètre champ, vous placez une des constantes définies ci-dessus qui correspondra au type de décalage souhaité, comme le mois, l'année, le jour, etc.. Avec le paramètre décalage, vous indiquez la valeur du décalage.
boolean after(Object quand) Détermine si la date en cours se trouve après celle présicée en argument de la méthode.
boolean before(Object quand) Détermine si la date en cours se trouve avant celle présicée en argument de la méthode.
int get(int champ) Retourne une valeur entière correspondant au critère d'évaluation. Avec le paramètre champ, vous placez une des constantes définies ci-dessus qui correspondra au critère souhaité, comme le mois, l'année, le jour, etc.
int getActualMaximum(int champ) Retourne la valeur entière maximale suivant le critère d'évaluation souhaité. Avec le paramètre champ, vous placez une des constantes définies ci-dessus qui correspondra au critère souhaité, comme le mois, l'année, le jour, etc.
int getActualMinimum(int champ) Retourne la valeur entière minimale suivant le critère d'évaluation souhaité. Avec le paramètre champ, vous placez une des constantes définies ci-dessus qui correspondra au critère souhaité, comme le mois, l'année, le jour, etc.
int getGreatestMinimum(int champ) Retourne une valeur entière correspondant au plus grand des minimaux suivant le critère d'évaluation souhaité. Avec le paramètre champ, vous placez une des constantes définies ci-dessus qui correspondra au critère souhaité, comme le mois, l'année, le jour, etc.
int getLeastMaximum(int champ) Retourne une valeur entière correspondant au plus petit des maximaux suivant le critère d'évaluation souhaité. Avec le paramètre champ, vous placez une des constantes définies ci-dessus qui correspondra au critère souhaité, comme le mois, l'année, le jour, etc.
Date getTime() Retourne un objet Date correspondant au calendrier. Méthode très utile pour la gestion de l'affichage prévu par la classe DateFormat.
long getTimeInMillis() Retourne l'heure en millisecondes. Ce long représente le temps écoulé depuis minuit le 1er janvier 1970.
set(int champ, int valeur) Change la valeur d'un élément de la date. Le type d'élément à changer doit être précisé avec le paramètre champ à l'aide d'une des constantes définit plus haut.
set(int année, int mois, int jour) Change la date actuelle.
set(int année, int mois, int jour, int heure, int minute) Change la date actuelle.
set(int année, int mois, int jour, int heure, int minute, int seconde) Change la date actuelle.
setTime(Date date) Change la date actuelle.
setTimeInMillis(long dateEnMilliseconde) Change la date actuelle.
Contructeurs de GregorianCalendar Description
GregorianCalendar(int année, int mois, int jour) Construit une date suivant les valeurs précisées en argument à minuit.
GregorianCalendar(int année, int mois, int jour, int heure, int minute) Construit une date suivant les valeurs précisées en argument.
GregorianCalendar(int année, int mois, int jour, int heure, int minute, int seconde) Construit une date suivant les valeurs précisées en argument à la seconde près.
GregorianCalendar() Construit la date de l'instant présent.

La méthode add() ajoute (ou retranche) des valeurs de champ calendaire, comme les jours, les mois, les années, les heures, etc. Par ailleurs, elle est suffisament compétente pour incrémenter le champ supérieur lorsque le champ affecté atteint sa valeur maximale. Les méthodes before() et after() comparent deux objets Calendar.

Beaucoup de méthodes de la classe Calendar remplacent des méthodes de la classe Date rendues obsolètes depuis Java 1.1.
.

Résultats

Fri Feb 16 12:19:09 CET 2007
Jour du mois : 16
Jour de la semaine : 6
Jour de l'année : 47
Numéro de la semaine : 7
Année : 2007
Mois : 1
Heure : 12
Minutes : 19
Nombre de jours dans le mois : 28
Année bissextile ? false
Ajouter 5 mois et retrancher 7 jours :
    Mon Jul 09 12:19:09 CEST 2007
Changer l'année :
    Sun Jul 09 12:19:09 CEST 2000
Sat Aug 12 00:00:00 CEST 2000

 
import java.util.*;
import static java.lang.System.*;

public class Principal {
   public static void main(String[] args) {
      Calendar date = Calendar.getInstance();
      out.println(date.getTime());
      out.println("Jour du mois : "+date.get(date.DAY_OF_MONTH));
      out.println("Jour de la semaine : "+date.get(date.DAY_OF_WEEK));
      out.println("Jour de l'année : "+date.get(date.DAY_OF_YEAR));
      out.println("Numéro de la semaine : "+date.get(date.WEEK_OF_YEAR));   
      out.println("Année : "+date.get(date.YEAR));
      out.println("Mois : "+date.get(date.MONTH));
      out.println("Heure : "+date.get(date.HOUR_OF_DAY));
      out.println("Minutes : "+date.get(date.MINUTE));      
      out.println("Nombre de jours dans le mois : "+date.getActualMaximum(Calendar.DAY_OF_MONTH));
      out.println("Année bissextile ? "+(date.getLeastMaximum(Calendar.DAY_OF_MONTH)!=28));
      date.add(Calendar.MONTH, 5);
      date.add(Calendar.DATE, -7);
      out.println("Ajouter 5 mois et retrancher 7 jours : "+date.getTime());
      date.set(Calendar.YEAR, 2000);
      out.println("Changer l'année : "+date.getTime());
      out.println(new GregorianCalendar(2000, Calendar.AUGUST, 12).getTime());
   }
}

Bien que la classe Calendar décompose une valeur temporelle selon ses champs heure, jour, mois, etc., elle n'est pas conçue pour représenter ces champs dans un format adapté à l'utilisateur final. Cette fonction est réalisée par la classe java.text.DateFormat, laquelle gère les aspects de sérialisation.

Exemple d'application - fabrication d'un calendrier

package calendrier;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

public class Calendrier extends JFrame {
   private static enum JourSemaine {Dim, Lun, Mar, Mer, Jeu, Ven, Sam};
   private Semaine semaine = new Semaine();
   private Mois jourMois = new Mois();
   private Sélection sélection = new Sélection();
   
   public Calendrier() {
       super("Calendrier");
       add(semaine, BorderLayout.NORTH);
       add(jourMois);
       add(sélection, BorderLayout.SOUTH);
       pack();
       setDefaultCloseOperation(EXIT_ON_CLOSE);
       setResizable(false);
       setVisible(true);
   }
            
   public static void main(String[] args) {
        new Calendrier();
   }
   
   private class Semaine extends JPanel {      
      Semaine() {
         setLayout(new GridLayout(1, 0));
         setBackground(Color.PINK);         
         for (JourSemaine jour : JourSemaine.values())
            add(new Jour(jour));      
      }
   }
   
   private class Jour extends JLabel {
      Jour(JourSemaine jour) {
         super(jour.name());
         init();
      }
      Jour(int i) {
         super(""+i);
         init();  
      }
      private void init() {
         setHorizontalAlignment(CENTER);
         setBorder(BorderFactory.createEtchedBorder());         
         setPreferredSize(new Dimension(37, 20));
      }
   }  
   
   private class Mois extends JComponent {
      Calendar aujourdhui = Calendar.getInstance();
      Calendar calendrier = Calendar.getInstance();
      int jour = calendrier.get(Calendar.DAY_OF_MONTH);
      int mois;
      int année;
      int premierJourDuMois;
      int nombreJour;
      
      Mois() {
         init();
         setLayout(new GridLayout(0, 7));
      }
      
      private void init() {
         removeAll();
         mois = calendrier.get(Calendar.MONTH);
         année = calendrier.get(Calendar.YEAR);
         Calendar calcul = calendrier;
         calcul.set(Calendar.DAY_OF_MONTH, 1);
         premierJourDuMois = calcul.get(Calendar.DAY_OF_WEEK);    
         nombreJour = calendrier.getActualMaximum(Calendar.DAY_OF_MONTH);
         for (int i=1; i<premierJourDuMois; i++) add(new JLabel());
         for (int i=1; i<=nombreJour; i++) {
            Jour jourDuMois = new Jour(i);
            if (i==jour && mois==aujourdhui.get(Calendar.MONTH) && année==aujourdhui.get(Calendar.YEAR)) 
               jourDuMois.setForeground(Color.RED);
            add(jourDuMois);
         }
         revalidate();
         repaint();
         pack();
      }
      
      void setMois(int mois) {
         calendrier.set(Calendar.MONTH, mois);
         init();        
      }
      
      void setAnnée(int année) {
         calendrier.set(Calendar.YEAR, année);
         init();   
      }
   }
   
   private class Sélection extends JComponent implements ActionListener, ChangeListener {
      private JComboBox mois = new JComboBox(new String[] {"Janvier", "Février", "Mars", 
         "Avril", "Mai","Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"});
      private JSpinner année = new JSpinner(new SpinnerNumberModel(jourMois.année, 0, 2100, 1));
      
      Sélection() {
         setLayout(new BorderLayout());
         mois.setSelectedIndex(jourMois.mois);         
         mois.addActionListener(this);
         année.setFont(new Font("Arial", Font.BOLD+Font.ITALIC, 16));
         année.addChangeListener(this);
         add(mois);
         add(année, BorderLayout.EAST);
      }

      public void actionPerformed(ActionEvent e) {
         jourMois.setMois(mois.getSelectedIndex());
      }

      public void stateChanged(ChangeEvent e) {
         jourMois.setAnnée((Integer)année.getValue());
      }
   }
}

 

Choix du chapitre Formater les dates pour l'affichage

La méthode toString() de la classe Date produit une représentation textuelle d'une date et d'une heure, mais ne réalise aucune localisation et ne permet pas de personnaliser les champs (par exemple, le jour, mois, année, heures et minutes) à afficher. Cette méthode toString() ne devrait être utilisée que pour produire une heure lisible par une machine, pas une chaîne lisible par un être humain.

La classe DateFormat

Pour représenter des dates sous forme textuelle, nous devons plutôt utiliser la classe java.text.DateFormat dont c'est sa spécialité. Pour créer une chaîne représentant un instant de temps, nous créons donc un objet DateFormat et nous appliquons sa méthode format() à un objet Date. Tout comme NumberFormat vu précédemment, DateFormat est lui-même abstrait.

Cette classe formate et analyse les dates et les heures d'une manière spécifique aux paramètres locaux. En tant que classe abstraite, elle ne peut être instanciée de manière directe, mais elle propose un certain nombre de méthodes statiques qui retournent des instances d'une sous-classe concrète utilisable pour formater les dates de diverses manières.

  1. getDateInstance() : retourne un objet DateFormat adapté au formatage des dates selon les paramètres locaux spécifiés ou par défaut.
  2. getTimeInstance() : retourne un objet DateFormat qui formate et analyse les heures.
  3. getDateTimeInstance() : retourne un objet DateFormat qui formate à la fois les dates et les heures.

Il est également possible de spécifier un style de formatage en précisant les constantes FULL, LONG, MEDIUM, SHORT à l'argumant de l'une de ces méthodes. Si vous utilisez une de ces méthodes sans spécifier de constante, il s'agit alors du format par défaut MEDIUM.

import java.text.DateFormat;
import java.util.*;
import static java.lang.System.*;

public class Principal {
   public static void main(String[] args) {
      Date date = new Date();
      out.println("Date par défaut : "+DateFormat.getDateInstance().format(date));
      out.println("Date complète : "+DateFormat.getDateInstance(DateFormat.FULL).format(date));
      out.println("Date longue : "+DateFormat.getDateInstance(DateFormat.LONG).format(date));
      out.println("Date moyenne : "+DateFormat.getDateInstance(DateFormat.MEDIUM).format(date));
      out.println("Date courte : "+DateFormat.getDateInstance(DateFormat.SHORT).format(date));
      out.println("Heure complète : "+DateFormat.getTimeInstance(DateFormat.FULL).format(date));
      out.println("Heure longue : "+DateFormat.getTimeInstance(DateFormat.LONG).format(date));
      out.println("Heure moyenne : "+DateFormat.getTimeInstance(DateFormat.MEDIUM).format(date));
      out.println("Heure courte : "+DateFormat.getTimeInstance(DateFormat.SHORT).format(date));
      out.println("Date et heure complètes : "+DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
      out.println("Date et heure longues : "+DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG).format(date));
      out.println("Date et heure moyennes : "+DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(date));
      out.println("Date et heure courtes : "+DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(date));
      DateFormat afficheDate = DateFormat.getDateInstance(DateFormat.FULL);
      Calendar aujourdhui = new GregorianCalendar();
      Calendar demain = new GregorianCalendar(2007, Calendar.FEBRUARY, 20);
      out.println("Aujourd'hui : "+afficheDate.format(aujourdhui.getTime()));
      out.println("Demain : "+afficheDate.format(demain.getTime()));
   }
}
Résultats

Date par défaut : 19 févr. 2007
Date complète : lundi 19 février 2007
Date longue : 19 février 2007
Date moyenne : 19 févr. 2007
Date courte : 19/02/07
Heure complète : 08 h 01 CET
Heure longue : 08:01:52 CET
Heure moyenne : 08:01:52
Heure courte : 08:01
Date et heure complètes : lundi 19 février 2007 08 h 01 CET
Date et heure longues : 19 février 2007 08:01:52 CET
Date et heure moyennes : 19 févr. 2007 08:01:52
Date et heure courtes : 19/02/07 08:01
Aujourd'hui : lundi 19 février 2007
Demain : mardi 20 février 2007

 

Retrouver la date réelle à partir d'un texte formaté

Tout comme pour la classe NumberFormat, vous pouvez utiliser la méthode parse() pour analyser une date, sous forme de texte, saisie par l'opérateur. Cette méthode parse() retourne donc un objet Date à partir de la chaîne de caractères formatée donnée en argument.

Résultats

Quelle est votre date ?
01/10/1959
jeudi 1 octobre 1959

 
import java.text.*;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);     
      System.out.println("Quelle est votre date ?");
      String saisie = clavier.nextLine();
      try {
         Date date = DateFormat.getDateInstance(DateFormat.SHORT).parse(saisie);
         System.out.println(DateFormat.getDateInstance(DateFormat.FULL).format(date));
      } 
      catch (ParseException ex) {
         System.err.println("Attention : "+saisie+" n'est pas une bonne date");
      }
   }
}

Affichage personnalisé avec la classe SimpleDateFormat

SimpleDateFormat est une classe concrète de Format utilisée par DateFormat pour gérer le formatage et l'analyse des dates. SimpleDateFormat formate les dates et les heures en fonction d'un motif, lequel spécifie les positions des divers champs de la date en fonction de caractères spécifiques qui indiquent quel est l'élément de la date à placer à cet endroit là.

Les applications qui nécessitent donc un formatage particulièrement personnalisé des dates et des heures peuvent créer un objet SimpleDateFormat personnalisé en spécifiant le motif désiré.

Champ Forme complète Forme abrégé
Année yyyy (4 chiffres) yy (2 chiffres)
Mois MMMM (nom complet), MMM(nom abrégé) MM (2 chiffres), M (1 ou 2 chiffres)
Jour de la semaine EEEE EE
Jour du mois dd (2 chiffres) d (1 ou 2 chiffres)
Heure (1-12) hh (2 chiffres) h (1 ou 2 chiffres)
Heure (0-23) HH (2 chiffres) H (1 ou 2 chiffres)
Heure (0-11) KK K (1 ou 2 chiffres)
Heure (1-24) kk k (1 ou 2 chiffres)
Minute mm m (1 ou 2 chiffres)
Seconde ss s (1 ou 2 chiffres)
Milliseconde SSS S (1, 2 ou 3 chiffres)
AM/PM a  
Fuseau horaire zzzz zz
Jour de la semaine dans le mois F (par exemple, 3ème jeudi)  
Jour de l'année DDD D (1, 2 ou 3 chiffres)
Semaine de l'année ww w (1, 2 ou 3 chiffres)
Ere (par exemple, av. J-C) G  
Un texte quelconque 'votre texte entre apostrophes'  

Vous placez votre motif sous forme de chaîne de caractères en argument du constructeur SimpleDateFormat. Nous pouvons éventuellement changer le motif en cours de route à l'aide de la méthode applyPattern().

Résultats

Date complète : mardi 20 février 2007
51ème jour de l'année
8ème semaine de l'année

 
import java.text.*;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      String motif = "EEEE dd MMMM yyyy";
      Date date = new Date();
      SimpleDateFormat affiche = new SimpleDateFormat(motif);
      System.out.println("Date complète : "+affiche.format(date));
      affiche.applyPattern("D");
      System.out.println(affiche.format(date)+"ème jour de l'année");
      affiche.applyPattern("w");
      System.out.println(affiche.format(date)+"ème semaine de l'année");
   }
}

Voici un programme qui détermine quel est le jour de la semaine d'une date saisie au clavier par l'opérateur :

Résultats

Quelle est votre date ?
01/10/1959
Le jour de la semaine est un jeudi

 
import java.text.*;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);     
      System.out.println("Quelle est votre date ?");
      String saisie = clavier.nextLine();
      DateFormat formatCourt = DateFormat.getDateInstance(DateFormat.SHORT);
      SimpleDateFormat jourSemaine = new SimpleDateFormat("EEEE");
      try {
         Date date = formatCourt.parse(saisie);
         System.out.println("Le jour de la semaine est un "+jourSemaine.format(date));
      } 
      catch (ParseException ex) {
         System.err.println("Attention : "+saisie+" n'est pas une bonne date");
      }
   }
}

 

Choix du chapitre Format de texte personnalisé

Jusqu'ici, nous avons vu comment formater des dates et des nombres sous forme de texte. A présent, jetons un oeil à une classe ChoiceFormat, qui fait correspondre des fourchettes de nombres à un texte. ChoiceFormat est construite en indiquant les fourchettes de nombres et les chaînes qui leur correspondent.

Cette classe est une sous-classe de Format qui convertit un nombre en objet String d'une manière qui rappelle une instruction switch() ou un type énuméré. Chaque objet Choiceformat possède un tableau de valeurs double qui représentent ses limites, ainsi qu'un tableau de chaînes de caractères qui représentent ses formats. Lorsque la méthode format() est appelée pour formater un nombre x, l'objet ChoiceFormat trouve un index i tel que :

limites[i] <= x < limites[i+1]

Si x est inférieur au premier élément du tableau, le premier élément est utilisé, et s'il est supérieur au dernier élément, le dernier élément est utilisé. Une fois que l'index i est déterminé, il est utiliser comme index dans le tableau de chaînes de caractères, et la chaîne de caractère est retournée comme résultat de la méthode format().

Un constructeur de ChoiceFormat accepte un tableau de double et de String, dans lequel chaque chaîne correspond à la fourchette en commençant par le nombre correspondant jusqu'au (mais sans l'inclure) nombre suivant.

Résultats

Quel âge as-tu ?
47
tu es vieux

 
import java.text.*;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);     
      System.out.println("Quel âge as-tu ?");
      int saisie = clavier.nextInt();
      double[] limites = new double[] {0, 20, 40, 60};
      String[] textes = new String[] {"jeune", "adulte", "vieux", "très vieux"};
      ChoiceFormat affiche = new ChoiceFormat(limites, textes);
      System.out.println("tu es "+affiche.format(saisie));
   }
}

Vous pouvez aussi préciser à la fois les limites et le texte associé en utilisant une chaîne spéciale dans le deuxième constructeur de ChoiceFormat. Les paires limite/valeur sont séparés par des barres verticales | ; le signe dièse # sépare chaque limite de sa valeur correspondante.

Résultats

Quel âge as-tu ?
47
tu es vieux

 
import java.text.*;
import java.util.*;

public class Principal {
   public static void main(String[] args) {
      Scanner clavier = new Scanner(System.in);     
      System.out.println("Quel âge as-tu ?");
      int saisie = clavier.nextInt();
      ChoiceFormat affiche = new ChoiceFormat("0#jeune|20#adulte|40#vieux|60#très vieux");
      System.out.println("tu es "+affiche.format(saisie));
   }
}

Vous pouvez utiliser la méthode applyPattern() pour changer le motif utilisé par un objet ChoiceFormat ; utilisez toPattern() afin d'obtenir le motif utilisé.
.

ChoiceFormat est très utile pour gérer les pluriels dans les messages, vous évitant des constructions peu élégantes du type "fichiers(s) ouvert(s)".
.

 

Choix du chapitre Format de messages paramétrés

La classe MessageFormat formate et substitue des objets à des positions d'un modèle donné par une chaîne de caractères. Elle présente beaucoup de similitude avec la fonction printf() du langage de programmation C++ même si la syntaxe diffère. Les arguments sont ici délimités par des accolades et peuvent contenir des informations de formatage.

Si un message doit être affiché plusieurs fois, la manière la plus simple d'utiliser la classe MessageFormat consiste à utiliser la méthode statique format(). Cette méthode reçoit alors un modèle ainsi qu'un tableau d'objets qui doivent être formatés et substitués dans le modèle. Si le modèle doit être affiché plusieurs fois, il vaut mieux créer un objet MessageFormat, lui fournir le modèle puis appeler la méthode d'instance format() de cet objet avec le tableau d'objets à formater dans le message.

Le modèle utilisé par Messageformat contient donc des chiffres délimités par des accolades pour indiquer l'endroit où chaque argument doit être substitué. La séquence {0} indique que le premier objet doit être converti en une chaîne de caractères (si nécessaire) et inséré à cet endroit, alors que la séquence {3} indique que c'est le quatrième objet qui doit être inséré à cet endroit.

Si l'objet à insérer n'est pas une chaîne de caractères, MessageFormat contrôle s'il s'agit d'une Date ou d'une sous-classe de Number. Si tel est le cas, il utilise un objet DateFormat par défaut afin de convertir la valeur en chaîne de caractères. Sinon, il se contente d'invoquer la méthode toString() de l'objet concerné pour effectuer la conversion.

Exemple en passant par la mise en oeuvre d'un objet MessageFormat

String texte = "Le {2}, un {0} a détruit {1} maisons et provoqué {3} de dégâts.";
Object[] arguments = {"ouragan", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E7};
MessageFormat message = new MessageFormat(texte);
System.out.println(message.format(arguments));
Le 01/01/99 00:00, un ouragan a détruit 99 maisons et provoqué 100 000 000 de dégâts. <-- Résultats

Autre solution sans constructeur, en utilisant directement avec la méthode statique format()

String texte = "Le {2}, un {0} a détruit {1} maisons et provoqué {3} de dégâts.";
Object[] arguments = {"ouragan", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E7};
System.out.println(MessageFormat.format(texte, arguments));

Depuis le JDK 5.0, dans la méthode format(), il est possible de placer chacun des arguments, séparés par une virgule, les uns à la suite des autres :

String texte = "Le {2}, un {0} a détruit {1} maisons et provoqué {3} de dégâts.";
System.out.println(MessageFormat.format(texte,"ouragan", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E7)); 

Type des arguments

Dans un modèle, un chiffre entre accolades peut être facultativement suivi par une virgule et l'un des mots date, time, number ou choice pour indiquer que l'argument correspondant devra être formaté comme une date, une heure, un nombre ou un choix, avant d'être substitué dans le modèle. Chacun des mots clés peut en outre être suivi par une autre virgule, puis par des informations supplémentaires (styles) à utiliser lors du formatage de la date, de l'heure, du nombre ou du choix.

Type Styles
choice motif
date short, medium, long, full, motif
number integer, percent, currency, motif
time short, medium, long, full, motif

En reprenant l'exemple ci-dessus, voici ce que nous pouvons faire :

String texte = "Le {2, date, full}, un {0} a détruit {1, number, integer} maisons et provoqué {3, number, currency} de dégâts.";
System.out.println(MessageFormat.format(texte, "ouragan", 99, new GregorianCalendar(1999, 0, 1).getTime(), 10.0E7));
Le vendredi 1 janvier 1999, un ouragan a détruit 99 maisons et provoqué 100 000 000,00 € de dégâts. <-- Résultats

Dans cet exemple, comme nous l'avons fait plus haut, il n'est pas nécessaire de spécifier le type et le style de l'argument {1}.
.

Utilisez un MessageFormat avec un ChoiceFormat

Voyons un autre exemple qui indique nous indique aujoud'hui quel est le nombre de messages de notre courrier électronique :

String texte = "Actuellement, le {0, date, long}, vous avez {1} messages.";
System.out.println(MessageFormat.format(texte, new Date(), 45));   
Actuellement, le 22 février 2007, vous avez 45 messages. <-- Résultats

C'est pas mal, mais cela reste approximatif. Que se passe-t-il avec un seul message ? Pour que cela reste correct sur le plan grammatical, nous pouvons intégrer une chaîne de motifs de style ChoiceFormat dans notre chaîne de motif MessageFormat. Pour un seul (ou zéro) message, voici ce qu'il se passe :

String texte = "Actuellement, le {0, date, long}, vous avez {1} message{1, choice, 1#|2#s}.";
System.out.println(MessageFormat.format(texte, new Date(), 1));   
Actuellement, le 22 février 2007, vous avez 1 message. <-- Résultats

Avec 45 messages, voici ce qu'il se passe :

String texte = "Actuellement, le {0, date, long}, vous avez {1} message{1, choice, 1#|2#s}.";
System.out.println(MessageFormat.format(texte, new Date(), 45));   
Actuellement, le 22 février 2007, vous avez 45 messages. <-- Résultats

Ici, nous utilisons deux fois l'élément {1} : une fois pour fournir le nombre de messages, une fois pour fournir l'entrée du motif ChoiceFormat. Le motif dit d'ajouter un s si l'argument {1} possède au moins la valeur 2, ou rien dans le cas contraire.

Nous pouvons proposer même un message encore plus sophistiqué :

int nombreMessage = 0;
String texte = "Actuellement, le {0, date, long}, vous avez {1, choice, 0#aucun message|1#un message|2#{1} messages}.";
System.out.println(MessageFormat.format(texte, new Date(), nombreMessage));   
Actuellement, le 22 février 2007, vous avez aucun message. <-- Résultats avec nombreMessage = 0
Actuellement, le 22 février 2007, vous avez un message. <-- Résultats avec nombreMessage = 1
Actuellement, le 22 février 2007, vous avez 45 messages. <-- Résultats avec nombreMessage = 45

ici aussi, nous utilisons deux fois {1} dans la chaîne de formatage ? Lorsque le format de message applique le format de choix à l'emplacement {1} et que la valeur est supérieure ou égale à 2, le format de choix renvoie "{1} messages". Cette chaîne est alors à nouveau mise en forme par le format du message et la réponse est répartie dans le résultat.

Autres méthodes de la classe MessageFormat

Vous pouvez définir un nouveau modèle associé à un objet MessageFormat en appelant la méthode applyPattern(), et vous pouvez obtenir une chaîne de caractères qui représente le modèle de formatage courant en appelant toPattern(). MessageFormat prend également en charge une méthode parse() capable d'analyser un tableau d'objets à partir d'une chaîne de caractères spécifiée, conformément au modèle donné.

 

Choix du chapitre Exemple de synthèse

Pour terminer, voici un exemple qui met en oeuvre ce que nous venons d'apprendre. Le programme ci-dessous permet de faire une conversion entre des €uros saisie au clavier d'une part, et des dollars ou des francs d'autre part. L'opérateur doit respecter la syntaxe de la valeur saisie en €uro.

Voici le code source correspondant :

package conversion;

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

public class Principal extends JFrame implements ActionListener {
   private JTextField saisie = new JTextField("0 €");
   private JButton $ = new JButton("$");
   private JButton f = new JButton("F");
   private JTextField résultat = new JTextField(" $0");
   private JPanel panneau = new JPanel();
   
   public Principal() {
      setTitle(DateFormat.getDateInstance(DateFormat.FULL).format(new Date()));
      setSize(300, 90);
      setDefaultCloseOperation(EXIT_ON_CLOSE);      
      saisie.setColumns(16);
      résultat.setEditable(false);
      résultat.setBackground(Color.yellow);
      $.addActionListener(this);
      f.addActionListener(this);
      panneau.add(saisie);
      panneau.add($);
      panneau.add(f);
      panneau.setBackground(Color.orange);
      add(panneau, BorderLayout.CENTER);
      add(résultat, BorderLayout.SOUTH);
      setVisible(true);
   }
   
   public static void main(String[] args) {
      new Principal();
   }

   public void actionPerformed(ActionEvent e) {
      double TauxDollar = 1.29984;
      double TauxFranc = 6.55975;
      NumberFormat monnaie€uro = NumberFormat.getCurrencyInstance();    
      String affiche = " {2, time, H'h'mm'mn'} : {0, number, currency}   {0, choice, 1#fait|2#font}   {1}";
      String valeur = "";
      try {
         double €uro = monnaie€uro.parse(saisie.getText().trim()).doubleValue();
         if (e.getSource()==$) {
            NumberFormat monnaieUS = NumberFormat.getCurrencyInstance(Locale.US);
            valeur = monnaieUS.format(€uro*TauxDollar);
         }
         if (e.getSource()==f) {
            String motif = MessageFormat.format("#,##0.00 Franc{0, choice, 0#|1#s}", €uro);
            DecimalFormat monnaieFranc = new DecimalFormat(motif);
            valeur = monnaieFranc.format(€uro*TauxFranc);
         }
         résultat.setText(MessageFormat.format(affiche, €uro, valeur, new Date()));
      } 
      catch (ParseException ex) {
         résultat.setText("Valeur en €uro incorrecte");
      }
   }
}