Utilisation Des Dates Java
Utilisation Des Dates Java
Niveau : Elémentaire
il existe plusieurs calendriers dont le plus usité est le calendrier Grégorien qui débute à la
naissance de Jésus Christ.
le calendrier Grégorien comporte de nombreuses imperfections : le nombre de jours d'un
mois varie selon le mois, le nombre de jours d'une année varie selon l'année (année
bissextile), ...
le format textuel de restitution des dates diffère selon la Locale utilisée
l'existence des fuseaux horaires qui donnent une date/heure différente d'un point dans le
temps selon la localisation géographique
la possibilité de prendre en compte un décalage horaire lié à l'heure d'été et à l'heure
d'hiver
...
Pourtant le temps s'écoule de façon linéaire : c'est d'ailleurs de cette façon que les calculs de dates
sont réalisés avec Java, en utilisant une représentation de la date qui indique le nombre de
millisecondes écoulées depuis un point d'origine défini. Dans le cas de Java, ce point d'origine est le
1er janvier 1970. Ceci permet de définir un point dans le temps de façon unique.
L'utilisation de dates en Java est de surcroît plus compliquée à cause de l'API historique qui permet
leur gestion car elle n'est pas toujours intuitive.
Il est intéressant de découpler l'obtention de la date/heure système par exemple en utilisant une
fabrique. Cette fabrique renvoie la date/heure système en production mais elle est aussi capable de
renvoyer une date/heure déterminée.
Exemple :
view source
print?
[Link] aujourdhui = [Link]();
L'utilisation d'une telle fabrique peut être particulièrement utile lors de tests unitaires ou
d'intégration pour faciliter la vérification des résultats par rapport à un type de données dont la
valeur par définition évolue constamment.
Ceci évite entre autres d'avoir à modifier la date système sur la ou les machines sur lesquelles les
tests sont exécutés.
La bibliothèque jFin aussi propose des fonctionnalités relatives au traitement des dates
spécifiquement dédiées à la finance.
A partir de Java 1.1, la responsabilité de la gestion et des traitements sur les dates sont réparties
sur plusieurs classes :
Les classes permettant la mise en oeuvre des dates sont dans le package [Link] exceptées celles
relatives à leur conversion de et vers une chaîne de caractères qui sont dans le package [Link]
Le package [Link] contient aussi des classes relatives aux dates et à leur utilisation dans une
base de données :
Les classes abstraites Calendar, TimeZone et DateFormat possèdent toutes une implémentation
concrète respectivement GregorianCalendar, SimpleTimeZone et SimpleDateFormat.
La conception des classes qui encapsulent et manipulent des dates ne facilitent pas leur mise en
oeuvre. C'est d'autant plus dommageable que l'utilisation de dates est courante notamment dans
les applications de gestion.
Par exemple, l'API propose au moins quatre manières pour obtenir un point dans le temps depuis le
1 janvier 1970 :
Exemple :
view source
print?
[Link]([Link]());
[Link](new [Link]().getTime());
[Link]([Link]().getTimelnMillis() );
[Link]([Link]().getTime().getTime ())
L'API de gestion des dates en Java est particulièrement propice à la confusion et à l'obtention
d'erreurs potentielles :
Depuis la version 1.1, toutes les méthodes permettant de manipuler la date sont deprecated.
Par défaut, cette classe encapsule le point courant dans le temps obtenu en utilisant la méthode
[Link]() ce qui rend sa précision dépendante du système d'exploitation.
La classe Calendar n'est pas stateless puisqu'elle encapsule un point dans le temps : il est donc
nécessaire d'initialiser ce point avant de pouvoir utiliser l'instance de Calendar.
Une nouvelle instance de la classe est toujours initialisée avec le point dans le temps courant. Pour
encapsuler un autre point, il faut obligatoirement après l'instanciation utiliser une des méthodes de
la classe pour modifier le point encapsulé.
Pour accéder aux différentes propriétés de la date encapsulée dans l'instance de Calendar, il
n'existe pas un getter pour chaque propriété mais une seule méthode get() qui attend en
paramètre le nom de la propriété souhaitée et qui retourne toujours une valeur de type int.
La classe Calendar définit des constantes de type int pour le nom de ces propriétés.
La classe Calendar définit aussi plusieurs constantes qui contiennent les valeurs possibles pour
certaines propriétés. Leur utilisation est fortement recommandée car certaines valeurs sont parfois
surprenantes notamment celles qui encapsulent un mois. La valeur d'un mois varie de 0 à 11
correspondants aux constantes [Link] à [Link]. Calendar définit aussi la
constante UNDECIMBER qui représente le treizième mois de l'année requis par certains calendriers.
Attention : toutes ces constantes sont définies pêle-mêle dans la classe et ne sont donc pas
groupées par une convention de nommage dans une interface dédiée par rôle. Elles sont toutes de
types int, ce qui peut permettre d'utiliser n'importe quelle constante à la place d'une autre.
Exemple :
view source
print?
[Link] calendar = [Link]();
[Link] ( [Link]( [Link] )==[Link] ) {
3. [Link]("la date courante est en janvier"); }
La classe Calendar propose trois façons de manipuler la date qu'elle encapsule en agissant sur les
éléments qui la compose :
directement par un calcul sur le nombre de millisecondes écoulées depuis le 1er janvier
1970
en agissant sur les éléments qui composent la date en utilisant les méthodes dédiées
La méthode isLeapYear() permet de savoir si l'année encapsulée par la classe est bissextile.
Une instance de type TimeZone est utilisée par la classe Calendar pour déterminer la date
correspondant au point dans le temps qu'elle encapsule. Un même point dans le temps correspond
à des dates/heures différentes pour deux fuseaux horaires différents.
Un fuseau horaire correspond à un certain décalage vis à vis du méridien de référence, le méridien
de Greenwich. Le fuseau horaire correspondant à ce méridien est désigné par GMT.
Ce décalage peut en plus être affecté par un second décalage induit par les heures d'été et d'hiver
(daylight savings time (DST)) si ceux-ci sont mis en place dans le fuseau horaire.
La classe TimeZone encapsule un nom long et un nom court qui permet d'identifier le fuseau
horaire qu'elle encapsule.
La méthode String[] getAvailableIDs() permet d'obtenir les noms des TimeZones définis en
standard : par exemple avec Java 6, il y a 597 TimeZones fournis.
La classe est une fabrique qui permet d'obtenir une instance de TimeZone à partir de son
identifiant en utilisant la méthode getTimeZone() ou celle correspondant à la Locale courante en
utilisant la méthode getDefault().
Ce formatage doit traduire certains éléments notamment le jour et le mois de la date selon la
Locale. De nombreux formats de dates sont aussi utilisés généralement dépendant eux aussi de la
Locale.
Quatre styles de formats sont définis par défaut : SHORT, MEDIUM, LONG, et FULL. Avec une Locale
et un style, la classe DateFormat peut fournir un formatage standard de la date.
La classe DateFormat propose plusieurs méthodes statiques getXXXlnstance() qui sont des
fabriques qui renvoient des instances de type DateFormat.
La méthode parse() permet d'extraire une date à partir de sa représentation sous la forme d'une
chaîne de caractères.
La Locale et le style de la classe DateFormat ne peuvent pas être modifiés après la création de son
instance.
Pour réaliser ces traitements, cette classe utilise un modèle (pattern) sous la forme d'une chaîne de
caractères.
La classe DataFormat propose plusieurs méthodes pour obtenir le modèle par défaut de la Locale
courante :
getTimeInstance(style)
getDateInstance(style)
getDateTimeInstance(styleDate, styleHeure)
Ces méthodes utilisent la Locale par défaut mais chacune de ces méthodes possède une surcharge
qui permet de préciser une Locale.
Pour chacune de ces méthodes, quatre styles sont utilisables : SHORT, MEDIUM, LONG et FULL. Ils
permettent de désigner la richesse des informations contenues dans le modèle pour la date et/ou
l'heure.
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
[Link] [Link];
[Link] [Link];
06.
[Link] class TestFormaterDate2 {
08.
09. /**
10. * @param args
11. */
12. public static void main(String[] args) {
13. Date aujourdhui = new Date();
14.
15. DateFormat shortDateFormat = [Link](
16. [Link],
17. [Link]);
18.
19. DateFormat shortDateFormatEN = [Link](
20. [Link],
21. [Link], new Locale("EN","en"));
22.
23. DateFormat mediumDateFormat = [Link](
24. [Link],
25. [Link]);
26.
27. DateFormat mediumDateFormatEN = [Link](
28. [Link],
29. [Link], new Locale("EN","en"));
30.
31. DateFormat longDateFormat = [Link](
32. [Link],
33. [Link]);
34.
35. DateFormat longDateFormatEN = [Link](
36. [Link],
37. [Link], new Locale("EN","en"));
38.
39. DateFormat fullDateFormat = [Link](
40. [Link],
41. [Link]);
42.
43. DateFormat fullDateFormatEN = [Link](
44. [Link],
45. [Link], new Locale("EN","en"));
46.
47. [Link]([Link](aujourdhui));
48. [Link]([Link](aujourdhui));
49. [Link]([Link](aujourdhui));
50. [Link]([Link](aujourdhui));
51. [Link]("");
52. [Link]([Link](aujourdhui));
53. [Link]([Link](aujourdhui));
54. [Link]([Link](aujourdhui));
55. [Link]([Link](aujourdhui));
56. }
57.
58.}
Résultat :
view source
print?
01.27/06/06 21:36
02.27 juin 2006 21:36:30
03.27 juin 2006 21:36:30 CEST
[Link] 27 juin 2006 21 h 36 CEST
05.
06.6/27/06 9:36 PM
[Link] 27, 2006 9:36:30 PM
[Link] 27, 2006 9:36:30 PM CEST
[Link], June 27, 2006 9:36:30 PM CEST
Il est aussi possible de définir son propre format en utilisant les éléments du tableau ci-dessous.
Chaque lettre du tableau est interprétée de façon particulière. Pour utiliser les caractères sans
qu'ils soient interprétés dans le modèle il faut les encadrer par de simples quotes. Pour utiliser une
quote il faut en mettre deux consécutives dans le modèle.
Lettr
Description Exemple
e
y Année 06 ; 2006
H Heure (0-23) 23
k Heure (1-24) 24
m Minutes 59
s Secondes 59
S Millisecondes 12564
Pour les caractères de type Text : moins de 4 caractères consécutifs représentent la version
abrégée sinon c'est la version longue qui est utilisée.
Pour les caractères de type Number : c'est le nombre de répétitions qui désigne le nombre
de chiffres utilisés, complété si nécessaire par des 0 à gauche.
Pour les caractères de type Year : 2 caractères précisent que l'année est codée sur deux
caractères.
Pour les caractères de type Month : 3 caractères ou plus représentent la forme littérale
sinon c'est la forme numérique du mois.
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
[Link] [Link];
[Link] [Link];
06.
[Link] class TestFormaterDate {
08.
09. public static void main(String[] args) {
10. SimpleDateFormat formater = null;
11.
12. Date aujourdhui = new Date();
13.
14. formater = new SimpleDateFormat("dd-MM-yy");
15. [Link]([Link](aujourdhui));
16.
17. formater = new SimpleDateFormat("ddMMyy");
18. [Link]([Link](aujourdhui));
19.
20. formater = new SimpleDateFormat("yyMMdd");
21. [Link]([Link](aujourdhui));
22.
23. formater = new SimpleDateFormat("h:mm a");
24. [Link]([Link](aujourdhui));
25.
26. formater = new SimpleDateFormat("K:mm a, z");
27. [Link]([Link](aujourdhui));
28.
29. formater = new SimpleDateFormat("hh:mm a, zzzz");
30. [Link]([Link](aujourdhui));
31.
32. formater = new SimpleDateFormat("EEEE, d MMM yyyy");
33. [Link]([Link](aujourdhui));
34.
35. formater = new SimpleDateFormat("'le' dd/MM/yyyy 'à' hh:mm:ss");
36. [Link]([Link](aujourdhui));
37.
38. formater = new SimpleDateFormat("'le' dd MMMM yyyy 'à' hh:mm:ss");
39. [Link]([Link](aujourdhui));
40.
41. formater = new SimpleDateFormat("dd MMMMM yyyy GGG, hh:mm aaa");
42. [Link]([Link](aujourdhui));
43.
44. formater = new SimpleDateFormat("yyyyMMddHHmmss");
45. [Link]([Link](aujourdhui));
46.
47. }
48.
49.}
Résultat :
view source
print?
01.27-06-06
02.270606
03.060627
04.9:37 PM
05.9:37 PM, CEST
06.09:37 PM, Heure d'été d'Europe centrale
[Link], 27 juin 2006
[Link] 27/06/2006 à 09:37:10
[Link] 27 juin 2006 à 09:37:10
10.27 juin 2006 ap. J.-C., 09:37 PM
11.20060627213710
Constructeur Rôle
La classe DateFormatSymbols encapsule les différents éléments textuels qui peuvent entrer dans la
composition d'une date pour une Locale donnée (les jours, les libellés courts des mois, les libellés
des mois, ...).
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
[Link] [Link];
05.
[Link] class TestFormaterDate3 {
07.
08. public static void main(String[] args) {
09. DateFormatSymbols dfsFR = new DateFormatSymbols([Link]);
10. DateFormatSymbols dfsEN = new DateFormatSymbols([Link]);
11.
12. String[] joursSemaineFR = [Link]();
13. String[] joursSemaineEN = [Link]();
14.
15. StringBuffer texteFR = new StringBuffer("Jours FR ");
16. StringBuffer texteEN = new StringBuffer("Jours EN ");
17.
18. for (int i = 1; i < [Link]; i++) {
19. [Link](" : ");
20. [Link](joursSemaineFR[i]);
21. [Link](" : ");
22. [Link](joursSemaineEN[i]);
23. }
24. [Link](texteFR);
25. [Link](texteEN);
26.
27. texteFR = new StringBuffer("Mois courts FR ");
28. texteEN = new StringBuffer("Mois courts EN ");
29. String[] moisCourtsFR = [Link]();
30. String[] moisCourtsEN = [Link]();
31.
32. for (int i = 0; i < [Link] - 1; i++) {
33. [Link](" : ");
34. [Link](moisCourtsFR[i]);
35. [Link](" : ");
36. [Link](moisCourtsEN[i]);
37. }
38.
39. [Link](texteFR);
40. [Link](texteEN);
41.
42. texteFR = new StringBuffer("Mois FR ");
43. texteEN = new StringBuffer("Mois EN ");
44. String[] moisFR = [Link]();
45. String[] moisEN = [Link]();
46.
47. for (int i = 0; i < [Link] - 1; i++) {
48. [Link](" : ");
49. [Link](moisFR[i]);
50. [Link](" : ");
51. [Link](moisEN[i]);
52. }
53.
54. [Link](texteFR);
55. [Link](texteEN);
56.
57. }
58.
59.}
Résultat :
view source
print?
[Link] FR : dimanche : lundi : mardi : mercredi : jeudi : vendredi :
samedi
[Link] EN : Sunday : Monday : Tuesday : Wednesday : Thursday : Friday :
Saturday
[Link] courts FR : janv. : févr. : mars : avr. : mai : juin : juil. :
août : sept. : oct.
04. : nov. : déc.
[Link] courts EN : Jan : Feb : Mar : Apr : May : Jun : Jul : Aug : Sep :
Oct : Nov : Dec
[Link] FR : janvier : février : mars : avril : mai : juin : juillet :
août : septembre :
[Link] : novembre : décembre
[Link] EN : January : February : March : April : May : June : July :
August : September :
09. October : November : December
Il est possible de définir son propre objet DateFormatSymbols pour personnaliser les éléments
textuels nécessaires au traitement des dates. La classe DateFormatSymbols propose à cet effet des
setters sur chacun des éléments.
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
[Link] [Link];
[Link] [Link];
06.
[Link] class TestFormaterDate4 {
08.
09. public static void main(String[] args) {
10. Date aujourdhui = new Date();
11. DateFormatSymbols monDFS = new DateFormatSymbols();
12. String[] joursCourts = new String[] {
13. "",
14. "Di",
15. "Lu",
16. "Ma",
17. "Me",
18. "Je",
19. "Ve",
20. "Sa" };
21. [Link](joursCourts);
22. SimpleDateFormat dateFormat = new SimpleDateFormat(
23. "EEE dd MMM yyyy HH:mm:ss",
24. monDFS);
25. [Link]([Link](aujourdhui));
26. }
27.
28.}
Résultat :
view source
print?
[Link] 27 juin 2006 21:38:22
La classe SimpleDataFormat permet également d'analyser une date sous la forme d'une chaîne de
caractères pour la transformer en objet de type Date en utilisant un modèle. Cette opération est
réalisée grâce à la méthode parse(). Si elle échoue, elle lève une exception de type ParseException.
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
[Link] [Link];
[Link] [Link];
06.
[Link] class TestParserDate {
08.
09. public static void main(String[] args) {
10. Date date = null;
11. SimpleDateFormat simpleDateFormat = new
SimpleDateFormat("dd/MM/yyyy");
12.
13. String date1 = "22/06/2006";
14. String date2 = "22062006";
15.
16. try {
17. date = [Link](date1);
18. [Link](date);
19. date = [Link](date2);
20. [Link](date);
21. } catch (ParseException e) {
22. [Link]();
23. }
24. }
25.}
Résultat :
view source
print?
[Link] Jun 22 00:00:00 CEST 2006
[Link]: Unparseable date: "22062006"
3. at [Link](Unknown Source)
4. at [Link]([Link])
La classe [Link] n'encapsule que la partie date en ignorant la partie horaire du point dans le
temps qu'elle encapsule.
La classe [Link], elle, n'encapsule que la partie horaire en ignorant la partie date du point
dans le temps qu'elle encapsule.
Ces trois méthodes redéfinissent la méthode toString() pour permettre une représentation
respectant le standard SQL 92.
Exemple :
view source
print?
[Link] [Link] dateSQL = new [Link](new Date().getTime()) ;
[Link](dateSQL);
Remarque : ces trois classes ne permettent pas de prendre en compte un TimeZone explicite
puisque généralement c'est celui de la base de données qui est toujours utilisé par défaut.
Exemple :
view source
print?
[Link] static final SimpleDateFormat dateFormat =
02. new SimpleDateFormat("dd/MM/yyyy");
[Link] static final SimpleDateFormat dateHeureFormat =
04. new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
05.
[Link] static String formatterDate(Date date) {
07. return [Link](date);
08.}
[Link] static String formatterDateHeure(Date date) {
10. return [Link](date);
11.}
Exemple :
view source
print?
[Link] df = new SimpleDateFormat("dd-MM-yyyy");
[Link] date=null;
[Link]
4.{
5. date= [Link]("25-12-2010");
6.} catch (ParseException e){
7. [Link]();
8.}
view source
print?
[Link] static Date ajouterJour(Date date, int nbJour) {
2. Calendar cal = [Link]();
3. [Link]([Link]();
4. [Link]([Link], nbJour);
5. return [Link]();
6.}
ou
Exemple :
view source
print?
[Link] static Date ajouterJour(Date date, int nbJour) {
2. Calendar cal = [Link]();
3. [Link]([Link]();
4. [Link](Calendar.DAY_OF_MONTH, nbJour);
5. return [Link]();
6.}
Pour retrancher des jours, il faut fournir un paramètre négatif au nombre de jours.
Exemple :
view source
print?
[Link] static Date ajouterMois(Date date, int nbMois) {
2. Calendar cal = [Link]();
3. [Link]([Link]();
4. [Link]([Link], nbMois);
5. return [Link]();
6.}
Pour retrancher des mois, il faut fournir un paramètre négatif au nombre de mois.
Exemple :
view source
print?
[Link] static Date ajouterAnnee(Date date, int nbAnnee) {
2. Calendar cal = [Link]();
3. [Link]([Link]());
4. [Link]([Link], nbAnnee);
5. return [Link]();
6.}
Pour retrancher des années, il faut fournir un paramètre négatif au nombre d'années.
Exemple :
view source
print?
[Link] static Date ajouterHeure(Date date, int nbHeure) {
2. Calendar cal = [Link]();
3. [Link]([Link]());
4. [Link]([Link], nbHeure);
5. return [Link]();
6.}
Pour retrancher des heures, il faut fournir un paramètre négatif au nombre d'heures.
Exemple :
view source
print?
[Link] static Date ajouterMinute(Date date, int nbMinute) {
2. Calendar cal = [Link]();
3. [Link]([Link]());
4. [Link]([Link], nbMinute);
5. return [Link]();
6.}
Pour retrancher des minutes, il faut fournir un paramètre négatif au nombre de minutes.
Exemple :
view source
print?
[Link] static Date ajouterSeconde(Date date, int nbSeconde) {
2. Calendar cal = [Link]();
3. [Link]([Link]());
4. [Link]([Link], nbSeconde);
5. return [Link]();
6.}
Pour retrancher des secondes, il faut fournir un paramètre négatif au nombre de secondes.
Exemple :
view source
print?
[Link] simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
[Link] dateStr = [Link](new Date());
[Link](dateStr);
Le format comporte de nombreuses options et peut même contenir du texte brut qui doit être
échappé avec des quotes simples.
Par défaut, la classe SimpleDateFormat travail avec la Locale courante. Il est possible de préciser
une autre Locale en tant que paramètre du constructeur.
Exemple :
view source
print?
[Link] simpleDateFormat = new SimpleDateFormat("dd MMMM yyyy
zzzz G", [Link]);
[Link] dateStr = [Link](new Date());
[Link](dateStr);
La méthode parse() permet de déterminer une date extraite d'une chaîne de caractères en utilisant
un format donné.
Exemple :
view source
print?
[Link] simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
[Link] date = [Link]("25/12/2010");
[Link](date);
Par défaut, SimpleDateFormat travail avec la Locale par défaut qui contient le fuseau horaire (time
zone)
Si la chaîne de caractères ne contient pas explicitement le fuseau horaire, il peut être nécessaire de
le préciser en utilisant la méthode setTimeZone();
Exemple :
view source
print?
[Link] simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
[Link]([Link]("PST"));
[Link] date = [Link]("25/12/2010");
[Link](date);
Il est possible de préciser le siècle si la date à parser ne contient que deux chiffres : par exemple
"01/01/02" peut correspondre à une date de l'année 1902 ou 2002. La méthode
set2DigitYearStart() permet de préciser la date de début de la plage de 100 ans dans laquelle
l'année sera traitée. Par défaut, cette plage de 100 ans correspond à la date du jour - 80 ans
jusqu'à la date du jour + 20 ans.
Exemple :
view source
print?
[Link] simpleDateFormat =
[Link] SimpleDateFormat("dd MMMM yy", [Link]);
[Link] date = [Link]("25-12-02");
[Link](date);
[Link] debut20emeSiecle = new GregorianCalendar(1901,1,1).getTime();
[Link].set2DigitYearStart(debut20emeSiecle);
[Link] = [Link]("25-12-02");
[Link](date);
Par défaut, le parsing de la date est très permissif : le format de la date n'a pas à respecter
strictement le format fourni à SimpleDateFormat. Dans ce cas, SimpleDateFormat va tenter
d'extraire une date qui potentiellement ne correspond pas du tout sans générer d'erreur.
Exemple :
view source
print?
[Link] simpleDateFormat =
[Link] SimpleDateFormat("dd-MM-yyyy", [Link]);
[Link] date = [Link]("31-04-10");
[Link](date);
Dans l'exemple ci-dessus, le mois d'avril ne possède que 30 jours. La classe SimpleDateFormat en
déduit que l'on veut le jour suivant le 30 avril soit le 1er mai. Ce comportement est rarement celui
souhaité.
Pour demander un respect strict du format, il faut passer la valeur false à la méthode setLenient().
Si le format de la date à traiter ne correspond pas, une exception est levée.
Exemple :
view source
print?
[Link] simpleDateFormat =
[Link] SimpleDateFormat("dd-MM-yyyy", [Link]);
[Link](false);
[Link] date = [Link]("31-04-10");
[Link](date);
La classe SimpleDateFormat n'est pas thread-safe car elle maintient son état, entre autre, avec
deux objets de type Calendar et NumberFormat. Si deux threads utilisent la même instance pour
manipuler deux dates en même temps, le résultat des traitements est aléatoire : généralement il
est erroné par rapport à la date traitée ce qui conduit à une corruption des données qui n'est pas
facilement détectable ou, plus rarement, une exception est levée.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
04.
[Link] class DateUtil {
06.
07. public static final Date parse(String date) throws ParseException{
08. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-
yyyy");
09. return [Link](date);
10. }
11.
12. public static final String format(Date date) throws ParseException{
13. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-
yyyy");
14. return [Link](date); }
15. }
Cette solution est threadsafe mais son inconvénient est qu'elle peut requérir de nombreuses
ressources si le nombre d'invocations est important.
Pour pallier au premier souci, il est possible de créer une instance de classe statique qui permettra
de n'avoir qu'un seul objet.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
04.
[Link] class DateUtil {
06.
07. public static final SimpleDateFormat simpleDateFormat = new
SimpleDateFormat("dd-MM-yyyy");
08.
09. public static final Date parse(String date) throws ParseException{
10. return [Link](date);
11. }
12.
13. public static final String format(Date date) throws ParseException{
14. return [Link](date);
15. }
16.}
Cependant ces résultats aléatoires ne sont pas faciles à détecter dans une application car il faut
que plusieurs threads sollicitent en même temps l'instance de SimpleDateFormat.
Exemple :
view source
print?
[Link] [Link];
02.
[Link] class TestSimpleDateFormat {
04.
05. public static void main(String[] args) {
06. final String[] dates = new String[] {"15-01-2000", "28-02-2005", "20-
04-2005",
07. "31-07-2015" };
08.
09. Runnable runnable = new Runnable() { public void run() {
10. try {
11. for (int j = 0; j < 1000; j++) {
12. for (int i = 0; i < 2; i++) {
13. String date = [Link]([Link](dates[i]));
14. if (!(dates[i].equals(date))) {
15. throw new ParseException(dates[i] + " =>"+ date, 0);
16. }
17. }
18. }
19. } catch (ParseException e) {
20. [Link]();
21. }
22.
23. new Thread(runnable).start();
24.
25. Runnable runnable2 = new Runnable() {
26. public void run() {
27. try {
28. for (int j = 0; j < 1000; j++) {
29. for (int i = 0; i < 2; i++) {
30. String date = [Link]([Link](dates[i]));
31. if (!(dates[i].equals(date))) {
32. throw new ParseException(dates[i] + " =>"+ date, 0);
33. }
34. }
35. }
36. } catch (ParseException e) {
37. [Link]();
38. }
39. }
40. };
41. new Thread(runnable2).start();
42. }
43.}
Dans cet exemple, le nombre d'exceptions et d'anomalies de traitement est important car les
threads utilisent en permanence le même objet. Dans la réalité, par exemple dans une application
web, les exceptions et les dates erronées sont très rares. L'ennui avec les erreurs de formatage et
de parsing c'est qu'elles sont difficiles à détecter.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
04.
[Link] class DateUtil {
06.
07. public static final SimpleDateFormat simpleDateFormat = new
SimpleDateFormat("dd-MM-yyyy");
08.
09. public synchronized static final Date parse(String date) throws
ParseException{
10. return [Link](date);
11. }
12.
13. public synchronized static final String format(Date date) throws
ParseException{
14. return [Link](date);
15. }
16.}
Cette solution simple est thread-safe mais elle peut impliquer de la contention liée au verrou posé
lors de l'exécution de la méthode qui bloque l'invocation par d'autres threads.
Une autre solution est d'utiliser la classe ThreadLocal qui est capable de fournir une instance pour
le thread en cours, ainsi chaque thread peut avoir sa propre instance.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
04.
[Link] class DateUtil {
06.
07. private static ThreadLocal<SimpleDateFormat> format = new
ThreadLocal<SimpleDateFormat>() {
08. protected synchronized SimpleDateFormat initialValue() {
09. return new SimpleDateFormat("dd-MM-yyyy");
10. }
11. };
12.
13. public static final Date parse(String date) throws ParseException{
14. return [Link]().parse(date);
15. }
16.
17. public static final String format(Date date) throws ParseException{
18. return [Link]().format(date); }
19. }
Remarque : selon l'implémentation fournie de la classe ThreadLocal par le JRE, il peut y avoir des
fuites de mémoire lors du redéploiement de l'application dans un conteneur web.
Il peut être intéressant d'utiliser une SoftReference en paramètre du ThreadLocal pour améliorer la
gestion de la mémoire par la JVM.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
[Link] [Link];
[Link] [Link];
06.
[Link] class DateUtil {
08.
09. private static final ThreadLocal<SoftReference<DateFormat» format =
10. new ThreadLocal<SoftReference<DateFormat>>();
11.
12. private static DateFormat getDateFormat() {
13. SoftReference<DateFormat> softRef = [Link]();
14. if (softRef != null) {
15. final DateFormat result = [Link]();
16. if (result != null) {
17. return result;
18. }
19. }
20. final DateFormat result = new SimpleDateFormat("dd-MM-yyyy");
21. softRef = new SoftReference<DateFormat>(result);
22. [Link](softRef);
23. return result;
24. }
25.
26. public static final Date parse(final String date) throws ParseException
{
27. return getDateFormat().parse(date);
28. }
29.
30. public static final String format(final Date date) throws
ParseException {
31. return getDateFormat().format(date);
32. }
33.}
Une autre solution peut être d'utiliser une API tiers tel que :
Lors de la mise en oeuvre de la classe SimpleDateFormat, il faut aussi être vigilent car par défaut,
la classe SimpleDateFormat est très permissive : elle tente au mieux de faire correspondre la date
selon le format fourni, ce qui peut conduire à un comportement non souhaité et surtout à des
résultats indésirables.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
[Link] [Link];
05.
[Link] class TestDate {
07.
08. public static void main(final String[] args) {
09. final DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
10. Date d;
11. try {
12. d = [Link]("2010-01-15 07:23:30");
13. [Link](d);
14. } catch (final ParseException e) {
15. [Link]();
16. }
17. }
18.}
Résultat :
view source
print?
[Link] Nov 30 23:05:07 CET 2009
Pour que la classe SimpleDateFormat respecte strictement le format fourni et lève une exception
de type [Link], il faut invoquer la méthode setLenient() en lui passant la valeur
false en paramètre.
Exemple :
view source
print?
[Link] [Link];
[Link] [Link];
[Link] [Link];
[Link] [Link];
05.
[Link] class TestDate {
07.
08. public static void main(final String[] args) {
09. final DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
10. [Link](false);
11. Date d;
12. try {
13. d = [Link]("2010-01-15 07:23:30");
14. [Link](d);
15. } catch (final ParseException e) {
16. [Link]();
17. }
18. }
19.}
Résultat :
view source
print?
[Link]: Unparseable date: "2010-01-15 07:23:30"
[Link] [Link]([Link]) at
[Link]([Link])
Joda Time est une bibliothèque open source dont le but est de fournir une solution simple et
complète pour manipuler des données de types date/heure.
Le support de plusieurs systèmes calendaires dont celui par défaut est celui définit par le
standard ISO8601 (utilisé par XML) : Grégorien, Julien, Bouddhiste, Islamique, ...
Le parsing et le formatage de dates
Le support des fuseaux horaires
Le support de plusieurs classes temporelles : date/heure locale, durée, période,
intervalle, ...
Le but de Joda Time est de proposer une solution de remplacement aux classes de gestion des
dates du JDK qui possèdent plusieurs inconvénients :
Par exemple, Joda Time gère les mois de 1 à 12 dans son implémentation du calendrier Grégorien
alors que la classe GregorianCalendar du JDK gère les mois de 0 à 11.
Joda Time a été développé pour améliorer la manière d'utiliser des données de type date/heure en
mettant l'accent sur :
La faciliter d'utilisation
La fourniture d'un ensemble complet de fonctionnalités relatives aux traitements de
données de type dates/heures
L'extensibilité : Joda Time propose le support de plusieurs calendriers qui reposent sur la
classe Chronology
L'interopérabilité avec les classes correspondantes du JDK
La performance
La maturité
La version couverte dans cette section est la 2.1. Elle nécessite une version 1.5 ou supérieure du
JDK.
La partie publique de l'API est contenue dans les packages [Link] et [Link].
Joda Time utilise plusieurs concepts :
La classe JodaTimePermission peut être utilisée dans le mécanisme standard de sécurité de la JVM
pour restreindre l'utilisation à certaines fonctionnalités globales de JodaTime.
L'API Joda Time a été utilisée comme une grande source d'inspiration pour la JSR 310.
Classe Rôle
DateMidnight Classe immuable qui encapsule une date dont l'heure est forcée à minuit
LocalDate Classe immuable qui encapsule une date locale (sans fuseau horaire)
LocalTime Classe immuable qui encapsule une heure locale (sans fuseau horaire)
LocalDateTime Classe immuable qui encapsule une date/heure locale (sans fuseau horaire)
La représentation d'un instant en une date est dépendante du calendrier et du fuseau horaire
utilisés pour représenter cet instant.
L'interface ReadableInstant décrit les fonctionnalités d'un objet qui encapsule un instant.
Joda Time propose plusieurs classes qui implémentent l'interface ReadableInstant dont :
Instant : une implémentation immuable qui encapsule un instant dans le temps sans utiliser
de système calendaire ou de fuseau horaire particulier. Elle stocke en interne une valeur de
type long qui contient le nombre de millisecondes écoulées depuis le 1 er janvier 1970 à
minuit.
DateTime : encapsule une date/heure selon un système calendaire et un fuseau horaire
donnés. C'est l'implémentation la plus fréquemment utilisée.
DateMidnight : cette classe agit comme la classe DateTime excepté le fait que l'heure
encapsulée est toujours minuit.
MutableDateTime : cette classe agit comme la classe DateTime excepté le fait qu'elle ne
soit pas immuable.
Attention : l'interface ReadableInstant n'est qu'un sous-ensemble des fonctionnalités des classes
qui l'implémente. Il est généralement préférable de typer les variables avec leur implémentation
plutôt que de les typer avec l'interface ReadableInstant sauf si les fonctionnalités requises de
l'instance sont définies dans l'interface.
Il est généralement recommandé d'utiliser dans la mesure du possible des implémentations qui
soient immuables. L'objet ne peut ainsi pas être modifié sans créer une nouvelle instance, ce qui lui
permet d'être thread safe.
Important : Joda Time considère qu'un instant null correspond à l'instant présent. Ainsi lorsqu'une
méthode attend en paramètre un objet de ReadableInstant et que la valeur reçue en paramètre est
null, alors cela revient à passer en paramètre un instant qui correspond à l'instant présent.
La classe DateTime encapsule un instant dans le temps pour un système calendaire et un fuseau
horaire donné : ceux-ci lui permettent de restituer l'instant encapsulé sous la forme d'une date et
d'une heure.
Par défaut, une instance de type DataTime utilise le système calendaire ISOChronology et le fuseau
horaire obtenu du système. De nombreux constructeurs attendent en paramètre un objet de type
Chronology et/ou DateTimeZone qui permettent de préciser le système calendaire et /ou le fuseau
horaire à utiliser.
Le constructeur sans paramètre créé une instance qui encapsule l'instant courant représenté dans
le système calendaire ISO et le fuseau horaire par défaut.
Exemple :
view source
print?
[Link] datetime = new DateTime();
Exemple :
view source
print?
[Link] datetime = new DateTime(2012,12,25,0,0,0);
La classe DateTime propose plusieurs autres constructeurs qui acceptent une instance de type
Object comme valeur pour représenter la date/heure. Ces surcharges permettent à Joda Time d'être
extensible mais en sacrifiant le typage fort.
Par défaut, la classe ConverterManager permet de gérer les différents types supportés :
ReadableInstant
String : une chaîne de caractères qui contient la date au format ISO-8601
[Link] : dans ce cas, le système calendaire encapsulé est utilisé
[Link] et [Link]
Long : un nombre de millisecondes
null : est interprété par Joda Time comme l'instant présent
Exemple :
view source
print?
[Link] date = new Date();
[Link] timeEnMs = [Link]();
[Link] dateTime = new DateTime(timeEnMs);
Exemple :
view source
print?
[Link] date = new Date();
[Link] dateTime = new DateTime(date);
Exemple :
view source
print?
[Link] calendar = [Link]();
[Link](new Date());
[Link] dateTime = new DateTime(calendar);
Exemple :
view source
print?
[Link] timeString = "2012-12-25";
[Link] dateTime = new DateTime(timeString);
Exemple :
view source
print?
[Link] dt = new DateTime("2012-10-28T16:23:13.324+01:00");
Il est ainsi facile de convertir une instance de type [Link] ou [Link] en un objet
de type DateTime simplement en passant l'instance au constructeur de la classe DateTime.
A l'exécution de l'exemple ci-dessous une exception de type IllegalArgumentException est levée
avec le message No instant converter found for type: [Link] car Joda Time ne peut pas
convertir l'instance de type Object fournie en paramètre en un instant.
Exemple :
view source
print?
[Link] liste = new ArrayList();
[Link] dateCourante = new DateTime(liste);
Méthode Rôle
static DateTime parse(String str, Extraire une date/heure de la chaîne de caractères fournie en
DateTimeFormatter formatter) utilisant le formatteur passé en paramètre
La classe DateTime encapsule une date/heure de manière immuable. Les méthodes qui permettent
de modifier un élément de la date/heure encapsulée renvoie une nouvelle instance de type
DateTime qui encapsule le résultat de l'opération.
Méthode Rôle
DateTime withDate(int year, int monthOfYear, int Renvoyer une nouvelle instance de DateTime
dayOfMonth) dont l'année, le mois et le jour ont été modifié
Exemple :
view source
print?
[Link] dateCourante = new DateTime();
[Link] dateLimite = [Link](2);
La classe DateTime propose plusieurs solutions pour obtenir individuellement chacun des champs
de la date/heure encapsulée :
Propriété Rôle
centuryOfEra Le siècle
hourOfDay L'heure
monthOfYear Le mois
weekYear
Year L'année
yearOfEra
Exemple :
view source
print?
[Link] dateTime = new DateTime();
[Link]([Link]());
[Link]([Link]().get());
[Link]([Link]([Link]()).get());
La classe [Link] encapsule la valeur d'un champ qui est un élément d'une [Link]
classe [Link] propose quelques getters :
Méthode Rôle
Chronology
Retourne le système calendaire du DateTime correspondant au champ
getChronology()
La classe DateTime propose plusieurs méthodes qui permettent de modifier la valeur du champ et
de retourner une nouvelle instance de type DateTime qui encapsule le résultat de la mise à jour.
Méthode Rôle
Exemple :
view source
print?
[Link] dateTime = new DateTime(new Date());
[Link](dateTime);
[Link]("dayOfMonth " + [Link]().get());
[Link]("dayOfWeek " + [Link]().get());
[Link]("dayOfYear " + [Link]().get());
[Link]("ear " + [Link]().get());
[Link]("hourOfDay " + [Link]().get());
[Link]("millisOfDay " + [Link]().get());
[Link]("millisOfSecond " +
[Link]().get());
[Link]("minuteOfDay " + [Link]().get());
[Link]("minuteOfHour " + [Link]().get());
[Link]("monthOfYear " + [Link]().get());
[Link]("secondOfDay " + [Link]().get());
[Link]("secondOfMinute " +
[Link]().get());
[Link]("weekOfWeekyear " +
[Link]().get());
[Link]("weekyear " + [Link]().get());
[Link]("year " + [Link]().get());
[Link]("yearOfCentury " + [Link]().get());
[Link]("yearOfEra " + [Link]().get());
Résultat :
view source
print?
01.2012-11-08T06:56:46.781+01:00
[Link] 8
[Link] 4
[Link] 313
[Link] 1
[Link] 6
[Link] 25006781
[Link] 781
[Link] 416
[Link] 56
[Link] 11
[Link] 25006
[Link] 46
[Link] 45
[Link] 2012
[Link] 2012
[Link] 12
[Link] 2012
Méthode Rôle
long
Obtenir la différence entre la valeur de champ et celle
getDifferenceAsLong(ReadableInstant
correspondante dans l'instant passé en paramètre
instant)
Exemple :
view source
print?
[Link] dateTime = new DateTime(new Date());
[Link]("date = "+dateTime);
[Link]("nom du champ = "+[Link]().getName());
[Link]("mois EN =
"+[Link]().getAsText([Link]));
[Link]("mois court =
"+[Link]().getAsShortText());
[Link]("est bissextile = "+[Link]().isLeap());
[Link]("jour rounded =
"+[Link]().roundFloorCopy());
[Link]("mois rounded =
"+[Link]().roundFloorCopy());
[Link]("dayofWeek = "+[Link]().toString());
Résultat :
view source
print?
[Link] = 2012-11-08T07:04:03.265+01:00
[Link] du champ = year
[Link] EN = November
[Link] court = nov.
[Link] bissextile = true
[Link] rounded = 2012-11-08T00:00:00.000+01:00
[Link] rounded = 2012-11-01T00:00:00.000+01:00
[Link] = Property[dayOfWeek]
L'interface ReadablePartial définit les fonctionnalités d'un objet qui encapsule une date partielle
locale (pas de fuseau horaire). Il est possible de définir tout ou partie des champs de la date/heure
encapsulée.
Il parfois nécessaire de manipuler une partie d'une date et/ou d'une heure : par exemple
uniquement le jour, le mois ou l'heure. Ce besoin est défini par l'interface ReadablePartial qui
représente un instant partiellement défini.
Joda Time propose plusieurs classes qui implémentent l'interface ReadablePartial dont :
Partial
LocalDate
LocalTime
LocalDateTime
Il est possible de convertir une instance de type ReadablePartial en une instance de type
ReadableInstant en utilisant la méthode toDateTime().
La classe LocalDate encapsule une date (année, mois, jour), sans heure et sans fuseau horaire de
manière immuable.
Exemple :
view source
print?
1. LocalDate
[Link] = new LocalDate(2012, 12, 25);
A partir de la version 1.3 de Joda Time, la classe LocalDate doit être utilisée à la place de la classe
YearMonthDay qui est deprecated.
La classe LocalTime encapsule une heure (heure, minutes, secondes, millisecondes) sans fuseau
horaire de manière immuable.
Exemple :
view source
print?
[Link] localTime = new LocalTime(17, 30, 45);
La classe Interval encapsule un intervalle entre deux instants de manière immuable. L'instant de
début est inclus et l'instant de fin est exclu de l'intervalle. L'instant de fin doit donc être supérieur
ou égal à l'instant de début.
Les deux instants doivent obligatoirement utiliser le même système calendaire et le même fuseau
horaire.
Exemple :
view source
print?
[Link] interval = new Interval(
2. new DateTime("2012-12-10"),
3. new DateTime("2012-12-15"));
La classe Interval propose plusieurs autres méthodes pour manipuler le contenu de l'intervalle.
Exemple :
view source
print?
[Link] debut = new DateTime("2012-01-01");
[Link] fin = [Link]([Link](1));
[Link] interval = new Interval(debut, fin);
[Link]("Interval = " + interval);
[Link] = [Link]([Link]().plusMonths(1));
[Link]("Interval = " + interval);
Résultat :
view source
print?
[Link] =
2.2012-01-01T00:00:00.000+01:00/2012-02-01T00:00:00.000+01:00
[Link] = 2012-01-01T00:00:00.000+01:00/2012-03-01T00:00:00.000+01:00
La méthode contains() permet de déterminer si un Instant est inclus dans l'intervalle ou pas
Exemple :
view source
print?
[Link] interval = new Interval(
02. new DateTime("2012-12-10"),
03. new DateTime("2012-12-15"));
[Link]([Link](
05. new DateTime(2012, 12, 9, 23, 59, 59, 999)));
[Link]([Link](
07. new DateTime(2012, 12, 10, 0, 0, 0, 0)));
[Link]([Link](
09. new DateTime(2012, 12, 14, 23, 59, 59, 999)));
[Link]([Link](
11. new DateTime(2012, 12, 15, 0, 0, 0, 0)));
Résultat :
view source
print?
[Link]
[Link]
[Link]
[Link]
La méthode toDuration() permet d'obtenir une instance de type Duration à partir de l'instance de
type Interval.
Pour comparer deux instances de type Interval, il faut comparer leur durée.
Une période ne possède ni système calendaire ni fuseau horaire. Elle ne possède donc pas de
représentation en millisecondes : il est nécessaire d'utiliser un Instant qui servira de référence et
qui précisera le système calendaire et le fuseau horaire à utiliser pour y associer la période.
Par exemple, une période d'un mois ne correspond pas au même nombre de millisecondes si on
l'ajoute au premier janvier ou au premier février. C'est aussi le cas si l'on ajoute une heure : ce ne
sont pas forcement 60 minutes qui sont ajoutés selon le fuseau horaire et l'utilisation de l'heure
d'été/d'hiver.
La classe Period encapsule une durée dont la valeur est constituée de valeurs dans des champs qui
expriment différentes unités.
Par défaut, les champs utilisables dans une Period (années, mois, semaines, jours, heures, minutes,
secondes, millisecondes) sont définis dans une instance de la classe PeriodType. Il est possible de
restreindre les champs utilisables en utilisant la classe PeriodType. La classe PeriodType propose
plusieurs fabriques qui renvoient des instances de type PeriodType :
Standard : années, mois, semaine, jours, heures, minutes, secondes, millisecondes (c'est
l'instance par défaut)
YearMonthDayTime : années, mois, jours, heures, minutes, secondes, millisecondes
YearMonthDay : années, mois, jours
YearWeekDayTime : années, semaines, jours, heures, minutes, secondes, millisecondes
YearWeekDay : années, semaines, jours
YearDayTime : années, jours, heures, minutes, secondes, millisecondes
YearDay : années, jours, heures
DayTime : jours, heures, minutes, secondes, millisecondes
Time : heures, minutes, secondes, millisecondes
Et une fabrique pour chaque champ
JodaTime propose plusieurs classes qui encapsulent une valeur pour un des champs de manière
immuable : Years, Weeks, Months, Days, Hours, Minutes, Seconds.
La classe Days encapsule un nombre de jours. Elle ne possède pas de constructeur public : pour
obtenir une instance, il faut utiliser une des méthodes statiques qui sont des fabriques.
La méthode days() est une fabrique qui retourne une constante de type Days ou un instance selon
le valeur fournie en paramètre.
La méthode daysBetween() permet d'obtenir une instance qui encapsule le nombre de jours entre
deux Instant ou deux Partial.
La méthode daysIn() permet d'obtenir une instance qui encapsule le nombre de jours d'un Interval.
La classe Hours encapsule un nombre d'heures. Elle ne possède pas de constructeur public : pour
obtenir une instance, il faut utiliser la méthode hours() qui est une fabrique qui retourne une
constante ou une instance de type Hours selon la valeur fournie en paramètre.
La méthode hoursBetween() permet d'obtenir une instance qui encapsule le nombre d'heures entre
deux Instant ou deux Partial.
La méthode hoursIn() permet d'obtenir une instance qui encapsule le nombre d'heure d'un Interval.
La classe Minutes encapsule un nombre de minutes. Elle ne possède pas de constructeur public :
pour obtenir une instance, il faut utiliser la méthode minutes() qui est une fabrique qui retourne
une constante ou une instance de type Minutes selon la valeur fournie en paramètre.
La méthode minutesBetween() permet d'obtenir une instance qui encapsule le nombre de minutes
entre deux Instant ou deux Partial.
La méthode minutesIn() permet d'obtenir une instance qui encapsule le nombre de minutes d'un
Interval.
La classe Seconds encapsule un nombre de secondes. Elle ne possède pas de constructeur public :
pour obtenir une instance, il faut utiliser la méthode seconds() qui est une fabrique qui retourne
une constante ou une instance de type Seconds selon la valeur fournie en paramètre.
La méthode secondsBetween() permet d'obtenir une instance qui encapsule le nombre de secondes
entre deux Instant ou deux Partial.
La méthode secondsIn() permet d'obtenir une instance qui encapsule le nombre de secondes d'un
Interval.
La classe Weeks encapsule un nombre de semaines. Elle ne possède pas de constructeur public :
pour obtenir une instance, il faut utiliser la méthode weeks() qui est une fabrique qui retourne une
constante ou une instance de type Weeks selon la valeur fournie en paramètre.
La méthode weeksBetween() permet d'obtenir une instance qui encapsule le nombre de semaines
entre deux Instant ou deux Partial.
La méthode weeksIn() permet d'obtenir une instance qui encapsule le nombre de semaines d'un
Interval.
La classe Years encapsule un nombre d'années. Elle ne possède pas de constructeur public : pour
obtenir une instance, il faut utiliser la méthode years() qui est une fabrique qui retourne une
constante ou une instance de type Years selon la valeur fournie en paramètre.
La méthode yearsBetween() permet d'obtenir une instance qui encapsule le nombre d'années entre
deux Instant ou deux Partial.
La méthode yearsIn() permet d'obtenir une instance qui encapsule le nombre d'années d'un
Interval.
Une instance de type Period peut utiliser avec une instance de type Instant pour obtenir une
nouvelle instance de type Instant
Exemple :
view source
print?
[Link] noel = new DateTime("2012-12-25");
[Link] nouvelAn = [Link]([Link](7));
[Link](nouvelAn);
Les classes Period et MutablePeriod implémentent l'interface ReadablePeriod.
La conversion d'une période peut être complexe : par exemple, une journée ne vaut pas forcement
24 heures : elle peut aussi valoir 23 ou 25 heures en fonction de l'heure d'été/d'hiver. Cependant
une journée est généralement considérée comme composée de 24 heures : la classe Days possède
la méthode toStandardHours() qui permet de convertir la valeur en heures sur la base d'une
journée de 24 heures.
La classe Period propose des méthodes pour obtenir et pour modifier les valeurs des différents
champs. Comme la classe Period est immuable, les opérations de modifications renvoient une
nouvelle instance.
Il est possible de créer une instance de type Period qui encapsule la durée entre deux instants. Il
suffit simplement de passer les deux instants en paramètre du constructeur de la classe Period.
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] noel13 = new DateTime("2013-12-25");
[Link] period = new Period(noel12, noel13);
[Link]([Link]() + " an entre les deux dates");
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] noel13 = new DateTime("2013-12-25");
[Link] year = [Link](noel12, noel13);
[Link]([Link]() + " an entre les deux dates");
Attention : Joda Time considère une instance de type Period qui est null comme une période dont
tous les champs sont à zéro.
La classe Duration encapsule une durée mesurée en millisecondes de manière immuable. Un objet
de type Duration ne possède aucun système calendaire ni fuseau horaire.
Exemple :
view source
print?
[Link] noel = new DateTime("2012-12-25");
[Link] nouvelAn = new DateTime("2013-01-01");
[Link] duree = new Duration(noel, nouvelAn);
Un objet de type Duration peut être ajouté à un objet de type Instant pour obtenir une nouvelle
instance de type Instant
Exemple :
view source
print?
[Link] noel = new DateTime("2012-12-25");
[Link] nouvelAn = [Link](new Duration(24L * 60L * 60L * 1000L *
7));
[Link](nouvelAn);
Une instance de type ReadableDuration à null est considérée par Joda Time comme une instance de
type ReadableDurantion ayant pour durée la valeur zéro.
La classe abstraite Chronology est la classe de base pour encapsuler un système calendaire. La
classe DateTimeZone encapsule un fuseau horaire.
Joda Time utilise par défaut le système calendaire ISO et le fuseau horaire par défaut du système.
En interne, Joda Time utilise des fabriques pour créer des instances de type Chronology et
DateTimeZone qui sont des singletons.
La classe abstraite Chronology est la classe mère de toutes les classes qui encapsulent un système
calendaire. Une instance de type Chronology encapsule un moteur de calcul pour appliquer les
règles d'un système calendaire.
Joda Time propose un système extensible pour supporter différents systèmes calendaires. Joda
Time propose plusieurs classes filles, chacune encapsulant une implémentation d'un système
calendaire :
Pour obtenir une instance dédiée à un système calendaire, il faut utiliser la fabrique correspondante
en invoquant la méthode getInstance() de la classe qui encapsule le système calendaire souhaité.
Exemple :
view source
print?
[Link] calendrierCopte = [Link]()
[Link] dt = new DateTime(calendrierCopte);
Le système de calendrier par défaut de Joda Time est le calendrier ISO. Ce calendrier est
couramment utilisé mais ne convient pas pour des dates antérieures à 1583.
Il est possible de fournir une instance de type DateTimeZone qui encapsule un fuseau horaire en
paramètre de la fabrique pour préciser le fuseau horaire à utiliser.
Exemple :
view source
print?
[Link] zone = [Link]("Europe/Paris");
[Link] calendrierCopte = [Link](zone)
[Link] dt = new DateTime(calendrierCopte);
Attention : une instance de type Chonology à null est toujours considérée par l'API Joda Time
comme une instance de type Chronology par défaut (système calendaire ISO8601 et fuseau horaire
par défaut).
Le fuseau horaire permet de préciser un décalage, positif ou négatif, par rapport à l'UTC. La valeur
de ce décalage peut varier en fonction de l'utilisation de l'heure d'été/d'hiver (DST en anglais :
Daylight Saving Time).
Un fuseau horaire est utilisé pour calculer une heure par rapport à une position géographique.
Lors du calcul de certaines données temporelles, il peut être important de connaître le lieu où un
point dans le temps doit être représenté. Cela se fait avec un fuseau horaire car selon celui-ci, la
représentation du point dans un calendrier peut être différente.
C'est la raison pour laquelle une instance de type Chronology encapsule une instance de type
DateTimeZone. Si aucun fuseau horaire n'est précisé, alors c'est le fuseau horaire par défaut qui est
utilisé : c'est celui de la machine hôte.
La méthode forId() de la classe DateTimeZone est une fabrique qui permet de créer une instance
en passant en paramètre l'identifiant de la zone concernée.
Exemple :
view source
print?
[Link] zone = [Link]("Europe/Paris");
La méthode getDefault() permet d'obtenir une instance de type DateTimeZone qui encapsule le
fuseau horaire par défaut qui correspond à celui du système hôte
Exemple :
view source
print?
[Link] zone = [Link]();
[Link](zone);
C'est ce fuseau horaire qui sera utilisé par défaut par l'API Joda Time si aucun fuseau horaire n'est
explicitement précisé.
La méthode statique setDefault() peut être utiliser pour modifier le fuseau horaire qui doit être
utilisé par défaut.
Les fuseaux horaires sont des concepts qui évoluent fréquemment en fonction du contexte
politique du pays concerné. Le JDK et Joda Time utilise la TZ Database. Comme le JDK peut ne pas
être mis à jour, il est possible de mettre à jour la base incluse dans Joda Time et de recompiler la
bibliothèque pour tenir compte des mises à jour dans la définition des fuseaux horaires.
Le système calendaire ISO8601 est une normalisation basée sur le calendrier Grégorien afin de
faciliter les échanges de date/heures entre applications, systèmes et pays.
Ce système calendaire est implémenté dans la classe ISOChronology qui est immuable.
Ce standard définit :
La classe ISOChronology est l'implémentation utilisée par défaut par Joda Time : si une instance de
type Chronology fournie à l'API est null, alors c'est une instance de type ISOChronology qui sera
utilisée.
Pour obtenir une instance de type ISOChronology, il faut invoquer sa méthode getInstance().
Exemple :
view source
print?
[Link] chrono = [Link]();
[Link] dt = new DateTime(2012, 12, 25, 0, 0, 0, 0, chrono);
Le calendrier Bouddhiste ne possède qu'une seule ère et ces années possèdent un décalage de 543
ans par rapport au calendrier Grégorien.
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.2555-12-25T00:00:00.000+01:00
Le calendrier copte est basé sur le calendrier utilisé dans l'Ancien Egypte. Il est utilisé par l'Eglise
Orthodoxe Copte.
Le calendrier Copte repose sur 12 mois de 30 jours chacun suivi d'une période de 5 ou 6 jours.
L'année contient donc 365 ou 366 jours. Les années bissextiles sont celles qui durent 366 jours :
elles surviennent tous les 4 ans.
La classe CopticChronology implémente le calendrier Copte. Dans cette implémentation, les 5ou 6
jours complémentaires sont stockées dans un treizième mois.
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.1729-04-16T00:00:00.000+01:00
La classe EthiopicChronology implémente le calendrier Ethiopien. Pour obtenir une instance, il faut
invoquer sa méthode getInstance().
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.2005-04-16T00:00:00.000+01:00
Le calendrier Grégorien est le calendrier majoritairement utilisé pour les traitements métiers. Ce
calendrier a remplacé le calendrier Julien. Le calendrier Grégorien définit une année bissextile tous
les quatre ans avec deux exceptions : les années divisibles par 100 ne sont pas bissextiles sauf
celles divisibles par 400.
Ce système calendaire est compatible avec le système calendaire ISO même si la gestion du siècle
est légèrement différente. Il n'est utilisable que pour des dates postérieures à 1583.
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.2012-12-25T00:00:00.000+01:00
Le système calendaire Grégorien/Julien est la combinaison des systèmes calendaires utilisés par les
chrétiens et les romains. Ce système calendaire est utilisé pour des traitements de dates
historiques puisqu'il permet de gérer les dates du calendrier Julien puis celles du calendrier
Grégorien. La date de basculement de calendrier est configurable : elle est par défaut au
15/10/1582 comme la définit le pape Grégory XIII.
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.2012-12-25T00:00:00.000+01:00
Une des surcharge de la méthode getInstance() permet de préciser le point dans le temps où le
calendrier Grégorien doit être utilisé.
Le calendrier Islamique est basé sur les cycles de la Lune : il est utilisé dans de nombreux pays
musulmans.
Exemple :
view source
print?
[Link] noel12 = new DateTime("2012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.1434-02-11T00:00:00.000+01:00
La classe JulianChronology implémente le système calendaire Julien. Pour obtenir une instance, il
faut invoquer sa méthode getInstance().
Exemple :
view source
print?
[Link] noel12 = new DateTime("1012-12-25");
[Link] dt = [Link]([Link]());
[Link](dt);
Résultat :
view source
print?
1.1012-12-19T00:00:00.000+00:09:21
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
04.
[Link] class TestJodaTime {
06. public static void main(String[] args) {
07. DateTime dateTime = new DateTime(2012, 1, 1, 0, 0, 0, 0);
08. [Link]([Link](30)
09. .toString("dd/MM/yyyy HH:mm:[Link]"));
10. }
11.}
Exemple :
view source
print?
[Link] [Link];
02.
[Link] [Link];
[Link] [Link];
05.
[Link] class TestJodaTime {
07.
08. public static void main(String[]args) {
09. Calendar calendar = [Link]();
10. [Link](0);
11. [Link](2012, [Link], 1, 0, 0, 0);
12. SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy
HH:mm:[Link]");
13. [Link](Calendar.DAY_OF_MONTH, 30);
14. [Link]([Link]([Link]()));
15. }
16.}
Cette section propose plusieurs exemples pour illustrer certaines fonctionnalités de manipulation
de dates proposées par Joda Time.
Exemple :
view source
print?
[Link] now = new DateTime();
[Link] limite = [Link](1).plusMinutes(30);
[Link](limite);
Exemple :
view source
print?
[Link] dernierJourduMoisPrecedent = [Link]() // aujourd'hui
2. .minusMonths(1) // on retire un mois
3. .dayOfMonth() // on récupère le jour du mois
4. .withMaximumValue(); // on lui affecte sa valeur maximale
[Link](dernierJourduMoisPrecedent);
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link] result =
[Link]().setCopy([Link]);
[Link](result);
Résultat :
view source
print?
1.2012-12-24T00:00:00.000+01:00
Exemple :
view source
print?
[Link] datePaiement = [Link]() // aujourd'hui
2. .plusDays(90) // on ajoute 90 jours
3. .dayOfMonth() // on récupère le jour du mois
4. .withMaximumValue(); // on lui affecte sa valeur maximale
[Link](datePaiement);
Pour calculer le nombre de jours entre deux dates, il est possible d'utiliser la méthode
daysBetween() de la classe Days.
Exemple :
view source
print?
[Link] dateTimeDeb = new DateTime("2012-12-25");
[Link] dateTimeFin = new DateTime("2012-12-31");
[Link] d = [Link](dateTimeDeb, dateTimeFin);
[Link] days = [Link]();
[Link](days);
Résultat :
view source
print?
1.6
Pour obtenir le nombre des différents éléments qui composent l'écart entre deux dates, il est
possible d'utiliser la classe Period.
Exemple :
view source
print?
[Link] dateTimeDeb = new DateTime("2011-11-25");
[Link] dateTimeFin = new DateTime("2012-12-31");
[Link] p = new Period(dateTimeDeb, dateTimeFin,
[Link]());
[Link]("annees " + [Link]());
[Link]("semaines " + [Link]());
[Link]("jours " + [Link]());
Résultat :
view source
print?
[Link] 1
[Link] 5
[Link] 1
Exemple :
view source
print?
[Link] aujourdhui = [Link]();
[Link] nouvelAn = [Link](1).withDayOfYear(1);
[Link] nbJours = [Link](aujourdhui, nouvelAn);
[Link]([Link]());
Les classes de Joda Time qui encapsulent une date/heure acceptent comme paramètre dans une
surcharge de leur constructeur un objet de type [Link] ou [Link].
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link] calendar = [Link]([Link]());
[Link](calendar);
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link] calendar = [Link]();
[Link](calendar);
La méthode toDate() de la classe AbstractInstant permet de convertir l'objet Joda Time en une
instance de type [Link].
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link] date = [Link]();
[Link](date);
Pour convertir un objet de type LocalDate en un objet de type Date ou Calender, il est nécessaire
de le convertir au préalable en une instance de type DateMidnight en invoquant la méthode
toDateMidnight().
Exemple :
view source
print?
[Link] localDate = new LocalDate("2012-12-25");
[Link] date = [Link]().toDate();
[Link](date);
Le plus simple pour formater un objet de type DateTime est d'invoquer sa méthode toString().
Il est possible de fournir en paramètre de la méthode toString() une chaîne de caractères qui
contient le format désiré. Le format à utiliser est quasiment le même que la classe
SimpleDateFormat du JDK.
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link]([Link]("dd-MM-yyyy HH:mm:ss"));
[Link]([Link]("EEEE dd MMMM yyyy HH:mm:ss"));
[Link]([Link]("MM/dd/yyyy HH:mm ZZZZ"));
[Link]([Link]("MM/dd/yyyy HH:mm Z"));
Résultat :
view source
print?
1.25-12-2012 00:00:00
[Link] 25 décembre 2012 00:00:00
3.12/25/2012 00:00 Europe/Paris
4.12/25/2012 00:00 +0100
La classe ISODateTimeFormat est une fabrique pour obtenir des instances de type
DateTimeFormatter pour différent format de dates respectant la norme ISO8601.
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link]([Link]([Link]()))
;
[Link]([Link](ISODateTimeFormat
4. .basicDateTimeNoMillis()));
[Link]([Link](ISODateTimeFormat
6. .basicOrdinalDateTime()));
[Link]
8. .println([Link]([Link]()));
Résultat :
view source
print?
1.20121225T000000.000+0100
2.20121225T000000+0100
3.2012360T000000.000+0100
4.2012W522T000000.000+0100
La classe DateTimeFormatter est utilisée pour formater et extraire une date d'une chaîne de
caractères.
La classe DateTimeFormatter est thread-safe et immuable. Elle contient un cache en interne qui
maintient des instances ce qui évite d'avoir à créer une instance à chaque utilisation.
La classe DateTimeFormat propose plusieurs méthodes qui sont des fabriques pour obtenir des
instances de type DateTimeFormatter. La plupart de ces méthodes proposent des formats
standards. La méthode forPattern() permet de préciser explicitement le format de la date/heure à
utiliser.
Exemple :
view source
print?
[Link] formatter = DateTimeFormat
2. .forPattern("dd-MM-yyyy HH:mm:ss");
[Link] dateTime = [Link]("25-12-2012 00:00:00");
[Link](dateTime);
Résultat :
view source
print?
1.2012-12-25T00:00:00.000+01:00
La méthode withLocale() permet de préciser la locale à utiliser et renvoie une nouvelle instance.
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link] formatter = DateTimeFormat
3. .forPattern("EEEE dd MMMM yyyy HH:mm:ss");
[Link] frenchFmt = [Link]([Link]);
[Link]([Link](dateTime));
[Link] englishFmt = [Link]([Link]);
[Link]([Link](dateTime));
Résultat :
view source
print?
[Link] 25 décembre 2012 00:00:00
[Link] 25 December 2012 00:00:00
Pour des formats très spécifiques, Joda Time propose la classe DateTimeFormatterBuilder qui
implémente le motif de conception builder pour créer une instance de type DateTimeFormatter.
Exemple :
view source
print?
[Link] dateTime = new DateTime("2012-12-25");
[Link] fmt = new
DateTimeFormatterBuilder().appendDayOfMonth(2)
3. .appendLiteral(' ').appendMonthOfYearShortText().appendLiteral(' ')
4. .appendTwoDigitYear(1949).toFormatter();
[Link]([Link](dateTime));
Résultat :
view source
print?
1.25 déc. 12
La méthode clear() permet de réinitialiser la configuration.
Exemple :
view source
print?
[Link] noel13 = new DateTime("2013-12-25");
[Link]([Link]());
[Link](new Date());
[Link]([Link]());
05.
06.// remettre la date de Joda Time à la date systeme
[Link]();
[Link]([Link]());
09.
10.// modifier la date de Joda Time à la veille
[Link](1000 * 60 * 60 * 24);
[Link]([Link]());
Attention : la date/heure de la JVM obtenue avec les API du JDK n'est pas modifiée.
Comme la plupart des objets Joda Time sont immuables, leur modification implique la création
d'une nouvelle instance à chaque méthode invoquée. Si plusieurs champs doivent être modifiés, il
peut être utile d'utiliser une version mutable afin de limiter le nombre d'instances créées.
Joda Time propose les classes MutableDateTime, MutableInterval et MutablePeriod.
Exemple :
view source
print?
[Link] noel13 = new DateTime("2013-12-25");
[Link] mdt = [Link]();
[Link](1);
[Link](2012);
[Link](1);
[Link] result = [Link]();
[Link](result);
La classe DateTime propose aussi la méthode toMutableDateTime() qui renvoie une instance de
type MutableDateTime qui encapsule la date/heure de l'objet.
La classe MutableDateTime propose de nombreuses méthodes qui ne renvoient rien pour modifier
un champ de la date/heure encapsulée.
La méthode toDateTime() permet de renvoyer une instance de type DateTime qui encapsule la
date/heure de l'objet.
La classe FastDateFormat offre des fonctionnalités de formatage d'une date similaires à celles de la
clase SimpleDateFormat : elle propose cependant un support du timezone différent dans le
formatage.
FastDateFormat ne permet que le formatage d'une date, elle ne permet pas comme la classe
SimpleDateFormat le fait, d'extraire une date d'une chaîne de caractères.
Exemple :
view source
print?
[Link] [Link]; import [Link];
02.
[Link] [Link];
04.
[Link] class TestSdf {
06.
07. public static void main(final String[] args) {
08. final String format = "dd-MM-yyyy HH:mm:[Link]";
09. SimpleDateFormat sdf = new SimpleDateFormat(format);
10. FastDateFormat fdf = [Link](format);
11. final Date d = new Date();
12. final int nblteration = 1000000;
13. long start = 0;
14. long tempsTrt = 0;
15.
16. start = [Link]();
17. for (int i = 0; i < nblteration; i++) {
18. fdf = [Link](format);
19. [Link]([Link]());
20. [Link](d);
21. }
22. tempsTrt = [Link]() - start;
23. [Link]("FastDateFormat : " + tempsTrt + " ms");
24.
25. start = [Link]();
26. for (int i = 0; i < nblteration; i++) {
27. sdf = new SimpleDateFormat(format);
28. [Link]([Link]());
29. [Link](d);
30. }
31. tempsTrt = [Link]() - start;
32. [Link]("SimpleDateFormat : " + tempsTrt + " ms");
33. }
34.
35.}
Résultat :
view source
print?
[Link] : 2041 ms
[Link] : 5360 ms
Elle propose des constantes pour des instances de FastDateFormat avec des motifs de formatage
courants en ISO08601 et SMTP :
Elle propose de nombreuses méthodes notamment plusieurs surcharges des méthodes format() et
formatUTC() acceptant la date à formater en plusieurs formats (long, Date, Calendar) et permettant
de préciser le format et la Locale à utiliser.
Exemple :
view source
print?
[Link] static void main(final String[] args) {
02. final Date today = new Date();
03.
04. /*
05. * formatage en ISO8601 sans timezone : yyyy-MM-dd'T'HH:mm:ss.
06. */
07. String timestamp = DateFormatUtils.ISO_DATETIME_FORMAT.format(today);
08. [Link]("timestamp = " + timestamp);
09.
10. /*
11. * formatage en ISO8601 avec timezone : yyyy-MM-dd' T'HH:mm:ssZZ.
12. */
13. timestamp =
DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(today)
14. [Link]("timestamp = " + timestamp);
15.
16. /*
17. * formatage au format SMTP : EEE, dd MMM yyyy HH:mm:ss Z (Locale US).
18. */
19. timestamp = DateFormatUtils.SMTP_DATETIME_FORMAT.format(today);
20. [Link]("timestamp = " + timestamp); }