Niveau : | Elémentaire |
La manipulation des dates n'est pas toujours facile à mettre en oeuvre :
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 : |
Date aujourdhui = SystemClockFactory.getDatetime();
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 propose aussi des fonctionnalités relatives au traitement des dates spécifiquement dédiées à la finance.
Ce chapitre contient plusieurs sections :
En Java 1.0, la classe java.util.Date était seule responsable de l'encapsulation et de la manipulation d'une date.
A partir de Java 1.1, la responsabilité de la gestion et des traitements sur les dates est répartie sur plusieurs classes :
Les classes permettant la mise en oeuvre des dates sont dans le package java.util exceptées celles relatives à leur conversion de et vers une chaîne de caractères qui sont dans le package java.text.
Le package java.sql 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 : |
System.out.println(System.currentTimeMillis());
System.out.println(new java.util.Date().getTime());
System.out.println(Calendar.getlnstance().getTimelnMillis() );
System.out.println(Calendar.getlnstance().getTime().getTime ())
L'API de gestion des dates en Java est particulièrement propice à la confusion et à l'obtention d'erreurs potentielle :
Cette classe encapsule, sous la forme d'une variable de type long, un point dans le temps qui est représenté par le nombre de millisecondes écoulées entre le 1 janvier 1970 à 00 heure 00 GMT et l'instant concerné.
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 System.currentTimeMillis() ce qui rend sa précision dépendante du système d'exploitation.
La classe Calendar encapsule un point dans le temps (une Date sous la forme d'une variable de type long) et permet une représentation et une manipulation dans un calendrier et un fuseau horaire.
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 correspondant aux constantes Calendar.JANUARY à Calendar.DECEMBER. 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 : |
Calendar calendar = Calendar.getInstance();
if ( calendar.get( Calendar.MONTH )==Calendar.JANUARY ) {
system.out.prinln("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 composent :
La date encapsulée dans Calendar peut être manipulée de deux façons :
La classe java.util.GregorianCalendar est la seule implémentation concrète de la classe Calendar fournie en standard. Cette implémentation correspond au calendrier Grégorien.
La méthode isLeapYear() permet de savoir si l'année encapsulée par la classe est bissextile.
La classe abstraite TimeZone et sa sous-classe SimpleTimeZone encapsulent un fuseau horaire.
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 pays considéré.
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().
La clase abstraite DateFormat propose les fonctionnalités de base pour interpréter et formater une date sous la forme d'une chaîne de caractères.
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 renvoyant des instances de type DateFormat.
La méthode format() permet de formater une date en chaîne de caractères.
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.
La classe SimpleDateFormat permet de formater et d'analyser une date en tenant compte d'une Locale. Elle hérite de la classe abstraite DateFormat.
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 :
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 : |
package com.jmd.test.dej.date;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
public class TestFormaterDate2 {
/**
* @param args
*/
public static void main(String[] args) {
Date aujourdhui = new Date();
DateFormat shortDateFormat = DateFormat.getDateTimeInstance(
DateFormat.SHORT,
DateFormat.SHORT);
DateFormat shortDateFormatEN = DateFormat.getDateTimeInstance(
DateFormat.SHORT,
DateFormat.SHORT, new Locale("EN","en"));
DateFormat mediumDateFormat = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM,
DateFormat.MEDIUM);
DateFormat mediumDateFormatEN = DateFormat.getDateTimeInstance(
DateFormat.MEDIUM,
DateFormat.MEDIUM, new Locale("EN","en"));
DateFormat longDateFormat = DateFormat.getDateTimeInstance(
DateFormat.LONG,
DateFormat.LONG);
DateFormat longDateFormatEN = DateFormat.getDateTimeInstance(
DateFormat.LONG,
DateFormat.LONG, new Locale("EN","en"));
DateFormat fullDateFormat = DateFormat.getDateTimeInstance(
DateFormat.FULL,
DateFormat.FULL);
DateFormat fullDateFormatEN = DateFormat.getDateTimeInstance(
DateFormat.FULL,
DateFormat.FULL, new Locale("EN","en"));
System.out.println(shortDateFormat.format(aujourdhui));
System.out.println(mediumDateFormat.format(aujourdhui));
System.out.println(longDateFormat.format(aujourdhui));
System.out.println(fullDateFormat.format(aujourdhui));
System.out.println("");
System.out.println(shortDateFormatEN.format(aujourdhui));
System.out.println(mediumDateFormatEN.format(aujourdhui));
System.out.println(longDateFormatEN.format(aujourdhui));
System.out.println(fullDateFormatEN.format(aujourdhui));
}
}
Résultat : |
27/06/06 21:36
27 juin 2006 21:36:30
27 juin 2006 21:36:30 CEST
mardi 27 juin 2006 21 h 36 CEST
6/27/06 9:36 PM
Jun 27, 2006 9:36:30 PM
June 27, 2006 9:36:30 PM CEST
Tuesday, 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.
Lettre |
Description |
Exemple |
G |
Era |
AD (Anno Domini), BC (Before Christ) |
y |
Année |
06 ; 2006 |
M |
Mois dans l'année |
Septembre; Sept.; 07 |
w |
Semaine dans l'année |
34 |
W |
Semaine dans le mois |
2 |
D |
Jour dans l'année |
192 |
d |
jour dans le mois |
23 |
F |
Jour de la semaine dans le mois |
17 |
E |
Jour de la semaine |
Mercredi; Mer. |
a |
Marqueur AM/PM (Ante/Post Meridiem) |
PM, AM |
H |
Heure (0-23) |
23 |
k |
Heure (1-24) |
24 |
K |
Heure en AM/PM (0-11) |
6 |
h |
Heure en AM/PM (1-12) |
7 |
m |
Minutes |
59 |
s |
Secondes |
59 |
S |
Millisecondes |
12564 |
z |
Zone horaire générale |
CEST; Heure d'été d'Europe centrale |
Z |
Zone horaire (RFC 822) |
+0200 |
Ces caractères peuvent être répétés pour préciser le format à utiliser :
Exemple : |
package com.jmd.test.dej.date;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class TestFormaterDate {
public static void main(String[] args) {
SimpleDateFormat formater = null;
Date aujourdhui = new Date();
formater = new SimpleDateFormat("dd-MM-yy");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("ddMMyy");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("yyMMdd");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("h:mm a");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("K:mm a, z");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("hh:mm a, zzzz");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("EEEE, d MMM yyyy");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("'le' dd/MM/yyyy 'à' hh:mm:ss");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("'le' dd MMMM yyyy 'à' hh:mm:ss");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("dd MMMMM yyyy GGG, hh:mm aaa");
System.out.println(formater.format(aujourdhui));
formater = new SimpleDateFormat("yyyyMMddHHmmss");
System.out.println(formater.format(aujourdhui));
}
}
Résultat : |
27-06-06
270606
060627
9:37 PM
9:37 PM, CEST
09:37 PM, Heure d'été d'Europe centrale
mardi, 27 juin 2006
le 27/06/2006 à 09:37:10
le 27 juin 2006 à 09:37:10
27 juin 2006 ap. J.-C., 09:37 PM
20060627213710
Il existe plusieurs constructeurs de la classe SimpleDateFormat :
Constructeur |
Rôle |
SimpleDateFormat() |
Constructeur par défaut utilisant le modèle par défaut et les symboles de formatage de dates de la Locale par défaut |
SimpleDateFormat(String) |
Constructeur utilisant le modèle fourni et les symboles de formatage de dates de la Locale par défaut |
SimpleDateFormat(String, DateFormatSymbols) |
Constructeur utilisant le modèle et les symboles de formatage de dates fournis |
SimpleDateFormat(String, Locale) |
Constructeur utilisant le modèle fourni et les symboles de formatage de dates de la Locale fournie |
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 : |
package com.jmd.test.dej.date;
import java.text.DateFormatSymbols;
import java.util.Locale;
public class TestFormaterDate3 {
public static void main(String[] args) {
DateFormatSymbols dfsFR = new DateFormatSymbols(Locale.FRENCH);
DateFormatSymbols dfsEN = new DateFormatSymbols(Locale.ENGLISH);
String[] joursSemaineFR = dfsFR.getWeekdays();
String[] joursSemaineEN = dfsEN.getWeekdays();
StringBuffer texteFR = new StringBuffer("Jours FR ");
StringBuffer texteEN = new StringBuffer("Jours EN ");
for (int i = 1; i < joursSemaineFR.length; i++) {
texteFR.append(" : ");
texteFR.append(joursSemaineFR[i]);
texteEN.append(" : ");
texteEN.append(joursSemaineEN[i]);
}
System.out.println(texteFR);
System.out.println(texteEN);
texteFR = new StringBuffer("Mois courts FR ");
texteEN = new StringBuffer("Mois courts EN ");
String[] moisCourtsFR = dfsFR.getShortMonths();
String[] moisCourtsEN = dfsEN.getShortMonths();
for (int i = 0; i < moisCourtsFR.length - 1; i++) {
texteFR.append(" : ");
texteFR.append(moisCourtsFR[i]);
texteEN.append(" : ");
texteEN.append(moisCourtsEN[i]);
}
System.out.println(texteFR);
System.out.println(texteEN);
texteFR = new StringBuffer("Mois FR ");
texteEN = new StringBuffer("Mois EN ");
String[] moisFR = dfsFR.getMonths();
String[] moisEN = dfsEN.getMonths();
for (int i = 0; i < moisFR.length - 1; i++) {
texteFR.append(" : ");
texteFR.append(moisFR[i]);
texteEN.append(" : ");
texteEN.append(moisEN[i]);
}
System.out.println(texteFR);
System.out.println(texteEN);
}
}
Résultat : |
Jours FR : dimanche : lundi : mardi : mercredi : jeudi : vendredi : samedi
Jours EN : Sunday : Monday : Tuesday : Wednesday : Thursday : Friday : Saturday
Mois courts FR : janv. : févr. : mars : avr. : mai : juin : juil. : août : sept. : oct.
: nov. : déc.
Mois courts EN : Jan : Feb : Mar : Apr : May : Jun : Jul : Aug : Sep : Oct : Nov : Dec
Mois FR : janvier : février : mars : avril : mai : juin : juillet : août : septembre :
octobre : novembre : décembre
Mois EN : January : February : March : April : May : June : July : August : September :
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 : |
package com.jmd.test.dej.date;
import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestFormaterDate4 {
public static void main(String[] args) {
Date aujourdhui = new Date();
DateFormatSymbols monDFS = new DateFormatSymbols();
String[] joursCourts = new String[] {
"",
"Di",
"Lu",
"Ma",
"Me",
"Je",
"Ve",
"Sa" };
monDFS.setShortWeekdays(joursCourts);
SimpleDateFormat dateFormat = new SimpleDateFormat(
"EEE dd MMM yyyy HH:mm:ss",
monDFS);
System.out.println(dateFormat.format(aujourdhui));
}
}
Résultat : |
Ma 27 juin 2006 21:38:22
Attention : il faut consulter la documentation de l'API pour connaître précisément le contenu et l'ordre des éléments fournis sous la forme de tableaux aux setters de la classe. Dans l'exemple, ci-dessus, les jours de la semaine commencent par Dimanche.
La méthode applyPattern() permet de modifier le modèle d'un objet SimpleDateFormat.
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 : |
package com.jmd.test.dej.date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestParserDate {
public static void main(String[] args) {
Date date = null;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
String date1 = "22/06/2006";
String date2 = "22062006";
try {
date = simpleDateFormat.parse(date1);
System.out.println(date);
date = simpleDateFormat.parse(date2);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Résultat : |
Thu Jun 22 00:00:00 CEST 2006
java.text.ParseException: Unparseable date: "22062006"
at java.text.DateFormat.parse(Unknown Source)
at com.jmd.test.dej.date.TestParserDate.main(TestParserDate.java:19)
Ces trois classes héritent de la classe java.util.Date pour encapsuler des données correspondant aux types DATE, TIME et TIMESTAMP de la norme SQL 92.
La classe java.sql.Date n'encapsule que la partie date en ignorant la partie horaire du point dans le temps qu'elle encapsule.
La classe java.sql.Time, elle, n'encapsule que la partie horaire en ignorant la partie date du point dans le temps qu'elle encapsule.
La classe java.sql.TimeStamp encapsule encapsule un instant exprimé en millisecondes et des informations permettant une expression de cet instant avec une précision à la nanoseconde.
Ces trois méthodes redéfinissent la méthode toString() pour permettre une représentation respectant le standard SQL 92.
Exemple : |
final java.sql.Date dateSQL = new java.sql.Date(new Date().getTime()) ;
System.out.println(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.
Cette section présente des portions de code pour répondre à des besoins courants de manipulations de dates.
Formater une date :
Exemple : |
protected static final SimpleDateFormat dateFormat =
new SimpleDateFormat("dd/MM/yyyy");
protected static final SimpleDateFormat dateHeureFormat =
new SimpleDateFormat("dd/MM/yyyy hh:mm:ss");
public static String formatterDate(Date date) {
return dateFormat.format(date);
}
public static String formatterDateHeure(Date date) {
return dateFormatHeure.format(date);
}
Extraire une date d'une chaîne de caractères :
Exemple : |
DateFormat df = new SimpleDateFormat("dd-MM-yyyy");
Date date=null;
try {
date= df.parse("25-12-2010");
} catch (ParseException e){
e.printstacktrace();
}
Ajouter/retrancher des jours à une date :
Exemple : |
public static Date ajouterJour(Date date, int nbJour) {
Calendar cal = Calendar.getInstance();
cal.setTime(date.getTime();
cal.add(Calendar.DATE, nbJour);
return cal.getTime();
}
ou
Exemple : |
public static Date ajouterJour(Date date, int nbJour) {
Calendar cal = Calendar.getlnstance();
cal.setTime(date.getTime();
cal.add(Calendar.DAY_OF_MONTH, nbJour);
return cal.getTime();
}
Pour retrancher des jours, il faut fournir un paramètre négatif au nombre de jours.
Ajouter/retrancher des mois à une date :
Exemple : |
public static Date ajouterMois(Date date, int nbMois) {
Calendar cal = Calendar.getInstance();
cal.setTime(date.getTime();
cal.add(Calendar.MONTH, nbMois);
return cal.getTime();
}
Pour retrancher des mois, il faut fournir un paramètre négatif au nombre de mois.
Ajouter/retrancher des années à une date :
Exemple : |
public static Date ajouterAnnee(Date date, int nbAnnee) {
Calendar cal = Calendar.getInstance();
cal.setTime(date.getTime());
cal.add(Calendar.YEAR, nbAnnee);
return cal.getTime();
}
Pour retrancher des années, il faut fournir un paramètre négatif au nombre d'années.
Ajouter/retrancher des heures à une date :
Exemple : |
public static Date ajouterHeure(Date date, int nbHeure) {
Calendar cal = Calendar.getInstance();
cal.setTime(date.getTime());
cal.add(Calendar.HOUR, nbHeure);
return cal.getTime();
}
Pour retrancher des heures, il faut fournir un paramètre négatif au nombre d'heures.
Ajouter/retrancher des minutes à une date :
Exemple : |
public static Date ajouterMinute(Date date, int nbMinute) {
Calendar cal = Calendar.getInstance();
cal.setTime(date.getTime());
cal.add(Calendar.MINUTE, nbMinute);
return cal.getTime();
}
Pour retrancher des minutes, il faut fournir un paramètre négatif au nombre de minutes.
Ajouter/retrancher des secondes à une date :
Exemple : |
public static Date ajouterSeconde(Date date, int nbSeconde) {
Calendar cal = Calendar.getlnstance();
cal.setTime(date.getTime());
cal.add(Calendar.SECOND, nbSeconde);
return cal.getTime();
}
Pour retrancher des secondes, il faut fournir un paramètre négatif au nombre de secondes.
La classe SimpleDateFormat permet de formater une date pour lui donner une représentation textuelle dans un format donné ou de parser une chaîne de caractères pour extraire une date dans un format donné.
Le constructeur de la classe SimpleDateFormat attend en paramètre une chaîne de caractères qui précise le format à utiliser durant les traitements de formatage et de parsing.
La méthode format() permet de formater la date fournie en paramètre.
Exemple : |
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
String dateStr = simpleDateFormat.format(new Date());
System.out.println(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 : |
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMMM yyyy zzzz G", Locale.FRENCH);
String dateStr = simpleDateFormat.format(new Date());
System.out.println(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 : |
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
Date date = simpleDateFormat.parse("25/12/2010");
System.out.println(date);
Par défaut, SimpleDateFormat travaille 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 : |
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd/MM/yyyy");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("PST"));
Date date = simpleDateFormat.parse("25/12/2010");
System.out.println(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 : |
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd MMMM yy", Locale.FRENCH);
Date date = simpleDateFormat.parse("25-12-02");
System.out.println(date);
Date debut20emeSiecle = new GregorianCalendar(1901,1,1).getTime();
simpleDateFormat.set2DigitYearStart(debut20emeSiecle);
date = simpleDateFormat.parse("25-12-02");
System.out.println(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, sans générer d'erreur, SimpleDateFormat va tenter d'extraire une date qui potentiellement ne correspond pas du tout.
Exemple : |
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy", Locale.FRENCH);
Date date = simpleDateFormat.parse("31-04-10");
System.out.println(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 : |
SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("dd-MM-yyyy", Locale.FRENCH);
simpleDateFormat.setLenient(false);
Date date = simpleDateFormat.parse("31-04-10");
System.out.println(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.
L'utilisation d'une même instance de SimpleDateFormat dans un contexte multithreads implique donc qu'il est nécessaire de prendre des précautions : le résultat peut être aléatoire lors du parsing et du formatage d'une date :
La classe SimpleDateFormat présente deux faiblesses lors de sa mise en oeuvre :
Exemple : |
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static final Date parse(String date) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
return simpleDateFormat.parse(date);
}
public static final String format(Date date) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
return simpleDateFormat.format(date);
}
}
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 ce premier souci, il est possible de créer une instance de classe statique qui permettra de n'avoir qu'un seul objet.
Exemple : |
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
public static final Date parse(String date) throws ParseException {
return simpleDateFormat.parse(date);
}
public static final String format(Date date) throws ParseException {
return simpleDateFormat.format(date);
}
}
Cette solution fréquemment utilisée permet de réduire le nombre d'instances créées. Malheureusement, comme indiqué dans la JavaDoc, elle ne fonctionne pas dans un environnement multithread puisque la classe SimpleDateFormat n'est pas threadsafe. L'utilisation de la classe ci-dessus dans un contexte multithread peut donner des résultats aléatoires.
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 : |
import java.text.ParseException;
public class TestSimpleDateFormat {
public static void main(String[] args) {
final String[] dates = new String[] {"15-01-2000", "28-02-2005", "20-04-2005",
"31-07-2015" };
Runnable runnable = new Runnable() { public void run() {
try {
for (int j = 0; j < 1000; j++) {
for (int i = 0; i < 2; i++) {
String date = DateUtil.format(DateUtil.parse(dates[i]));
if (!(dates[i].equals(date))) {
throw new ParseException(dates[i] + " =>"+ date, 0);
}
}
}
} catch (ParseException e) {
e.printStackTrace();
}
new Thread(runnable).start();
Runnable runnable2 = new Runnable() {
public void run() {
try {
for (int j = 0; j < 1000; j++) {
for (int i = 0; i < 2; i++) {
String date = DateUtil.format(DateUtil.parse(dates[i]));
if (!(dates[i].equals(date))) {
throw new ParseException(dates[i] + " =>"+ date, 0);
}
}
}
} catch (ParseException e) {
e.printStackTrace();
}
}
};
new Thread(runnable2).start();
}
}
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.
Il est possible de sécuriser l'utilisation de l'instance de SimpleDateFormat en l'entourant d'un bloc synchronized dont le moniteur est l'instance de la classe SimpleDateFormat ou en définissant les méthodes utilisant l'instance synchronized. Ainsi, un seul thread pourra accéder à l'instance à la fois.
Exemple : |
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
public static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy");
public synchronized static final Date parse(String date) throws ParseException {
return simpleDateFormat.parse(date);
}
public synchronized static final String format(Date date) throws ParseException {
return simpleDateFormat.format(date);
}
}
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 : |
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static ThreadLocal<SimpleDateFormat> format = new ThreadLocal<SimpleDateFormat>() {
protected synchronized SimpleDateFormat initialValue() {
return new SimpleDateFormat("dd-MM-yyyy");
}
};
public static final Date parse(String date) throws ParseException {
return format.get().parse(date);
}
public static final String format(Date date) throws ParseException {
return format.get().format(date); }
}
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 : |
import java.lang.ref.SoftReference;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final ThreadLocal<SoftReference<DateFormat» format =
new ThreadLocal<SoftReference<DateFormat>>();
private static DateFormat getDateFormat() {
SoftReference<DateFormat> softRef = format.get();
if (softRef != null) {
final DateFormat result = softRef.get();
if (result != null) {
return result;
}
}
final DateFormat result = new SimpleDateFormat("dd-MM-yyyy");
softRef = new SoftReference<DateFormat>(result);
format.set(softRef);
return result;
}
public static final Date parse(final String date) throws ParseException {
return getDateFormat().parse(date);
}
public static final String format(final Date date) throws ParseException {
return getDateFormat().format(date);
}
}
Cette approche nécessite de recréer l'instance locale de SimpleDateFormat dans le cas où le ramasse-miettes aurait détruit la précédente.
Une autre solution peut être d'utiliser une API tierce telle 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 : |
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDate {
public static void main(final String[] args) {
final DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
Date d;
try {
d = df.parse("2010-01-15 07:23:30");
System.out.println(d);
} catch (final ParseException e) {
e.printStackTrace();
}
}
}
Résultat : |
Mon Nov 30 23:05:07 CET 2009
Pour que la classe SimpleDateFormat respecte strictement le format fourni et lève une exception de type java.text.ParseException, il faut invoquer la méthode setLenient() en lui passant la valeur false en paramètre.
Exemple : |
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDate {
public static void main(final String[] args) {
final DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
df.setLenient(false);
Date d;
try {
d = df.parse("2010-01-15 07:23:30");
System.out.println(d);
} catch (final ParseException e) {
e.printStackTrace();
}
}
}
Résultat : |
java.text.ParseException: Unparseable date: "2010-01-15 07:23:30"
at java.text.DateFormat.parse(DateFormat.java:337) at TestDate.main(TestDate.java:39)
La plupart des applications ont besoin à un moment ou à un autre de manipuler des données de type date ou heure. Le JDK fournit des classes pour permettre ces manipulations, notamment les classes Date et Calendar, mais leur utilisation n'est pas simple et généralement source d'erreurs.
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.
Joda Time propose au travers de son API :
Le but de Joda Time est de proposer une solution de remplacement aux classes de gestion des dates du JDK qui présentent 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 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 org.joda.time et org.joda.time.format.
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.
La plupart des classes de Joda Time sont immuables : les méthodes qui permettent d'effectuer des modifications les font sur une copie de l'objet qu'elles retournent.
Classe |
Rôle |
DateTime |
Equivalent de la classe Calendar |
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) |
Un instant est un point unique dans le temps dont la représentation est le nombre de millisecondes depuis le 1er janvier 1970 00 heure 00. Ceci rend un Instant compatible avec les classes Calendar et Date du JDK.
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.
Un instant est défini par l'interface ReadableInstant.
L'interface ReadableInstant décrit les fonctionnalités d'un objet qui encapsule un instant.
Les implémentations de cette interface peuvent être immuables ou non.
Joda Time propose plusieurs classes qui implémentent l'interface ReadableInstant dont :
Attention : l'interface ReadableInstant n'est qu'un sous-ensemble des fonctionnalités des classes qui l'implémentent. 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ée une instance qui encapsule l'instant courant représenté dans le système calendaire ISO et le fuseau horaire par défaut.
Exemple : |
DateTime datetime = new DateTime();
Plusieurs constructeurs permettent de préciser les éléments de la date/heure encapsulée : année, mois, jour, heure, minute, seconde.
Exemple : |
DateTime 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 :
Exemple : |
java.util.Date date = new Date();
long timeEnMs = date.getTime();
DateTime dateTime = new DateTime(timeEnMs);
Exemple : |
java.util.Date date = new Date();
DateTime dateTime = new DateTime(date);
Exemple : |
java.util.Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
DateTime dateTime = new DateTime(calendar);
Exemple : |
String timeString = "2012-12-25";
DateTime dateTime = new DateTime(timeString);
Exemple : |
DateTime dt = new DateTime("2012-10-28T16:23:13.324+01:00");
Il est ainsi facile de convertir une instance de type java.util.Date ou java.util.Calendar 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: java.util.ArrayList car Joda Time ne peut pas convertir l'instance de type Object fournie en paramètre en un instant.
Exemple : |
List liste = new ArrayList();
DateTime dateCourante = new DateTime(liste);
Plusieurs méthodes statiques permettent d'obtenir une instance de type DateTime.
Méthode |
Rôle |
static DateTime now() |
Obtenir une instance de type DateTime qui encapsule la date/heure système courante en utilisant le système calendaire ISO et le fuseau horaire par défaut |
static DateTime now(Chronology chronology) |
Obtenir une instance de type DateTime qui encapsule la date/heure système courante en utilisant le système calendaire fourni en paramètre et le fuseau horaire par défaut |
static DateTime now(DateTimeZone zone) |
Obtenir une instance de type DateTime qui encapsule la date/heure système courante en utilisant le système calendaire ISO et le fuseau horaire fourni en paramètre |
static DateTime parse(String str) |
Extraire une date/heure de la chaîne de caractères fournie en paramètre |
static DateTime parse(String str, DateTimeFormatter formatter) |
Extraire une date/heure de la chaîne de caractères fournie en utilisant le formateur passé en paramètre |
Les opérations de manipulations de date/heure encapsulées dans un objet de type DateTime peuvent être réalisées en invoquant des méthodes de DateTime ou en invoquant des méthodes sur les propriétés de l'objet DateTime. Cela rend ces opérations particulièrement pratiques et flexibles.
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 encapsulant le résultat de l'opération.
Méthode |
Rôle |
DateTime minus(long duration) |
Renvoyer une nouvelle instance de DateTime dont la durée fournie a été soustraite |
DateTime minus(ReadableDuration duration) |
Renvoyer une nouvelle instance de DateTime dont la durée fournie a été soustraite |
DateTime minus(ReadablePeriod period) |
Renvoyer une nouvelle instance de DateTime dont la période fournie a été soustraite |
DateTime minusDays(int days) |
Renvoyer une nouvelle instance de DateTime dont le nombre de jours fourni a été soustrait |
DateTime minusHours(int hours) |
Renvoyer une nouvelle instance de DateTime dont le nombre d'heures fourni a été soustrait |
DateTime minusMillis(int millis) |
Renvoyer une nouvelle instance de DateTime dont le nombre de millisecondes fourni a été soustrait |
DateTime minusMinutes(int minutes) |
Renvoyer une nouvelle instance de DateTime dont le nombre de minutes fourni a été soustrait |
DateTime minusMonths(int months) |
Renvoyer une nouvelle instance de DateTime dont le nombre de mois fourni a été soustrait |
DateTime minusSeconds(int seconds) |
Renvoyer une nouvelle instance de DateTime dont le nombre de secondes fourni a été soustrait |
DateTime minusWeeks(int weeks) |
Renvoyer une nouvelle instance de DateTime dont le nombre de semaines fourni a été soustrait |
DateTime minusYears(int years) |
Renvoyer une nouvelle instance de DateTime dont le nombre d'années fourni a été soustrait |
DateTime plus(long duration) |
Renvoyer une nouvelle instance de DateTime dont la durée fournie a été ajoutée |
DateTime plus(ReadableDuration duration) |
Renvoyer une nouvelle instance de DateTime dont la durée fournie a été ajoutée |
DateTime plus(ReadablePeriod period) |
Renvoyer une nouvelle instance de DateTime dont la période fournie a été ajoutée |
DateTime plusDays(int days) |
Renvoyer une nouvelle instance de DateTime dont le nombre de jours fourni a été ajouté |
DateTime plusHours(int hours) |
Renvoyer une nouvelle instance de DateTime dont le nombre d'heures fourni a été ajouté |
DateTime plusMillis(int millis) |
Renvoyer une nouvelle instance de DateTime dont le nombre de millisecondes fourni a été ajouté |
DateTime plusMinutes(int minutes) |
Renvoyer une nouvelle instance de DateTime dont le nombre de minutes fourni a été ajouté |
DateTime plusMonths(int months) |
Renvoyer une nouvelle instance de DateTime dont le nombre de mois fourni a été ajouté |
DateTime plusSeconds(int seconds) |
Renvoyer une nouvelle instance de DateTime dont le nombre de secondes fourni a été ajouté |
DateTime plusWeeks(int weeks) |
Renvoyer une nouvelle instance de DateTime dont le nombre de semaines fourni a été ajouté |
DateTime plusYears(int years) |
Renvoyer une nouvelle instance de DateTime dont le nombre d'années fourni a été ajouté |
DateMidnight toDateMidnight() |
Convertir en une instance de type DateMidnight en utilisant le même système calendaire |
LocalDate toLocalDate() |
Convertir en une instance de type LocalDate en utilisant le même système calendaire |
LocalDateTime toLocalDateTime() |
Convertir en une instance de type LocalDateTime en utilisant le même système calendaire |
DateTime withCenturyOfEra(int centuryOfEra) |
Renvoyer une nouvelle instance de DateTime dont le siècle a été modifié |
DateTime withChronology(Chronology newChronology) |
Renvoyer une nouvelle instance de DateTime qui utilise le système calendaire fourni en paramètre |
DateTime withDate(int year, int monthOfYear, int dayOfMonth) |
Renvoyer une nouvelle instance de DateTime dont l'année, le mois et le jour ont été modifié |
DateTime withDayOfMonth(int dayOfMonth) |
Renvoyer une nouvelle instance de DateTime dont le jour du mois a été modifié |
DateTime withDayOfWeek(int dayOfWeek) |
Renvoyer une nouvelle instance de DateTime dont le jour de la semaine a été modifié |
DateTime withDayOfYear(int dayOfYear) |
Renvoyer une nouvelle instance de DateTime dont le jour de l'année a été modifié |
DateTime withDurationAdded(long durationToAdd, int scalar) |
Renvoyer une nouvelle instance de DateTime à la laquelle la durée a été ajoutée |
DateTime withDurationAdded(ReadableDuration durationToAdd, int scalar) |
Renvoyer une nouvelle instance de DateTime à la laquelle la durée a été ajoutée |
DateTime withEra(int era) |
Renvoyer une nouvelle instance de DateTime dont l'ère a été modifiée |
DateTime withField(DateTimeFieldType fieldType, int value) |
Renvoyer une nouvelle instance de DateTime dont la propriété fournie a été modifiée |
DateTime withFieldAdded(DurationFieldType fieldType, int amount) |
Renvoyer une nouvelle instance de DateTime dont la propriété fournie a été ajoutée |
DateTime withHourOfDay(int hour) |
Renvoyer une nouvelle instance de DateTime dont l'heure du jour a été modifiée |
DateTime withMillis(long newMillis) |
Renvoyer une nouvelle instance de DateTime dont le nombre de millisecondes a été modifié |
DateTime withMillisOfDay(int millis) |
Renvoyer une nouvelle instance de DateTime dont le nombre de millisecondes du jour a été modifié |
DateTime withMillisOfSecond(int millis) |
Renvoyer une nouvelle instance de DateTime dont le nombre de millisecondes courant a été modifié |
DateTime withMinuteOfHour(int minute) |
Renvoyer une nouvelle instance de DateTime dont le nombre de minutes a été modifié |
DateTime withMonthOfYear(int monthOfYear) |
Renvoyer une nouvelle instance de DateTime dont le mois a été modifié |
DateTime withPeriodAdded(ReadablePeriod period, int scalar) |
Renvoyer une nouvelle instance de DateTime à laquelle la période a été ajoutée |
DateTime withSecondOfMinute(int second) |
Renvoyer une nouvelle instance de DateTime dont le nombre de secondes a été modifié |
DateTime withTime(int hourOfDay, int minuteOfHour, int secondOfMinute, int millisOfSecond) |
Renvoyer une nouvelle instance de DateTime dont les heures, les minutes, les secondes et les millisecondes ont été modifiées |
DateTime withYear(int year) |
Renvoyer une nouvelle instance de DateTime dont l'année a été modifiée |
DateTime withZone(DateTimeZone newZone) |
Renvoyer une nouvelle instance de DateTime qui utilise le fuseau horaire fourni en paramètre sans modifier l'instant encapsulé |
DateTime withZoneRetainFields(DateTimeZone newZone) |
Renvoyer une nouvelle instance de DateTime qui utilise le fuseau horaire fourni en paramètre sans modifier les champs encapsulés |
Exemple : |
DateTime dateCourante = new DateTime();
DateTime dateLimite = dateCourante.plusDays(2);
La classe DateTime propose plusieurs solutions pour obtenir individuellement chacun des champs de la date/heure encapsulée :
Les propriétés contenues dans un DateTime sont :
Propriété |
Rôle |
centuryOfEra |
Le siècle |
dayOfMonth |
Le jour du mois |
dayOfWeek |
Le jour de la semaine |
dayOfYear |
Le jour de l'année |
era |
L'ère comme défini par le système calendaire |
hourOfDay |
L'heure |
millisOfDay |
Le nombre de millisecondes du jour |
millisOfSecond |
Le nombre de millisecondes de l'heure |
minuteOfDay |
Le nombre de minutes du jour |
minuteOfHour |
Le nombre de minutes |
monthOfYear |
Le mois |
secondOfDay |
Le nombre de secondes du jour |
secondOfMinute |
Le nombre de secondes |
weekOfWeekyear |
La semaine de l'année |
weekYear |
|
Year |
L'année |
yearOfCentury |
L'année du siècle |
yearOfEra |
Exemple : |
DateTime dateTime = new DateTime();
System.out.println(dateTime.getYear());
System.out.println(dateTime.year().get());
System.out.println(dateTime.property(DateTimeFieldType.year()).get());
La classe DateTime.Property encapsule la valeur d'un champ qui est un élément d'une DateTime.La classe DateTime.Property propose quelques getters :
Méthode |
Rôle |
Chronology getChronology() |
Retourne le système calendaire du DateTime correspondant au champ |
DateTime getDateTime() |
Retourne l'instance de type DateTime correspondant au champ |
DateTimeField getField() |
Retourne le champ encapsulé |
long getMillis() |
Retourne le nombre de millisecondes du DateTime correspondant au champ |
La classe DateTime propose plusieurs méthodes qui permettent de modifier la valeur du champ et de retourner une nouvelle instance de type DateTime encapsulant le résultat de la mise à jour.
Méthode |
Rôle |
DateTime addToCopy(int value) |
Ajouter une valeur à la valeur de ce champ dans l'instance retournée |
DateTime addToCopy(long value) |
Ajouter une valeur à la valeur de ce champ dans l'instance retournée |
DateTime setCopy(int value) |
Modifier la valeur de ce champ dans l'instance retournée |
DateTime setCopy(String text) |
Modifier la valeur de ce champ dans l'instance retournée |
DateTime setCopy(String text, Locale locale) |
Modifier la valeur de ce champ dans l'instance retournée |
DateTime withMaximumValue() |
Forcer la valeur de ce champ à sa valeur maximale dans l'instance retournée |
DateTime withMinimumValue() |
Forcer la valeur de ce champ à sa valeur minimale dans l'instance retournée |
Exemple : |
DateTime dateTime = new DateTime(new Date());
System.out.println(dateTime);
System.out.println("dayOfMonth " + dateTime.dayOfMonth().get());
System.out.println("dayOfWeek " + dateTime.dayOfWeek().get());
System.out.println("dayOfYear " + dateTime.dayOfYear().get());
System.out.println("ear " + dateTime.era().get());
System.out.println("hourOfDay " + dateTime.hourOfDay().get());
System.out.println("millisOfDay " + dateTime.millisOfDay().get());
System.out.println("millisOfSecond " + dateTime.millisOfSecond().get());
System.out.println("minuteOfDay " + dateTime.minuteOfDay().get());
System.out.println("minuteOfHour " + dateTime.minuteOfHour().get());
System.out.println("monthOfYear " + dateTime.monthOfYear().get());
System.out.println("secondOfDay " + dateTime.secondOfDay().get());
System.out.println("secondOfMinute " + dateTime.secondOfMinute().get());
System.out.println("weekOfWeekyear " + dateTime.weekOfWeekyear().get());
System.out.println("weekyear " + dateTime.weekyear().get());
System.out.println("year " + dateTime.year().get());
System.out.println("yearOfCentury " + dateTime.yearOfCentury().get());
System.out.println("yearOfEra " + dateTime.yearOfEra().get());
Résultat : |
2012-11-08T06:56:46.781+01:00
dayOfMonth 8
dayOfWeek 4
dayOfYear 313
ear 1
hourOfDay 6
millisOfDay 25006781
millisOfSecond 781
minuteOfDay 416
minuteOfHour 56
monthOfYear 11
secondOfDay 25006
secondOfMinute 46
weekOfWeekyear 45
weekyear 2012
year 2012
yearOfCentury 12
yearOfEra 2012
Elle possède aussi de nombreuses méthodes héritées de la classe AbstractReadableInstantField.
Méthode |
Rôle |
int compareTo(ReadableInstant instant) |
Comparer ce champ au champ correspondant de l'instant |
int compareTo(ReadablePartial partial) |
Comparer ce champ au champ correspondant de l'instant partiel |
boolean equals(Object object) |
Comparer ce champ à un autre |
int get() |
Obtenir la valeur du champ |
String getAsShortText() |
Obtenir la valeur textuelle courte du champ dans la locale par défaut. |
String getAsShortText(Locale locale) |
Obtenir la valeur textuelle du champ dans la locale fournie. |
String getAsString() |
Obtenir la valeur du champ sous la forme d'une chaîne de caractères |
String getAsText() |
Obtenir la valeur textuelle du champ dans la locale par défaut. |
String getAsText(Locale locale) |
Obtenir la valeur textuelle du champ dans la locale fournie. |
int getDifference(ReadableInstant instant) |
Obtenir la différence entre la valeur de champ et celle correspondante dans l'instant passé en paramètre |
long getDifferenceAsLong(ReadableInstant instant) |
Obtenir la différence entre la valeur de champ et celle correspondante dans l'instant passé en paramètre |
DateTimeFieldType getFieldType() |
Obtenir le type du champ |
int getMaximumShortTextLength(Locale locale) |
Obtenir la taille maximale de valeur textuelle courte pour ce champ |
int getMaximumTextLength(Locale locale) |
Obtenir la taille maximale de valeur textuelle pour ce champ |
int getMaximumValue() |
Obtenir la valeur maximale pour ce champ |
int getMaximumValueOverall() |
Obtenir la valeur maximale pour ce champ sans tenir compte de l'instant |
protected abstract long getMillis() |
Obtenir le nombre de millisecondes du DateTime |
int getMinimumValue() |
Obtenir la valeur minimale pour ce champ |
int getMinimumValueOverall() |
Obtenir la valeur minimale pour ce champ sans tenir compte de l'instant |
String getName() |
Obtenir le nom du champ |
boolean isLeap() |
Retourner un booléen qui indique si la valeur du champ est bissextile |
String toString() |
Obtenir une représentation textuelle orientée debug du champ |
Exemple : |
DateTime dateTime = new DateTime(new Date());
System.out.println("date = "+dateTime);
System.out.println("nom du champ = "+dateTime.year().getName());
System.out.println("mois EN = "+dateTime.monthOfYear().getAsText(Locale.ENGLISH));
System.out.println("mois court = "+dateTime.monthOfYear().getAsShortText());
System.out.println("est bissextile = "+dateTime.year().isLeap());
System.out.println("jour rounded = "+dateTime.dayOfMonth().roundFloorCopy());
System.out.println("mois rounded = "+dateTime.monthOfYear().roundFloorCopy());
System.out.println("dayofWeek = "+dateTime.dayOfWeek().toString());
Résultat : |
date = 2012-11-08T07:04:03.265+01:00
nom du champ = year
mois EN = November
mois court = nov.
est bissextile = true
jour rounded = 2012-11-08T00:00:00.000+01:00
mois rounded = 2012-11-01T00:00:00.000+01:00
dayofWeek = Property[dayOfWeek]
Un instant partiel peut représenter plusieurs points dans le temps. Par exemple, le premier janvier existe chaque année dans le calendrier Grégorien. Un instant partiel est aussi pratique pour gérer des dates/heures locales (sans fuseau horaire) ou pour gérer uniquement des dates ou des heures.
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 est 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 :
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.
La classe LocalDate propose plusieurs constructeurs.
Exemple : |
LocalDate localDate = 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.
La classe LocalTime propose plusieurs constructeurs.
Exemple : |
LocalTime localTime = new LocalTime(17, 30, 45);
Joda Time propose un support pour la gestion d'intervalles qui correspondent à une plage entre deux dates et de périodes de temps qui sont une durée grâce à trois classes : Interval, Period et Duration.
Les classes Interval et MutableInterval implémentent l'interface ReadableInterval.
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.
La classe Interval propose plusieurs constructeurs.
Exemple : |
Interval interval = new Interval(
new DateTime("2012-12-10"),
new DateTime("2012-12-15"));
La méthode getStart() renvoie l'instant de début de l'intervalle. La méthode getEnd() renvoie l'instant de fin de l'intervalle.
La classe Interval propose plusieurs autres méthodes pour manipuler le contenu de l'intervalle.
Exemple : |
DateTime debut = new DateTime("2012-01-01");
DateTime fin = debut.plus(Months.months(1));
Interval interval = new Interval(debut, fin);
System.out.println("Interval = " + interval);
interval = interval.withEnd(interval.getEnd().plusMonths(1));
System.out.println("Interval = " + interval);
Résultat : |
Interval =
2012-01-01T00:00:00.000+01:00/2012-02-01T00:00:00.000+01:00
Interval = 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 : |
Interval interval = new Interval(
new DateTime("2012-12-10"),
new DateTime("2012-12-15"));
System.out.println(interval.contains(
new DateTime(2012, 12, 9, 23, 59, 59, 999)));
System.out.println(interval.contains(
new DateTime(2012, 12, 10, 0, 0, 0, 0)));
System.out.println(interval.contains(
new DateTime(2012, 12, 14, 23, 59, 59, 999)));
System.out.println(interval.contains(
new DateTime(2012, 12, 15, 0, 0, 0, 0)));
Résultat : |
false
true
true
false
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 forcément 60 minutes qui sont ajoutées 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 champs qui expriment ses 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 :
JodaTime propose plusieurs classes qui encapsulent une valeur pour un des champs de manière immuable : Years, Weeks, Months, Days, Hours, Minutes, Seconds.
Ces classes implémentent l'interface Comparable et proposent quelques méthodes permettant de réaliser des opérations mathématiques de base sur les valeurs qu'elles encapsulent (plus(), multipliedBy(), dividedBy(), negated(), ...) et des opérations de comparaison (isGreaterThan(), isLesserThan()).
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 retournant 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'heures 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 retournant 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 retournant 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 retournant 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 retournant 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.
La classe Period propose de nombreux constructeurs.
Une instance de type Period peut s'utiliser avec une instance de type Instant pour obtenir une nouvelle instance de type Instant.
Exemple : |
DateTime noel = new DateTime("2012-12-25");
DateTime nouvelAn = noel.plus(Period.days(7));
System.out.println(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 forcément 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ètres du constructeur de la classe Period.
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime noel13 = new DateTime("2013-12-25");
Period period = new Period(noel12, noel13);
System.out.println(period.getYears() + " an entre les deux dates");
Le même calcul peut se faire en utilisant la classe Years :
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime noel13 = new DateTime("2013-12-25");
Years year = Years.yearsBetween(noel12, noel13);
System.out.println(year.getYears() + " 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.
La classe Duration implémente l'interface ReadableDuration. L'interface ReadableDuration est un sous-ensemble des opérations du type Duration, il est donc généralement préférable de définir une variable de type Duration plutôt que du type ReadableDuration.
La classe Duration possède plusieurs constructeurs qui attendent en paramètres la durée ou deux instants qui seront utilisés pour déterminer la durée encapsulée.
Exemple : |
DateTime noel = new DateTime("2012-12-25");
DateTime nouvelAn = new DateTime("2013-01-01");
Duration 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 : |
DateTime noel = new DateTime("2012-12-25");
DateTime nouvelAn = noel.plus(new Duration(24L * 60L * 60L * 1000L * 7));
System.out.println(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.
Joda Time propose le support de plusieurs systèmes calendaires et la gestion des fuseaux horaires.
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.
Un système calendaire est une manière particulière de représenter le temps et de permettre de réaliser des calculs temporels. Joda Time propose en standard le support de plusieurs systèmes calendaires.
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 : |
Chronology calendrierCopte = CopticChronology.getInstance()
DateTime 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 : |
DateTimeZone zone = DateTimeZone.forID("Europe/Paris");
Chronology calendrierCopte = CopticChronology.getInstance(zone)
DateTime 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).
Un fuseau horaire correspond à un découpage géographique de la surface de la Terre relatif au méridien de Greenwich : le fuseau horaire de ce méridien est nommé GMT (Greenwich Mean Time). Le concept d'UTC (Universal Coordonated Time) est similaire mais pas tout à fait identique.
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.
La classe DateTimeZone encapsule un fuseau horaire de manière immuable.
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 : |
DateTimeZone zone = DateTimeZone.forID("Europe/Paris");
La classe DateTimeZone définit la constante UTC qui correspond à l'instance de DateTimeZone pour l'UTC.
La méthode getDefault() permet d'obtenir une instance de type DateTimeZone encapsulant le fuseau horaire par défaut qui correspond à celui du système hôte.
Exemple : |
DateTimeZone zone = DateTimeZone.getDefault();
System.out.println(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 utilisée 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.
La dernière version de la base peut être téléchargée à l'url : http://www.twinsun.com/tz/tz-link.php
Il faut télécharger les sources de l'API Joda Time à l'url http://sourceforge.net/projects/joda-time/files/joda-time/
Il faut décompresser les sources et remplacer les fichiers dans le sous-répertoire src/java/org/joda/time/tz/src par les fichiers téléchargés.
La recompilation du code source se fait en utilisant la commande ant jar dans le répertoire racine des sources. Il est recommandé dans ce cas de renommer le fichier jar généré pour indiquer que cette version à été modifiée par rapport à l'originale.
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 : |
Chronology chrono = ISOChronology.getInstance();
DateTime dt = new DateTime(2012, 12, 25, 0, 0, 0, 0, chrono);
Le calendrier Bouddhiste ne possède qu'une seule ère et ses années possèdent un décalage de 543 ans par rapport au calendrier Grégorien.
La classe BuddhistChronology est l'implémentation du calendrier Bouddhiste. Pour obtenir une instance, il faut invoquer la méthode getInstance() de la classe BuddhistChronology.
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime dt = noel12.withChronology(BuddhistChronology.getInstance());
System.out.println(dt);
Résultat : |
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 5 ou 6 jours complémentaires sont stockées dans un treizième mois.
Pour obtenir une instance, il faut invoquer sa méthode getInstance().
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime dt = noel12.withChronology(CopticChronology.getInstance());
System.out.println(dt);
Résultat : |
1729-04-16T00:00:00.000+01:00
Le calendrier Ethiopien est similaire au calendrier Copte.
La classe EthiopicChronology implémente le calendrier Ethiopien. Pour obtenir une instance, il faut invoquer sa méthode getInstance().
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime dt = noel12.withChronology(EthiopicChronology.getInstance());
System.out.println(dt);
Résultat : |
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.
La classe GregorianChronology implémente le calendrier Grégorien. Pour obtenir une instance, il faut invoquer sa méthode getInstance().
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime dt = noel12.withChronology(GregorianChronology.getInstance());
System.out.println(dt);
Résultat : |
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 calendriers est configurable : elle est par défaut au 15/10/1582 comme l'a défini le pape Grégoire XIII.
La classe GJChronology implémente le calendrier Grégorien/Julien. Cette classe est similaire à la classe java.util.GregorianCalendar du JDK.
Pour obtenir une instance, il faut invoquer sa méthode getInstance().
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime dt = noel12.withChronology(GJChronology.getInstance());
System.out.println(dt);
Résultat : |
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.
La classe IslamicChronology implémente le système calendaire Islamique. Pour obtenir une instance, il faut invoquer sa méthode getInstance().
Exemple : |
DateTime noel12 = new DateTime("2012-12-25");
DateTime dt = noel12.withChronology(IslamicChronology.getInstance());
System.out.println(dt);
Résultat : |
1434-02-11T00:00:00.000+01:00
La classe IslamicChronology.LeapYearPatternType permet de définir la façon dont les années bissextiles sont définies.
Le calendrier Julien est utilisé jusqu'au 15 octobre 1582 où il a été remplacé par le calendrier Grégorien.
La classe JulianChronology implémente le système calendaire Julien. Pour obtenir une instance, il faut invoquer sa méthode getInstance().
Exemple : |
DateTime noel12 = new DateTime("1012-12-25");
DateTime dt = noel12.withChronology(JulianChronology.getInstance());
System.out.println(dt);
Résultat : |
1012-12-19T00:00:00.000+00:09:21
Les fonctionnalités de manipulation de dates sont le point fort de l'API Joda Time de part leur richesse et leur facilité d'utilisation par rapport aux classes fournies par le JDK.
Exemple : |
package com.jmdoudoux.test.jodatime;
import org.joda.time.DateTime;
public class TestJodaTime {
public static void main(String[] args) {
DateTime dateTime = new DateTime(2012, 1, 1, 0, 0, 0, 0);
System.out.println(dateTime.plusDays(30)
.toString("dd/MM/yyyy HH:mm:ss.SSS"));
}
}
Le code équivalent en utilisant les classes du JDK est le suivant :
Exemple : |
package com.jmdoudoux.test.jodatime;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class TestJodaTime {
public static void main(String[]args) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(0);
calendar.set(2012, Calendar.JANUARY, 1, 0, 0, 0);
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS");
calendar.add(Calendar.DAY_OF_MONTH, 30);
System.out.println(sdf.format(calendar.getTime()));
}
}
Cette section propose plusieurs exemples pour illustrer certaines fonctionnalités de manipulation de dates proposées par Joda Time.
Exemple : obtenir la date/heure courante plus une heure et demi.
Exemple : |
DateTime now = new DateTime();
DateTime limite = now.plusHours(1).plusMinutes(30);
System.out.println(limite);
Exemple : obtenir le dernier jour du mois précédent.
Exemple : |
LocalDate dernierJourduMoisPrecedent = LocalDate.now() // aujourd'hui
.minusMonths(1) // on retire un mois
.dayOfMonth() // on récupère le jour du mois
.withMaximumValue(); // on lui affecte sa valeur maximale
System.out.println(dernierJourduMoisPrecedent);
Exemple : obtenir le lundi de la semaine de Noël.
Exemple : |
DateTime dateTime = new DateTime("2012-12-25");
DateTime result = dateTime.dayOfWeek().setCopy(DateTimeConstants.MONDAY);
System.out.println(result);
Résultat : |
2012-12-24T00:00:00.000+01:00
Exemple : Obtenir la date de paiement à 90 jours fin de mois.
Exemple : |
LocalDate datePaiement = LocalDate.now() // aujourd'hui
.plusDays(90) // on ajoute 90 jours
.dayOfMonth() // on récupère le jour du mois
.withMaximumValue(); // on lui affecte sa valeur maximale
System.out.println(datePaiement);
Pour calculer le nombre de jours entre deux dates, il est possible d'utiliser la méthode daysBetween() de la classe Days.
Exemple : |
DateTime dateTimeDeb = new DateTime("2012-12-25");
DateTime dateTimeFin = new DateTime("2012-12-31");
Days d = Days.daysBetween(dateTimeDeb, dateTimeFin);
int days = d.getDays();
System.out.println(days);
Résultat : |
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 : |
DateTime dateTimeDeb = new DateTime("2011-11-25");
DateTime dateTimeFin = new DateTime("2012-12-31");
Period p = new Period(dateTimeDeb, dateTimeFin, PeriodType.yearWeekDay());
System.out.println("annees " + p.getYears());
System.out.println("semaines " + p.getWeeks());
System.out.println("jours " + p.getDays());
Résultat : |
annees 1
semaines 5
jours 1
Exemple : Obtenir le nombre de jours avant la nouvelle année.
Exemple : |
LocalDate aujourdhui = LocalDate.now();
LocalDate nouvelAn = aujourdhui.plusYears(1).withDayOfYear(1);
Days nbJours = Days.daysBetween(aujourdhui, nouvelAn);
System.out.println(nbJours.getDays());
Joda Time offre une grande facilité pour l'interopérabilité avec les classes du JDK relatives aux traitements des données de type date/heure (notamment les classes Date et Calendar).
Il est possible de convertir des classes du JDK vers leur équivalent et vice versa tout en profitant de la facilité et de la richesse des fonctionnalités de manipulation de dates/heures offertes par Joda Time.
Les classes de Joda Time qui encapsulent une date/heure acceptent comme paramètre dans une surcharge de leur constructeur un objet de type java.util.Date ou java.util.Calendar.
La méthode toCalendar() de la classe AbstractDateTime permet de convertir l'objet Joda Time en une instance de type java.util.Calendar.
Exemple : |
DateTime dateTime = new DateTime("2012-12-25");
Calendar calendar = dateTime.toCalendar(Locale.getDefault());
System.out.println(calendar);
La méthode toGregorianCalendar() de la classe AbstractDateTime permet de convertir l'objet Joda Time en une instance de type java.util.GregorianCalendar.
Exemple : |
DateTime dateTime = new DateTime("2012-12-25");
GregorianCalendar calendar = dateTime.toGregorianCalendar();
System.out.println(calendar);
La méthode toDate() de la classe AbstractInstant permet de convertir l'objet Joda Time en une instance de type java.util.Date.
Exemple : |
DateTime dateTime = new DateTime("2012-12-25");
Date date = dateTime.toDate();
System.out.println(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 : |
LocalDate localDate = new LocalDate("2012-12-25");
Date date = localDate.toDateMidnight().toDate();
System.out.println(date);
L'obtention d'une date à partir d'une ressource externe (fichiers, services web, ...) ou d'une zone de saisie de l'utilisateur, le formattage d'une date sont fréquents dans une application. Le format de ces dates n'est pas toujours le même : Joda Time propose plusieurs solutions pour définir ce format de manière simple ou personnalisée.
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 : |
DateTime dateTime = new DateTime("2012-12-25");
System.out.println(dateTime.toString("dd-MM-yyyy HH:mm:ss"));
System.out.println(dateTime.toString("EEEE dd MMMM yyyy HH:mm:ss"));
System.out.println(dateTime.toString("MM/dd/yyyy HH:mm ZZZZ"));
System.out.println(dateTime.toString("MM/dd/yyyy HH:mm Z"));
Résultat : |
25-12-2012 00:00:00
mardi 25 décembre 2012 00:00:00
12/25/2012 00:00 Europe/Paris
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 : |
DateTime dateTime = new DateTime("2012-12-25");
System.out.println(dateTime.toString(ISODateTimeFormat.basicDateTime()));
System.out.println(dateTime.toString(ISODateTimeFormat
.basicDateTimeNoMillis()));
System.out.println(dateTime.toString(ISODateTimeFormat
.basicOrdinalDateTime()));
System.out
.println(dateTime.toString(ISODateTimeFormat.basicWeekDateTime()));
Résultat : |
20121225T000000.000+0100
20121225T000000+0100
2012360T000000.000+0100
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 standard. La méthode forPattern() permet de préciser explicitement le format de la date/heure à utiliser.
Exemple : |
DateTimeFormatter formatter = DateTimeFormat
.forPattern("dd-MM-yyyy HH:mm:ss");
DateTime dateTime = formatter.parseDateTime("25-12-2012 00:00:00");
System.out.println(dateTime);
Résultat : |
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 : |
DateTime dateTime = new DateTime("2012-12-25");
DateTimeFormatter formatter = DateTimeFormat
.forPattern("EEEE dd MMMM yyyy HH:mm:ss");
DateTimeFormatter frenchFmt = formatter.withLocale(Locale.FRENCH);
System.out.println(frenchFmt.print(dateTime));
DateTimeFormatter englishFmt = formatter.withLocale(Locale.ENGLISH);
System.out.println(englishFmt.print(dateTime));
Résultat : |
mardi 25 décembre 2012 00:00:00
Tuesday 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.
La classe DateTimeFormatterBuilder propose de nombreuses méthodes appendXXX() qui permet de préciser chaque élément qui devra être ajouté pour définir le format de la date.
La méthode toFormatter() permet de demander l'instance de type DateTimeFormatter selon la configuration définie.
L'exemple ci-dessous va demander le formatage de l'année sur deux caractères.
Exemple : |
DateTime dateTime = new DateTime("2012-12-25");
DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendDayOfMonth(2)
.appendLiteral(' ').appendMonthOfYearShortText().appendLiteral(' ')
.appendTwoDigitYear(1949).toFormatter();
System.out.println(fmt.print(dateTime));
Résultat : |
25 déc. 12
La méthode clear() permet de réinitialiser la configuration.
Joda Time propose aussi des fonctionnalités pour modifier la date/heure par défaut ou utiliser des objets mutables.
La classe DateTimeUtils propose plusieurs méthodes qui permettent de modifier la date/heure obtenue par l'API Joda Time.
La méthode setCurrentMillisFixed() permet de modifier la date/heure de Joda Time avec celle correspondant au nombre de millisecondes fourni en paramètre.
La méthode setCurrentMillisOffset() permet de modifier la date/heure de Joda Time en effectuant un décalage avec le nombre de millisecondes fourni en paramètre.
La méthode setCurrentMillisSystem() permet d'accorder la date/heure de Joda Time avec celle du système.
Exemple : |
DateTime noel13 = new DateTime("2013-12-25");
DateTimeUtils.setCurrentMillisFixed(noel13.getMillis());
System.out.println(new Date());
System.out.println(LocalDateTime.now());
// remettre la date de Joda Time à la date systeme
DateTimeUtils.setCurrentMillisSystem();
System.out.println(LocalDateTime.now());
// modifier la date de Joda Time à la veille
DateTimeUtils.setCurrentMillisOffset(1000 * 60 * 60 * 24);
System.out.println(LocalDateTime.now());
L'utilisation de ces méthodes peut être pratique pour des tests.
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 intéressant d'utiliser une version mutable afin de limiter le nombre d'instances créées.
Joda Time propose les classes MutableDateTime, MutableInterval et MutablePeriod.
Exemple : |
DateTime noel13 = new DateTime("2013-12-25");
MutableDateTime mdt = noel13.toMutableDateTime();
mdt.setDayOfMonth(1);
mdt.setYear(2012);
mdt.setMonthOfYear(1);
DateTime result = mdt.toDateTime();
System.out.println(result);
La classe MutableDateTime possède de nombreux constructeurs pour indiquer la date/heure encapsulée.
La classe DateTime propose aussi la méthode toMutableDateTime() qui renvoie une instance de type MutableDateTime encapsulant 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 org.apache.commons.lang.time.FastDateFormat permet le formatage d'une date comme SimpleDateFormat mais elle est plus performante et surtout thread-safe.
La classe FastDateFormat offre des fonctionnalités de formatage d'une date similaires à celles de la classe SimpleDateFormat : elle propose cependant un support du timezone différent dans le formatage.
FastDateFormat ne permet que le formatage d'une date. Contrairement à la classe SimpleDateFormat, elle ne permet pas d'extraire une date d'une chaine de caractères.
Exemple : |
import java.text.SimpleDateFormat; import java.util.Date;
import org.apache.commons.lang.time.FastDateFormat;
public class TestSdf {
public static void main(final String[] args) {
final String format = "dd-MM-yyyy HH:mm:ss.SSS";
SimpleDateFormat sdf = new SimpleDateFormat(format);
FastDateFormat fdf = FastDateFormat.getlnstance(format);
final Date d = new Date();
final int nblteration = 1000000;
long start = 0;
long tempsTrt = 0;
start = System.currentTimeMillis();
for (int i = 0; i < nblteration; i++) {
fdf = FastDateFormat.getlnstance(format);
d.setTime(System.currentTimeMillis());
fdf.format(d);
}
tempsTrt = System.currentTimeMillis() - start;
System.out.println("FastDateFormat : " + tempsTrt + " ms");
start = System.currentTimeMillis();
for (int i = 0; i < nblteration; i++) {
sdf = new SimpleDateFormat(format);
d.setTime(System.currentTimeMillis());
sdf.format(d);
}
tempsTrt = System.currentTimeMillis() - start;
System.out.println("SimpleDateFormat : " + tempsTrt + " ms");
}
}
Résultat : |
FastDateFormat : 2041 ms
SimpleDateFormat : 5360 ms
La classe org.apache.commons.lang.time.DateFormatUtils est une classe utilitaire pour le formatage de dates et d'heures en utilisant la classe FastDateFormat.
Elle propose des constantes pour des instances de FastDateFormat avec des motifs de formatage courants en ISO08601 et SMTP :
ISO_DATETIME_FORMAT |
formatage d'une date/heure en ISO860 sans timezone : yyyy-MM-dd'T'HH:mm:ss |
ISO_DATETIME_TIMEZONE_FORMAT |
formatage d'une date/heure en ISO8601 avec timezone : yyyy-MM-dd'T'HH:mm:ssZZ |
ISO_DATE_FORMAT |
formatage d'une date enISO8601 sans timezone : yyyy-MM-dd |
ISO_TIME_FORMAT |
formatage d'une heure en pseudo ISO8601 sans time zone (la spécification ne permet pas d'avoir une heure sans timezone) : 'T'HH:mm:ss |
ISO_DATE_TIME_ZONE_FORMAT |
formatage d'une date enISO8601 sans timezone (la spécification ne permet pas d'avoir une heure sans timezone) : yyyy-MM-ddZZ |
ISO_TIME_TIMEZONE_FORMAT |
formatage d'une heure en ISO8601 avec time zone : 'T'HH:mm:ssZZ. |
ISO_TIME_NOT_FORMAT |
formatage d'une heure en pseudo ISO8601 sans time zone (la spécification requiert que l'heure soit préfixée par le caractère T) : HH:mm:ss. |
ISO_TIME_NOTTIMEZONE_FORMAT |
formatage d'une heure en pseudo ISO8601 avec time zone (la spécification requiert que l'heure soit préfixée par le caractère T) : HH:mm:ssZZ. |
SMTP_DATETIME_FORMAT |
formatage d'une date heure au format requis par SMTP : EEE, dd MMM yyyy EH:mm:ss Z in US locale. |
Il n'est pas recommandé d'utiliser son constructeur par défaut.
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 : |
public static void main(final String[] args) {
final Date today = new Date();
/*
* formatage en ISO8601 sans timezone : yyyy-MM-dd'T'HH:mm:ss.
*/
String timestamp = DateFormatUtils.ISO_DATETIME_FORMAT.format(today);
System.out.println("timestamp = " + timestamp);
/*
* formatage en ISO8601 avec timezone : yyyy-MM-dd' T'HH:mm:ssZZ.
*/
timestamp = DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(today)
System.out.println("timestamp = " + timestamp);
/*
* formatage au format SMTP : EEE, dd MMM yyyy HH:mm:ss Z (Locale US).
*/
timestamp = DateFormatUtils.SMTP_DATETIME_FORMAT.format(today);
System.out.println("timestamp = " + timestamp);
}
Malgré les nombreux intérêts de la plate-forme Java, celle-ci a toujours manqué d'un support correct pour la gestion des données temporelles jusqu'à Java 8. Pourtant un bon support de ce type de fonctionnalités est important car l'utilisation de données temporelles est omniprésente notamment dans les applications de gestion.
Il y a 3 API différentes pour manipuler le temps dans le JDK8 :
Java 1.0 ne propose que la classe java.util.Date concernant le support de données temporelles. Elle encapsule un point dans le temps avec une précision à la milliseconde.
Elle présente de nombreuses lacunes dont les principales sont :
Exemple : |
package com.jmdoudoux.test.java8.datetime;
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
Date date = new Date(114, 11, 25);
System.out.println(date);
}
}
Résultat : |
Thu Dec 25 00:00:00 CET 2014
Pour combler une partie de ces lacunes et conserver la compatibilité, Java 1.1 a rendu deprecated une grande partie des méthodes et constructeurs de la classe Date et a introduit la classe java.util.Calendar. Celle-ci n'a pas résolu tous les problèmes car elle a aussi certains inconvénients de la classe Date mais aussi des inconvénients qui lui sont propres :
De plus, certaines fonctionnalités requièrent uniquement un objet de type Date ce qui oblige à faire des conversions.
Pour analyser et formater une date, la classe abstraite DateFormat et la classe SimpleDateFormat ont été ajoutées. Elles présentent aussi des inconvénients :
La classe java.util.TimeZone encapsule un fuseau horaire.
Pour pallier ces inconvénients, plusieurs API open source ont été développées : la plus populaire est Joda-Time. Cependant Joda-Time n'est pas exempt de défauts :
Ces points expliquent en grande partie pourquoi Joda-Time n'est pas et ne peut pas être l'implémentation de référence de la JSR 310.
L'API Date-Time offre un support pour utiliser et manipuler des données temporelles de manière la plus complète possible : date, heure, intervalle de temps, calendrier, fuseau et décalage horaire, ...
Les spécifications de l'API Date-Time sont définies dans la JSR 310 dont l'implémentation de référence est le projet ThreeTen. Celle-ci s'est largement inspirée de l'API Joda-Time pour proposer une API riche permettant le support de données temporelles dans l'API du JDK. Elle est intégrée à Java SE version 8.
L'API propose une meilleure modélisation des classes et interfaces permettant la gestion de dates/heures qui est riche, puissante et extensible.
L'API possède plusieurs avantages :
L'API Date-Time utilise :
La représentation du temps peut se faire selon deux grands modèles :
L'API propose plusieurs classes qui encapsulent différentes données temporelles sous ces deux formes :
Classe |
Rôle |
LocalDate |
Encapsule une date sans heure ni fuseau horaire ni offset. Cette classe peut par exemple être utile pour stocker une date d'anniversaire |
LocalDateTime |
Encapsule une date et une heure sans fuseau horaire |
Localtime |
Encapsule une heure sans date, ni fuseau horaire ni offset |
Period |
Encapsule une période de temps exprimée dans des unités compréhensibles par un humain, par exemple 1 mois et 12 jours. Les valeurs exprimées dans une unité peuvent être négatives |
Duration |
Encapsule une durée entre deux instants stockée avec une précision de la nanoseconde. La durée peut être négative |
Instant |
Encapsule un point dans le temps avec une précision de la nanoseconde. Elle permet de représenter un point dans la ligne du temps. La classe Instant est la classe la plus proche de la classe java.util.Date |
ZonedDateTime |
Encapsule une date et une heure avec un fuseau horaire et son offset correspondant. La classe ZonedDateTime est la classe la plus proche de la classe GregorianCalendar |
ZoneId |
Encapsule un fuseau horaire précisé par son identifiant, par exemple «Europe/Paris» |
ZoneOffset |
Encapsule un fuseau horaire précisé par un décalage à partir du méridien de Greenwich/UTC, par exemple +01:00 |
Il y a plusieurs classes qui permettent de gérer une date mais seulement quelques-unes pour gérer l'heure. Par exemple, aucune classe ne propose d'encapsuler une combinaison heures-minutes ou minutes-secondes. Les heures sont toujours gérées sous la forme des quatre champs (heures, minutes, secondes, nanosecondes) pour simplifier l'API. Si un de ces champs n'est pas utile, il suffit de mettre sa valeur à zéro.
De la même façon pour rester pragmatique, le nombre de classes qui encapsulent une combinaison de champs d'une date est limité : YearMonth et MonthDay.
Les classes de l'API qui permettent d'obtenir ou de manipuler une donnée temporelle telles que Instant, LocalDateTime, LocalDate, LocalTime, ZonedDateTime, ... sont désignées par objets temporels dans les sections suivantes.
Elle propose aussi des classes utilitaires pour réaliser des opérations sur des données temporelles :
Classe |
Rôle |
ChronoUnit |
Une énumération des différentes unités temporelles. Elle permet aussi de réaliser des opérations de calcul en utilisant ces unités |
DateTimeFormatter |
Analyser et formater une date/heure sous une forme textuelle |
Clock |
Obtenir la date/heure courante dans le fuseau horaire par défaut. |
Les interfaces et les classes de l'API Date-Time sont contenues dans le package java.time et ses quatre sous-packages :
La conception de cette API repose sur plusieurs principes :
L'API Date-Time est composée de nombreuses classes et méthodes. Elle utilise de préférence une convention de nommage pour certaines méthodes ayant le même rôle dans différentes classes afin de renforcer la cohérence et faciliter son utilisation.
Nom ou préfixe |
Rôle |
format |
Formater l'objet pour obtenir une chaîne de caractères |
get |
Obtenir la valeur d'un élément |
is |
Obtenir la valeur booléenne d'un élément |
with |
Renvoyer une copie de l'objet avec un élément modifié |
plus |
Renvoyer une copie de l'objet avec une portion de temps ajoutée |
minus |
Renvoyer une copie de l'objet avec une portion de temps soustraite |
to |
Convertir l'objet dans un autre type |
at |
Combiner l'objet avec une autre instance |
De nombreuses classes de base sont immuables : elles ne proposent donc pas de setter mais des fabriques pour faciliter la création de nouvelles instances. Le nom des fabriques utilise au mieux quelques conventions :
Nom ou préfix |
Rôle |
of |
Créer une nouvelle instance à partir des données passées en paramètres : les valeurs sont validées mais ne sont pas converties |
from |
Créer une nouvelle instance en opérant une conversion des données fournies en paramètres |
now |
Créer une nouvelle instance en utilisant les données utiles de la date-heure courante |
parse |
Analyser la chaîne de caractères fournie en paramètre pour créer une nouvelle instance |
Le package java.time.temporal contient des interfaces, des classes et des énumérations permettant d'obtenir des informations à partir de données temporelles et de réaliser des opérations basiques sur celles-ci.
Ces interfaces sont de bas niveaux : elles ne devraient pas être utilisées pour déclarer des objets dans le code d'une application. Il est préférable d'utiliser le type d'une classe concrète pour déclarer un objet temporel essentiellement car les interfaces sont indépendantes de tout calendrier.
Interface |
Rôle |
TemporalAccessor |
Accès en lecture seule aux informations d'une donnée temporelle |
Temporal |
Opérations de base de manipulation de données temporelles |
TemporalAmount |
Une quantité temporelle |
TemporalField |
Un champ d'une donnée temporelle |
TemporalUnit |
Une unité temporelle |
Les dates et les heures sont exprimées sous la forme de champs. Ces champs possèdent :
Un objet temporel qui implémente l'interface TemporalAccessor encapsule ses données sous la forme de champs de type TemporalField.
L'énumération ChronoField qui implémente l'interface TemporalField définit ces différents champs.
La valeur de ces champs est exprimée dans une unité définie par l'interface TemporalUnit.
L'énumération ChronoUnit qui implémente l'interface TemporalUnit définit différentes unités couramment utilisées. Certains calendriers peuvent requérir des unités qui leur sont particulières.
Les quantités passées en paramètre des opérations arithmétiques de l'interface Temporal sont définies par l'interface TemporalAmount. Les classes Period et Duration implémentent cette interface.
Il existe différentes définitions de la notion de semaines en fonction de la Locale : par exemple en Europe elle commence le lundi, aux Etats Unis elle commence le dimanche. La classe WeekFields encapsule ces définitions en proposant des constantes pour les plus courantes d'entre-elles.
L'interface Temporal propose des opérations de base de manipulation de données temporelles telles que l'ajout ou le retrait d'une quantité temporelle.
Il est fréquent d'avoir à ajuster une date pour en obtenir une nouvelle : par exemple, le prochain lundi, le dernier jour du mois, ... Généralement, ces opérations sont plus ou moins complexes et sont définies par l'interface TemporalAdjuster.
L'interface TemporalQuery définit les fonctionnalités qui permettent d'obtenir des informations d'un objet temporel sous la forme d'une requête.
L'interface TemporalAccessor définit des fonctionnalités permettant un accès en lecture seule à certains éléments d'un objet temporel en accédant directement à un champ ou en exécutant une requête.
C'est l'interface de base pour les objets qui encapsulent des dates, des heures et des offsets.
La plupart des champs qui composent un objet temporel peuvent être représentés sous la forme d'un entier. Ces champs implémentent l'interface TemporalField. Deux informations d'un objet temporel ne peuvent pas être représentées sous la forme d'un entier : le calendrier et le fuseau horaire. Ces informations peuvent être accédées en utilisant une requête de type TemporalQuery.
Elle définit plusieurs méthodes :
Méthode |
Rôle |
default int get(TemporalField field) |
Obtenir la valeur du champ précisé en paramètre sous la forme d'un entier |
long getLong(TemporalField field) |
Obtenir la valeur du champ précisé en paramètre sous la forme d'un entier long |
boolean isSupported(TemporalField field) |
Vérifier si le champ fourni en paramètre est supporté |
default <R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
default ValueRange range(TemporalField field) |
Obtenir la plage des valeurs possibles pour le champ fourni en paramètre |
L'interface Temporal hérite de l'interface TemporalAccessor pour définir des opérations de base de manipulations sur des objets temporels.
Elle définit plusieurs méthodes :
Méthode |
Rôle |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité passée en paramètre est supportée |
default Temporal minus(long amountToSubtract, TemporalUnit unit) default Temporal minus(TemporalAmount amount) |
Renvoyer une instance du même type minorée de la quantité temporelle fournie en paramètre |
Temporal plus(long amountToAdd, TemporalUnit unit) default Temporal plus(TemporalAmount amount) |
Renvoyer une instance du même type majorée de la quantité temporelle fournie en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps jusqu'à l'objet temporel fourni en paramètre. La valeur retournée est exprimée dans l'unité temporelle précisée en paramètre |
default Temporal with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet qui encapsule les traitements fourni en paramètre |
Temporal with(TemporalField field, long newValue) |
Renvoyer un objet du même type dont la valeur du champ fourni en paramètre est modifiée |
Plusieurs classes de l'API implémentent l'interface Temporal : HijrahDate, Instant, JapaneseDate, LocalDate, LocalDateTime, LocalTime, MinguoDate, OffsetDateTime, OffsetTime, ThaiBuddhistDate, Year, YearMonth et ZonedDateTime.
L'interface TemporalAmount définit les fonctionnalités d'un objet qui encapsule une quantité temporelle telle qu'une heure et 30 minutes, trois jours, un an et un jour, ...
Une quantité temporelle n'est pas liée à un point dans le temps. Cette quantité est exprimée au moyen d'une ou plusieurs paires de valeur-unité.
Elle définit plusieurs méthodes :
Méthode |
Rôle |
Temporal addTo(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre auquel est ajoutée la quantité encapsulée |
long get(TemporalUnit unit) |
Obtenir la valeur dans l'unité précisée |
List<TemporalUnit> getUnits() |
Retourner une liste des unités temporelles utilisables pour exprimer la quantité. La collection est triée dans l'ordre décroissant de durée de l'unité (de la plus longue à la plus courte) |
Temporal subtractFrom(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre duquel est soustrait la quantité encapsulée |
La quantité est le cumul des différentes valeurs de chacune des unités : pour chaque unité obtenue en invoquant la méthode getUnits(), cela revient à invoquer la méthode get() en lui passant l'unité en paramètre.
L'API fournit deux classes qui implémentent cette interface :
L'interface TemporalField définit les fonctionnalités d'un champ d'un objet temporel dont il encapsule une représentation humaine.
Elle définit plusieurs méthodes, notamment :
Méthode |
Rôle |
<R extends Temporal> R adjustInto(R temporal, long newValue) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans lequel la valeur du champ encapsulé est remplacée par celle passée en paramètre |
TemporalUnit getBaseUnit() |
Renvoyer l'unité temporelle dans laquelle la valeur du champ est exprimée |
default String getDisplayName(Locale locale) |
Renvoyer le nom du champ dans la Locale fournie en paramètre |
long getFrom(TemporalAccessor temporal) |
Obtenir la valeur du champ encapsulée dans l'objet temporel fourni en paramètre |
boolean isDateBased() |
Renvoyer un booléen qui précise si le champ entre dans la composition d'une date |
boolean isSupportedBy(TemporalAccessor temporal) |
Renvoyer un booléen qui précise si le champ est supporté par l'objet temporel fourni en paramètre |
boolean isTimeBased() |
Renvoyer un booléen qui précise si le champ entre dans la composition d'une heure |
ValueRange range() |
Obtenir la plage des valeurs valides pour le champ |
ValueRange rangeRefinedBy(TemporalAccessor temporal) |
Obtenir la plage des valeurs valides pour le champ dans le contexte de l'objet temporel fourni en paramètre. |
Il est possible pour certains champs que les méthodes isDateBased() et isTimeBased() renvoient toutes les deux false.
L'énumération ChronoField implémente l'interface TemporalField. D'autres classes contiennent des objets de type TemporalField : IsoFields, WeekFields et JulienFields.
L'énumération ChronoField propose des constantes de type TemporalField pour les champs standard utilisés dans la composition de dates et/ou d'heures. Ces champs peuvent être utilisés dans différents calendriers.
Valeur |
Description |
ALIGNED_DAY_OF_WEEK_IN_MONTH |
|
ALIGNED_DAY_OF_WEEK_IN_YEAR |
|
ALIGNED_WEEK_OF_MONTH |
La semaine du mois |
ALIGNED_WEEK_OF_YEAR |
La semaine de l'année |
AMPM_OF_DAY |
La demi-journée (0 AM, 1 PM) |
CLOCK_HOUR_OF_AMPM |
L'heure de la demi-journée (1-12) |
CLOCK_HOUR_OF_DAY |
L'heure de la journée (0-24) |
DAY_OF_MONTH |
Le jour du mois (1-31) |
DAY_OF_WEEK |
Le jour de la semaine (1-7) |
DAY_OF_YEAR |
Le jour de l'année (1-366) |
EPOCH_DAY |
Le Neme jour depuis l'EPOCH (01/01/1970 dans le calendrier ISO) |
ERA |
L'ère : dans le calendrier ISO (0 BCE, 1 CE) |
HOUR_OF_AMPM |
L'heure de la demi-journée (0-11) |
HOUR_OF_DAY |
L'heure de la journée (0-23) |
INSTANT_SECONDS |
Représente le nombre de secondes comptées à partir du 1er janvier 1970 à 0000z |
MICRO_OF_DAY |
La microseconde de la journée |
MICRO_OF_SECOND |
La microseconde de la seconde |
MILLI_OF_DAY |
La milliseconde de la journée |
MILLI_OF_SECOND |
La milliseconde de la seconde |
MINUTE_OF_DAY |
La minute de la journée (0-1440) |
MINUTE_OF_HOUR |
La minute de l'heure (0-59) |
MONTH_OF_YEAR |
Le mois de l'année |
NANO_OF_DAY |
La nanoseconde de la journée |
NANO_OF_SECOND |
La nanoseconde de la seconde |
OFFSET_SECONDS |
Le décalage depuis le méridien de Greenwich |
PROLEPTIC_MONTH |
Le mois depuis l'année 0 |
SECOND_OF_DAY |
La seconde de la journée (0-86400) |
SECOND_OF_MINUTE |
La seconde de la minute (0-59) |
YEAR |
L'année |
YEAR_OF_ERA |
L'année de l'ère |
La méthode isSupported() de la classe TemporalAccessor qui attend en paramètre un objet de type TemporalField permet de déterminer si l'objet temporel supporte le champ passé en paramètre.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.temporal.ChronoField;
import java.time.temporal.IsoFields;
public class TestTemporalField {
public static void main(String[] args) {
LocalDate date = LocalDate .now();
int mois = date.get(ChronoField.MONTH_OF_YEAR);
System.out.println("Mois="+mois);
boolean supporte = date.isSupported(ChronoField.HOUR_OF_DAY);
System.out.println("Supporte="+supporte);
int trimestre = date.get(IsoFields.QUARTER_OF_YEAR);
System.out.println("Trimestre="+trimestre);
}
}
Résultat : |
Mois=2
Supporte=false
Trimestre=1
L'interface TemporalUnit définit les fonctionnalités d'une unité temporelle. Une unité temporelle permet de mesurer le temps en exprimant une quantité temporelle telle qu'une heure, un jour, une année, ...
Elle définit plusieurs méthodes :
Méthode |
Rôle |
<R extends Temporal> R addTo(R temporal, long amount) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans lequel la quantité précisée est ajoutée. La quantité est exprimée dans l'unité encapsulée |
long between(Temporal temporal1Inclusive, Temporal temporal2Exclusive) |
Calculer la quantité de temps entre les deux objets temporels fournis en paramètres exprimée dans l'unité précisée |
Duration getDuration() |
Renvoyer une durée de l'unité qui peut être estimée |
boolean isDateBased() |
Indiquer si l'unité peut être utilisée sur un champ composant une date |
boolean isDurationEstimated() |
Indiquer si la durée de l'unité est estimée. Par exemple, à cause de la gestion de l'heure d'été/hiver la durée d'une journée est estimée |
default boolean isSupportedBy(Temporal temporal) |
Vérifier si l'unité est supportée par l'objet temporel fourni en paramètre |
boolean isTimeBased() |
Indiquer si l'unité peut être utilisée sur un champ composant une heure |
L'énumération ChronoUnit implémente cette interface. Quelques unités sont définies dans la classe IsoFields.
L'énumération ChronoUnit propose des constantes de type TemporalUnit pour les unités temporelles standard utilisées dans la composition de dates et/ou d'heures. Ces unités pour mesurer le temps peuvent être utilisées dans différents calendriers.
Valeur |
Rôle |
CENTURIES |
Un siècle |
DAYS |
Un jour |
DECADES |
Une décade |
ERAS |
Une ère |
FOREVER |
Le concept de pour toujours (sans fin) |
HALF_DAYS |
Une demi-journée |
HOURS |
Une heure |
MICROS |
Une microseconde |
MILLENNIA |
Un millénaire |
MILLIS |
Une milliseconde |
MINUTES |
Une minute |
MONTH |
Un mois |
NANOS |
Une nanoseconde |
SECONDS |
Une seconde |
WEEKS |
Une semaine |
YEARS |
Une année |
La méthode isSupported() de la classe TemporalAccessor qui attend en paramètre un objet de type TemporalUnit permet de vérifier si l'unité passée en paramètre est supportée par l'objet Temporal.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
public class TestTemporalUnit {
public static void main(String[] args) {
LocalDate dateDebut = LocalDate.of(2014, 12, 25);
LocalDate dateFin = LocalDate.of(2015, 12, 25);
System.out.println("Duree="+ChronoUnit.DAYS.between(dateDebut, dateFin));
LocalTime heure = LocalTime.now();
boolean supporte = heure.isSupported(ChronoUnit.YEARS);
System.out.println("Supporte="+supporte);
System.out.println("ToString="+ChronoUnit.DAYS.toString());
}
}
Résultat : |
Duree=365
Supporte=false
ToString=Days
L'API Date-Time propose des énumérations spécifiques :
L'API Date-Time propose quatre classes qui encapsulent tout ou partie d'une date sans tenir compte de l'heure et du fuseau horaire :
L'énumération java.time.DayOfWeek définit des constantes pour les 7 jours de la semaine tels que définit dans le calendrier ISO-8601 : FRIDAY, MONDAY, SATURDAY, SUNDAY, THURSDAY, TUESDAY et WEDNESDAY.
Chaque élément de l'énumération est associé à une valeur numérique tel que définie dans le calendrier ISO-8601 : 1 pour MONDAY à 7 pour SUNDAY.
Il est recommandé d'utiliser les éléments de l'énumération plutôt que leur valeur numérique.
Elle propose aussi plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans laquelle le jour est remplacé par celui encapsulé |
static DayOfWeek from(TemporalAccessor temporal) |
Obtenir une instance à partir des données encapsulées dans l'objet fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier |
String getDisplayName(TextStyle style, Locale locale) |
Renvoyer une représentation textuelle selon le format et la Locale fournis en paramètres |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier long |
int getValue() |
Renvoyer la valeur numérique associée au jour de la semaine |
boolean isSupported(TemporalField field) |
Vérifier si le champ fourni en paramètre est supporté |
DayOfWeek minus(long days) |
Renvoyer une copie de l'instance dont le nombre de jours est minoré de la valeur fournie en paramètre |
static DayOfWeek of(int dayOfWeek) |
Renvoyer le jour de la semaine associé à la valeur fournie en paramètre |
DayOfWeek plus(long days) |
Renvoyer une copie de l'instance dont le nombre de jours fourni en paramètre est ajouté |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage des valeurs valides pour le champ fourni en paramètre |
static DayOfWeek valueOf(String name) |
Renvoyer l'élément de l'énumération dont le nom est fourni paramètre |
static DayOfWeek[] values() |
Renvoyer un tableau des éléments de l'énumération dans leur ordre de déclaration |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.DayOfWeek;
public class TestDayOfWeek {
public static void main(String[] args) {
DayOfWeek dow = DayOfWeek.MONDAY;
System.out.println(dow.getValue());
System.out.println(dow.plus(4));
}
}
Résultat : |
1
FRIDAY
La méthode getDisplayName() permet d'obtenir le nom du jour dans le format et la Locale précisés.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.DayOfWeek;
import java.time.format.TextStyle;
import java.util.Locale;
public class TestDayOfWeek {
public static void main(String[] args) {
DayOfWeek dow = DayOfWeek.MONDAY;
Locale locale = Locale.getDefault();
System.out.println(dow.getDisplayName(TextStyle.FULL, locale));
System.out.println(dow.getDisplayName(TextStyle.SHORT, locale));
System.out.println(dow.getDisplayName(TextStyle.NARROW, locale));
}
}
Résultat : |
lundi
lun.
L
L'énumération java.time.Month définit des constantes pour les 12 mois de l'année telles que définies dans le calendrier ISO-8601 : JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER et DECEMBER.
Chaque élément de l'énumération est associé à une valeur numérique. Il est recommandé d'utiliser les éléments de l'énumération plutôt que leur valeur numérique.
Elle propose aussi plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dont le mois est ajusté avec celui encapsulé |
int firstDayOfYear(boolean leapYear) |
Renvoyer le jour de l'année correspondant au premier jour du mois. Le paramètre booléen permet de préciser si cela concerne une année bissextile |
Month firstMonthOfQuarter() |
Obtenir le premier mois du trimestre auquel appartient le mois encapsulé |
static Month from(TemporalAccessor temporal) |
Renvoyer une instance de type Month qui correspond au mois encapsulé dans l'objet fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier |
String getDisplayName(TextStyle style, Locale locale) |
Obtenir une représentation textuelle selon le style et la Locale fournis en paramètres |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier long |
int getValue() |
Renvoyer la valeur numérique associée au mois |
boolean isSupported(TemporalField field) |
Vérifier si le champ fourni en paramètre est supporté |
int length(boolean leapYear) |
Renvoyer le nombre de jours du mois. Le paramètre booléen permet de préciser si cette valeur est celle d'une année bissextile |
int maxLength() |
Renvoyer le nombre maximum de jours du mois |
int minLength() |
Renvoyer le nombre minimum de jours du mois |
Month minus(long months) |
Renvoyer une copie de l'instance minorée du nombre de mois fourni en paramètre |
static Month of(int month) |
Renvoyer l'instance de type Month associée à la valeur numérique fournie en paramètre |
Month plus(long months) |
Renvoyer une copie de l'instance majorée du nombre de mois fourni en paramètre |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage des valeurs valides pour le champ fourni en paramètre. Seul MONTH_OF_YEAR renvoie une valeur comprise entre 1 et 12. Les autres champs renvoient une exception de type UnsupportedTemporalTypeException |
static Month valueOf(String name) |
Renvoyer la constante associée au nom fourni en paramètre |
static Month[] values() |
Renvoyer un tableau des constantes dans l'ordre dans lequel elles sont déclarées |
Il ne faut pas utiliser la méthode ordinal() pour obtenir la valeur numérique mais utiliser la méthode getValue().
La méthode getDisplayName() permet d'obtenir le libellé en différente taille pour la Locale fournie en paramètre. La taille est fournie en paramètre grâce à un objet de type java.time.format.TextStyle qui est une énumération (FULL, FULL_STANDALONE, NARROW, NARROW_STANDALONE, SHORT, SHORT_STANDALONE).
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Month;
import java.time.format.TextStyle;
import java.util.Locale;
public class TestMonth {
public static void main(String[] args) {
Month month = Month.NOVEMBER;
Locale locale = Locale.getDefault();
System.out.println(month.getDisplayName(TextStyle.FULL, locale));
System.out.println(month.getDisplayName(TextStyle.SHORT, locale));
System.out.println(month.getDisplayName(TextStyle.NARROW, locale));
}
}
Résultat : |
novembre
N
nov.
La classe YearMonth encapsule un mois d'une année donnée, par exemple octobre 2014. Elle permet d'encapsuler une date partielle pour laquelle le jour n'est pas important. Par exemple, un cas d'utilisation est la date d'expiration d'une carte de crédit. Cette classe est immuable et thread-safe.
Elle possède plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dont le mois et l'année sont ajustés avec ceux encapsulés |
LocalDate atDay(int dayOfMonth) |
Combiner les données de l'instance avec le jour du mois fourni en paramètre pour créer une instance de type LocalDate. |
LocalDate atEndOfMonth() |
Renvoyer une instance de type LocalDate qui encapsule la date du dernier jour du mois |
int compareTo(YearMonth other) |
Comparer l'instance avec celle fournie en paramètre |
boolean equals(Object obj) |
Tester l'égalité de l'instance avec l'objet fourni en paramètre |
String format(DateTimeFormatter formatter) |
Formater l'instance en utilisant le Formatter fourni en paramètre |
static YearMonth from(TemporalAccessor temporal) |
Obtenir une instance de type YearMonth à partir des champs encapsulés dans l'objet fourni en paramètre. |
int get(TemporalField field) |
Obtenir la valeur du champ demandé en paramètre sous la forme d'un entier |
long getLong(TemporalField field) |
Obtenir la valeur du champ demandé en paramètre sous la forme d'un entier long |
Month getMonth() |
Renvoyer le mois encapsulé sous la forme d'une instance de type Month |
int getMonthValue() |
Obtenir le mois de l'année sous la forme d'un entier compris entre 1 et 12 |
int getYear() |
Renvoyer l'année encapsulée dans l'instance |
boolean isAfter(YearMonth other) |
Comparer si l'instance est postérieure à celle fournie en paramètre |
boolean isBefore(YearMonth other) |
Comparer si l'instance est antérieure à celle fournie en paramètre |
boolean isLeapYear() |
Indiquer si l'année encapsulée est bissextile |
boolean isSupported(TemporalField field) |
Préciser si le champ fourni en paramètre est supporté |
boolean isSupported(TemporalUnit unit) |
Préciser si l'unité de temps fournie en paramètre est supportée |
boolean isValidDay(int dayOfMonth) |
Vérifier si le jour du mois fourni en paramètre est valide pour cette instance |
int lengthOfMonth() |
Renvoyer le nombre de jours du mois encapsulé |
int lengthOfYear() |
Renvoyer le nombre de jours de l'année encapsulée |
YearMonth minus(long amountToSubtract, TemporalUnit unit)YearMonth minus(TemporalAmount amountToSubtract) |
Renvoyer une copie de l'instance pour laquelle la valeur temporelle fournie en paramètre est soustraite |
YearMonth minusMonths(long monthsToSubtract) |
Renvoyer une copie de l'instance pour laquelle le nombre de mois fourni en paramètre est soustrait |
YearMonth minusYears(long yearsToSubtract) |
Renvoyer une copie de l'instance pour laquelle le nombre d'années fourni en paramètre est soustrait |
static YearMonth now() |
Obtenir une instance encapsulant le mois et l'année de l'horloge système dans le fuseau horaire par défaut |
static YearMonth now(Clock clock) |
Obtenir une instance encapsulant le mois et l'année de l'horloge fournie en paramètre |
static YearMonth now(ZoneId zone) |
Obtenir une instance encapsulant le mois et l'année de l'horloge système dans le fuseau horaire fourni par défaut |
static YearMonth of(int year, int month) static YearMonth of(int year, Month month) |
Obtenir une instance encapsulant le mois et l'année fournis en paramètre |
static YearMonth parse(CharSequence text) |
Obtenir une instance à partir de l'analyse utilisant le DateTimeFormatter par défaut de la représentation textuelle fournie en paramètre (exemple : 2014-12) |
static YearMonth parse(CharSequence text, DateTimeFormatter formatter) |
Obtenir une instance à partir de l'analyse utilisant le DateTimeFormatter et la représentation textuelle fournis en paramètre |
YearMonth plus(long amountToAdd, TemporalUnit unit) YearMonth plus(TemporalAmount amountToAdd) |
Renvoyer une copie de l'instance à laquelle la valeur temporelle fournie en paramètre est ajoutée |
YearMonth plusMonths(long monthsToAdd) |
Renvoyer une copie de l'instance à laquelle le nombre de mois fourni en paramètre est ajouté |
YearMonth plusYears(long yearsToAdd) |
Renvoyer une copie de l'instance à laquelle le nombre d'années fourni en paramètre est ajouté |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage de valeurs valides pour le champ fourni en paramètre |
String toString() |
Obtenir une représentation textuelle de l'instance (exemple : 2014-12) |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps entre l'instance et l'objet temporel fourni en paramètre. Le résultat est obtenu dans l'unité précisé en paramètre |
YearMonth with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet fourni en paramètre qui encapsule les traitements |
YearMonth with(TemporalField field, long newValue) |
Obtenir une instance dans laquelle la valeur du champ fournie en paramètre est remplacée |
YearMonth withMonth(int month) |
Renvoyer une copie de l'instance encapsulant le mois précisé |
YearMonth withYear(int year) |
Renvoyer une copie de l'instance encapsulant l'année précisée |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Month;
import java.time.YearMonth;
public class TestYearMonth {
public static void main(String[] args) {
YearMonth yearMonth = YearMonth.of(2012, Month.FEBRUARY);
System.out.println(yearMonth+" : " + yearMonth.lengthOfMonth()+" jours");
yearMonth = YearMonth.of(2014, Month.FEBRUARY);
System.out.println(yearMonth+" : " + yearMonth.lengthOfMonth()+" jours");
yearMonth = YearMonth.parse("2014-12");
yearMonth = yearMonth.plusYears(2);
System.out.println(yearMonth);
}
}
Résultat : |
2012-02 : 29 jours
2014-02 : 28 jours
2016-12
Il ne faut pas utiliser d'opérations sur une instance de type YearMonth requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe MonthDay encapsule de manière immuable un mois et un jour dans ce mois comme par exemple le jour de l'an, le premier janvier ou un anniversaire. Cette classe est immuable et thread-safe.
Elle possède plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans laquelle les champs jour et mois sont remplacés par ceux encapsulés |
LocalDate atYear(int year) |
Combiner le jour et le mois encapsulé avec l'année fournie en paramètre pour obtenir une instance de type LocalDate |
int compareTo(MonthDay other) |
Comparer l'instance avec celle fournie en paramètre |
boolean equals(Object obj) |
Tester l'égalité de l'instance avec celle fournie en paramètre |
String format(DateTimeFormatter formatter) |
Formater l'instance en utilisant le Formatter fourni en paramètre |
static MonthDay from(TemporalAccessor temporal) |
Obtenir une instance de type MonthDay à partir des champs encapsulés dans l'objet fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre |
int getDayOfMonth() |
Obtenir la valeur du champ day-of-month encapsulé |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre |
Month getMonth() |
Obtenir le mois encapsulé correspondant au champ month-of-year sous la forme d'un objet de type Month |
int getMonthValue() |
Obtenir le mois encapsulé correspondant au champ month-of-year sous la forme d'un entier de 1 à 12 |
boolean isAfter(MonthDay other) |
Comparer si l'instance est postérieure à celle fournie en paramètre |
boolean isBefore(MonthDay other) |
Comparer si l'instance est antérieure à celle fournie en paramètre |
boolean isSupported(TemporalField field) |
Vérifier si le champ fourni en paramètre est supporté |
boolean isValidYear(int year) |
Vérifier si le jour et le mois encapsulés sont valides pour l'année fournie en paramètre |
static MonthDay now() |
Obtenir une instance de type MonthDay à partir de la date courante du système dans le fuseau horaire par défaut |
static MonthDay now(Clock clock) |
Obtenir une instance de type MonthDay à partir de l'instance de type Clock fournie en paramètre |
static MonthDay now(ZoneId zone) |
Obtenir une instance de type MonthDay à partir de la date courante du système dans le fuseau horaire fourni en paramètre |
static MonthDay of(int month, int dayOfMonth) |
Obtenir une instance de type MonthDay encapsulant le jour et le mois fourni en paramètre |
static MonthDay of(Month month, int dayOfMonth) |
Obtenir une instance de type MonthDay encapsulant le jour et le mois fourni en paramètre |
static MonthDay parse(CharSequence text) |
Obtenir une instance à partir de l'analyse utilisant le DateTimeFormatter par défaut de la représentation textuelle fournie en paramètre (par exemple --02-29) |
static MonthDay parse(CharSequence text, DateTimeFormatter formatter) |
Obtenir une instance de type MonthDay encapsulant le jour et le mois extraits du texte en utilisant le Formatter fournis en paramètre |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage des valeurs valides pour le champ fourni en paramètre |
String toString() |
Obtenir une représentation textuelle de l'instance en utilisant le DateTimeFormatter par défaut (par exemple --02-29) |
MonthDay with(Month month) |
Renvoyer une copie de l'instance dont le mois encapsulé est celui fourni en paramètre |
MonthDay withDayOfMonth(int dayOfMonth) |
Renvoyer une copie de l'instance dont le jour encapsulé est celui fourni en paramètre |
MonthDay withMonth(int month) |
Renvoyer une copie de l'instance dont le mois encapsulé est celui fourni en paramètre |
La méthode isValidYear() permet de vérifier si le jour du mois encapsulé est valide pour l'année fournie en paramètre. Attention, elle n'encapsule pas l'année : elle ne peut donc pas déterminer si l'année est bissextile et considère donc toujours comme valide le 29 février.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Month;
import java.time.MonthDay;
public class TestMonthDay {
public static void main(String[] args) {
MonthDay date = MonthDay.of(Month.FEBRUARY, 29);
System.out.println(date.isValidYear(2014));
}
}
Résultat : |
false
Elle implémente l'interface TemporalAccessor mais elle ne permet que l'accès aux champs MONTH_OF_YEAR et DAY_OF_MONTH.
Il ne faut pas utiliser d'opérations sur une instance de type YearMonth requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe Year encapsule de manière immuable une année du calendrier ISO-8601.
Elle possède plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans laquelle le champ année est remplacé par celui encapsulé |
LocalDate atDay(int dayOfYear) |
Combiner le jour de l'année fourni en paramètre avec l'année encapsulée pour obtenir une instance de type LocalDate |
YearMonth atMonth(int month) |
Combiner le mois fourni en paramètre avec l'année encapsulée pour obtenir une instance de type YearMonth |
YearMonth atMonth(Month month) |
Combiner le mois fourni en paramètre avec l'année encapsulée pour obtenir une instance de type YearMonth |
LocalDate atMonthDay(MonthDay monthDay) |
Combiner le jour et le mois fournis en paramètre avec l'année encapsulée pour obtenir une instance de type LocalDate |
int compareTo(Year other) |
Comparer l'instance avec l'année fournie en paramètre |
boolean equals(Object obj) |
Vérifier l'égalité avec l'instance fournie en paramètre |
String format(DateTimeFormatter formatter) |
Obtenir une représentation textuelle en utilisant le Formatter fourni en paramètre |
static Year from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier long |
int getValue() |
Obtenir la valeur de l'année encapsulée |
boolean isAfter(Year other) |
Vérifier si l'année encapsulée est postérieure à celle fournie en paramètre |
boolean isBefore(Year other) |
Vérifier si l'année encapsulée est antérieure à celle fournie en paramètre |
boolean isLeap() |
Vérifier si l'année encapsulée est bissextile selon le calendrier ISO |
static boolean isLeap(long year) |
Vérifier si l'année est bissextile selon le calendrier ISO |
boolean isSupported(TemporalField field) |
Vérifier si le champ fourni en paramètre est supporté |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité de temps est supportée |
boolean isValidMonthDay(MonthDay monthDay) |
Vérifier si le jour et le mois encapsulés dans le MonthDay fourni en paramètre est valide |
int length() |
Obtenir le nombre de jours de l'année encapsulée |
Year minus(long amountToSubtract, TemporalUnit unit) |
Renvoyer une copie de l'instance pour laquelle la quantité de temps fournie en paramètre a été soustraite |
Year minus(TemporalAmount amountToSubtract) |
Renvoyer une copie de l'instance pour laquelle le nombre d'années fourni en paramètre a été soustrait |
Year minusYears(long yearsToSubtract) |
Renvoyer une copie de l'instance pour laquelle le nombre d'années fourni en paramètre a été soustrait |
static Year now() |
Obtenir une instance de type Year encapsulant l'année de la date courante en utilisant le fuseau horaire par défaut |
static Year now(Clock clock) |
Obtenir une instance de type Year encapsulant l'année obtenue de l'instance de type Clock passée en paramètre |
static Year now(ZoneId zone) |
Obtenir une instance de type Year encapsulant l'année de la date courante en utilisant le fuseau horaire passé en paramètre |
static Year of(int isoYear) |
Obtenir une instance de type Year encapsulant l'année passée en paramètre. |
static Year parse(CharSequence text) |
Obtenir une instance de type Year encapsulant l'année passée en paramètre |
static Year parse(CharSequence text, DateTimeFormatter formatter) |
Obtenir une instance de type Year encapsulant l'année extraite du texte en utilisant le Formatter fourni en paramètre |
Year plus(long amountToAdd, TemporalUnit unit) |
Renvoyer une copie de l'instance encapsulant une année à laquelle la valeur passée en paramètre a été ajoutée |
Year plus(TemporalAmount amountToAdd) |
Renvoyer une copie de l'instance encapsulant une année à laquelle la valeur passée en paramètre a été ajoutée |
Year plusYears(long yearsToAdd) |
Renvoyer une copie de l'instance à laquelle le nombre d'années fourni en paramètre est ajouté |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage des valeurs valides pour le champ fourni en paramètre |
String toString() |
Obtenir une représentation textuelle qui utilise le Formatter par défaut |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps entre l'instance et l'objet temporel fourni en paramètre. Le résultat est obtenu dans l'unité précisée en paramètre |
Year with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet qui encapsule les traitements fourni en paramètre |
Year with(TemporalField field, long newValue) |
Obtenir une instance dans laquelle la valeur du champ fourni en paramètre est remplacée |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Year;
public class TestYear {
public static void main(String[] args) {
boolean estBissextile = Year.of(2016).isLeap();
System.out.println(estBissextile);
}
}
Elle implémente l'interface TemporalAccessor mais elle ne permet que l'accès aux champs YEAR_OF_ERA, YEAR et ERA.
Il ne faut pas utiliser d'opérations sur une instance de type Year requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
Le temps machine représente une quantité de temps depuis un point d'origine : l'API Date-Time le gère sous la forme d'un entier long représentant le nombre de millisecondes écoulées depuis le 1er janvier 1970 à 00:00:00 désigné par le terme epoch.
La classe java.time.Instant est une des principales classes de l'API Date-Time : elle encapsule un point sur la ligne du temps.
La classe Instant définit plusieurs constantes :
Elle possède de nombreuses méthodes pour obtenir une instance ou réaliser des opérations dessus :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie du même type de l'objet temporel fourni en paramètre encapsulant une représentation de l'instance courante |
OffsetDateTime atOffset(ZoneOffset offset) |
Obtenir une instance de type OffsetDateTime qui combine l'instance courante et l'objet fourni en paramètre |
ZonedDateTime atZone(ZoneId zone) |
Obtenir une instance de type ZonedDateTime qui combine l'instance courante et l'objet fourni en paramètre |
int compareTo(Instant otherInstant) |
Comparer l'instance courante avec celle fournie en paramètre |
boolean equals(Object otherInstant) |
Vérifier l'égalité de l'instance courante avec celle fournie en paramètre |
static Instant from(TemporalAccessor temporal) |
Obtenir une instance à partir des données de l'objet fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier |
long getEpochSecond() |
Obtenir le nombre de secondes écoulées depuis l'EPOCH |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier long |
int getNano() |
Obtenir le nombre de nanosecondes écoulées depuis le début de la seconde |
boolean isAfter(Instant otherInstant) |
Vérifier si l'instance courante est après l'Instant fourni en paramètre dans la ligne du temps |
boolean isBefore(Instant otherInstant) |
Vérifier si l'instance courante est avant l'Instant fourni en paramètre dans la ligne du temps |
boolean isSupported(TemporalField field) |
Vérifier si le champ précisé en paramètre est supporté par l'objet |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité temporelle précisée en paramètre est supportée par l'objet |
Instant minus(long amountToSubtract, TemporalUnit unit) Instant minus(TemporalAmount amountToSubtract) |
Obtenir une copie de l'instance minorée de la quantité temporelle fournie en paramètre |
Instant minusMillis(long millisToSubtract) |
Obtenir une copie de l'instance minorée du nombre de millisecondes fourni en paramètre |
Instant minusNanos(long nanosToSubtract) |
Obtenir une copie de l'instance minorée du nombre de nanosecondes fourni en paramètre |
Instant minusSeconds(long secondsToSubtract) |
Obtenir une copie de l'instance minorée du nombre de secondes fourni en paramètre |
static Instant now() |
Obtenir une instance qui encapsule le moment présent de l'horloge système |
static Instant now(Clock clock) |
Obtenir une instance qui encapsule le moment présent de l'horloge fournie en paramètre |
static Instant ofEpochMilli(long epochMilli) |
Obtenir une instance qui encapsule le moment correspondant au nombre de millisecondes depuis l'EPOCH |
static Instant ofEpochSecond(long epochSecond) |
Obtenir une instance qui encapsule le moment correspondant au nombre de secondes depuis l'EPOCH |
static Instant parse(CharSequence text) |
Analyser, avec le DateTimeFormatter par défaut, la chaîne de caractères fournie en paramètre pour créer une instance (Exemple : 2014-12-25T23:59:59Z) |
Instant plus(long amountToAdd, TemporalUnit unit) Instant plus(TemporalAmount amountToAdd) |
Obtenir une copie de l'instance majorée de la quantité temporelle fournie en paramètre |
Instant plusMillis(long millisToAdd) |
Obtenir une copie de l'instance majorée du nombre de millisecondes fourni en paramètre |
Instant plusNanos(long nanosToAdd) |
Obtenir une copie de l'instance majorée du nombre de nanosecondes fourni en paramètre |
Instant plusSeconds(long secondsToAdd) |
Obtenir une copie de l'instance majorée du nombre de secondes fourni en paramètre |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage de valeurs valides pour le champ précisé en paramètre |
long toEpochMilli() |
Obtenir le nombre de millisecondes écoulées entre l'instant encapsulé et l'EPOCH |
String toString() |
Obtenir une représentation textuelle au format ISO-8601 de l'instance |
Instant truncatedTo(TemporalUnit unit) |
Obtenir une copie de l'instance tronquée à l'unité fournie en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps entre l'instance et l'objet temporel fourni en paramètre. Le résultat est obtenu dans l'unité précisée en paramètre |
Instant with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet qui encapsule les traitements fourni en paramètre |
Instant with(TemporalField field, long newValue) |
Renvoyer une copie de l'instance dont la valeur du champ précisé est remplacée par la valeur fournie |
La classe Instant encapsule le nombre de secondes écoulées depuis le 1er janvier 1970 (1970-01-01T00:00:00Z) désigné par la constante EPOCH sous la forme d'un entier long avec une précision à la nanoseconde sous la forme d'un second entier de type int. Si l'instant encapsulé est antérieure à l'EPOCH alors la valeur est négative sinon elle est positive.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Instant;
public class TestInstant {
public static void main(String[] args) {
System.out.println(Instant.EPOCH);
Instant instant = Instant.now();
System.out.println(instant);
}
}
Résultat : |
1970-01-01T00:00:00Z
2014-12-14T14:21:38.031Z
La classe Instant permet de gérer un temps machine mais ne permet pas de gérer un temps humain. Dans ce cas, il faut convertir l'Instant en une classe qui encapsule le temps de manière humaine : LocalDateTime ou ZonedDateTime par exemple.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Instant;
import java.time.LocalDateTime;
public class TestInstant {
public static void main(String[] args) {
Instant instant = Instant.now();
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant,
ZoneId.systemDefault());
}
}
Un objet de type ZonedDateTime ou OffsetTimeZone peut être converti en une instance de type Instant mais pas l'inverse sans préciser respectivement le fuseau ou le décalage horaire.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Instant;
import java.time.ZonedDateTime;
public class TestInstant {
public static void main(String[] args) {
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
Instant instant1 = zonedDateTime.toInstant();
System.out.println(instant1);
Instant instant2 = Instant.from(zonedDateTime);
System.out.println(instant2);
}
}
La classe java.time.Duration encapsule une durée (la distance entre deux points dans le temps) sous la forme d'une quantité temporelle. Elle encapsule une durée temporelle stockée en interne sous la forme de valeurs exprimées en secondes et nanosecondes. Elle est donc utile lors de l'utilisation de temps machine.
Remarque : la classe Period permet aussi d'encapsuler une durée mais exprimée de façon humaine sous la forme de différentes valeurs temporelles (année, mois, jours).
Attention : le JDK contient deux classes Duration respectivement dans les packages javax.xml.datatype et java.time. JavaFx possède aussi une classe Duration dans le package javafx.util.
La valeur encapsulée peut être négative si l'instant de début est plus récent que l'instant de fin.
Elle définit une constante Duration.ZERO qui correspond à une durée nulle d'un point de vue fonctionnelle.
Elle possède de nombreuses méthodes pour créer une nouvelle instance ou obtenir une instance qui soit une copie modifiée de l'instance courante :
Méthode |
Rôle |
Duration abs() |
Obtenir une copie dont la quantité encapsulée est toujours positive |
Temporal addTo(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre en lui ajoutant la durée encapsulée |
static Duration between(Temporal startInclusive, Temporal endExclusive) |
Obtenir une instance qui encapsule la durée entre les deux représentations temporelles fournies en paramètres incluses |
int compareTo(Duration otherDuration) |
Comparer l'instance courante avec celle fournie en paramètre |
Duration dividedBy(long divisor) |
Obtenir une copie dont la quantité encapsulée est divisée par la quantité fournie en paramètre |
boolean equals(Object otherDuration) |
Vérifier l'égalité de l'instance courante avec celle fournie en paramètre |
static Duration from(TemporalAmount amount) |
Obtenir une instance qui encapsule la durée correspondant à la quantité temporelle fournie en paramètre |
long get(TemporalUnit unit) |
Obtenir la valeur dans l'unité demandée |
int getNano() |
Obtenir le nombre de nanosecondes de la durée encapsulée |
long getSeconds() |
Obtenir le nombre de secondes de la durée encapsulée |
List<TemporalUnit> getUnits() |
Obtenir la liste des unités temporelles supportées |
boolean isNegative() |
Vérifier si la valeur encapsulée est strictement inférieure à zéro |
boolean isZero() |
Vérifier si la durée est égale à zéro |
Duration minus(Duration duration) Duration minus(long amountToSubtract, TemporalUnit unit) |
Obtenir une copie pour laquelle la durée est retirée de celle fournie en paramètre |
Duration minusDays(long daysToSubtract) |
Obtenir une copie dont la durée est minorée du nombre de jours fourni en paramètre |
Duration minusHours(long hoursToSubtract) |
Obtenir une copie dont la durée est minorée du nombre d'heures fourni en paramètre |
Duration minusMillis(long millisToSubtract) |
Obtenir une copie dont la durée est minorée du nombre de millisecondes fourni en paramètre |
Duration minusMinutes(long minutesToSubtract) |
Obtenir une copie dont la durée est minorée du nombre de minutes fourni en paramètre |
Duration minusNanos(long nanosToSubtract) |
Obtenir une copie dont la durée est minorée du nombre de nanosecondes fourni en paramètre |
Duration minusSeconds(long secondsToSubtract) |
Obtenir une copie dont la durée est minorée du nombre de secondes fourni en paramètre |
Duration multipliedBy(long multiplicand) |
Obtenir une copie dont la durée est multipliée par la valeur fournie en paramètre |
Duration negated() |
Obtenir une copie dont la valeur est multipliée par -1 (inversion de la valeur positive/négative) |
static Duration of(long amount, TemporalUnit unit) |
Obtenir une instance qui encapsule la durée correspondant à la quantité temporelle fournie en paramètre sous la forme d'une quantité et de son unité |
static Duration ofDays(long days) |
Obtenir une durée qui correspond au nombre de jours fournis en paramètre (une journée correspond toujours à 24 heures) |
static Duration ofHours(long hours) |
Obtenir une durée qui correspond au nombre d'heures fourni en paramètre |
static Duration ofMillis(long millis) |
Obtenir une durée qui correspond au nombre de millisecondes fourni en paramètre |
static Duration ofMinutes(long minutes) |
Obtenir une durée qui correspond au nombre de minutes fourni en paramètre |
static Duration ofNanos(long nanos) |
Obtenir une durée qui correspond au nombre de nanosecondes fourni en paramètre |
static Duration ofSeconds(long seconds) |
Obtenir une durée qui correspond au nombre de secondes fourni en paramètre |
static Duration ofSeconds(long seconds, long nanoAdjustment) |
Obtenir une durée qui correspond au nombre de secondes et nanosecondes fournis en paramètre |
static Duration parse(CharSequence text) |
Obtenir une instance en analysant la chaîne de caractères fournie en paramètre. Le format doit respecter le format PnDTnHnMn.nS.ou n correspond à la valeur d'un champ |
Duration plus(Duration duration) |
Obtenir une copie qui encapsule la durée majorée de la durée fournie en paramètre |
Duration plus(long amountToAdd, TemporalUnit unit) |
Obtenir une copie qui encapsule la durée majorée de la quantité temporelle fournie en paramètre sous la forme d'une quantité et de son unité |
Duration plusDays(long daysToAdd) |
Obtenir une copie qui encapsule la durée majorée du nombre de jours fourni en paramètre |
Duration plusHours(long hoursToAdd) |
Obtenir une copie qui encapsule la durée majorée du nombre d'heures fourni en paramètre |
Duration plusMillis(long millisToAdd) |
Obtenir une copie qui encapsule la durée majorée du nombre de millisecondes fourni en paramètre |
Duration plusMinutes(long minutesToAdd) |
Obtenir une copie qui encapsule la durée majorée du nombre de minutes fourni en paramètre |
Duration plusNanos(long nanosToAdd) |
Obtenir une copie qui encapsule la durée majorée du nombre de nanosecondes fourni en paramètre |
Duration plusSeconds(long secondsToAdd) |
Obtenir une copie qui encapsule la durée majorée du nombre secondes fourni en paramètre |
Temporal subtractFrom(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dont la durée a été soustraite |
long toDays() |
Obtenir le nombre de jours de la durée |
long toHours() |
Obtenir le nombre d'heures de la durée |
long toMillis() |
Obtenir le nombre de millisecondes de la durée |
long toMinutes() |
Obtenir le nombre de minutes de la durée |
long toNanos() |
Obtenir le nombre de nanosecondes de la durée |
String toString() |
Obtenir une représentation textuelle de la durée respectant le format ISO-8601 |
Duration withNanos(int nanoOfSecond) |
Obtenir une copie de l'instance avec le nombre de nanosecondes fourni en paramètre |
Duration withSeconds(long seconds) |
Obtenir une copie de l'instance avec le nombre de secondes fourni en paramètre |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalUnit;
public class TestDuration {
public static void main(String[] args) {
Duration uneHeure = Duration.ofHours(1L);
System.out.println("uneHeure = "+uneHeure);
uneHeure = Duration.ofMinutes(60L);
System.out.println("uneHeure = "+uneHeure);
uneHeure = Duration.ofSeconds(3600L);
System.out.println("uneHeure = "+uneHeure);
System.out.println("uneHeure = " +
uneHeure.get(ChronoUnit.SECONDS) + " secondes");
System.out.print("TemporalUnit supportees : ");
for (TemporalUnit tu : uneHeure.getUnits()) {
System.out.print(tu+" ");
}
System.out.println();
Instant instant1 = Instant.parse("2014-12-25T23:59:59Z");
Instant instant2 = instant1.plus(1L, ChronoUnit.DAYS);
Duration duree = Duration.between(instant1, instant2);
System.out.println("duree = "+duree);
instant1 = Instant.parse("2014-12-25T23:59:59Z");
instant2 = Instant.parse("2011-12-25T23:59:59Z");
duree = Duration.between(instant1, instant2);
System.out.println("duree = "+duree);
Duration dureeN = uneHeure.negated();
System.out.println("uneHeure = "+uneHeure + "
"+dureeN.isNegative());
dureeN = dureeN.negated();
System.out.println("uneHeure = "+uneHeure + "
"+dureeN.isNegative());
boolean estEgal = Duration.ofHours(1L).equals(Duration.ofSeconds(3600L));
System.out.println("est egal = " +estEgal);
duree = Duration.ofDays(1L);
Instant instant = instant1.plus(duree);
System.out.println("instant = "+instant);
duree = Duration.parse("P2DT8H30M12S");
System.out.println("duree = "+duree);
}
}
Résultat : |
uneHeure = PT1H
uneHeure = PT1H
uneHeure = PT1H
uneHeure = 3600 secondes
TemporalUnit supportees : Seconds Nanos
duree = PT24H
duree = PT-26304H
uneHeure = PT1H true
uneHeure = PT1H false
est egal = true
instant = 2014-12-26T23:59:59Z
duree = PT56H30M12S
Remarque : une journée correspond à une Duration de 24 heures. L'ajout d'une Duration correspondant à une journée à une instance de type ZonedDateTime va ajouter 24 heures quelque soit la situation : cette opération ne tiendra pas compte d'un décalage horaire comme l'heure d'été/hiver.
Le standard ISO-8601 définit le format textuel d'une durée : PTnHnMn.nS
Si le format ne peut pas être correctement analysé par la méthode parse(), celle-ci lève une exception de type DataTimeParseException avec le message « Text cannot be parsed to a Duration ».
Le temps humain est représenté sous la forme de champs entiers dans un calendrier pour la date ( année, mois, jour) et d'heures, minutes, secondes, millisecondes et nanosecondes
Les classes java.time.LocalDate, java.time.LocalTime et java.time.LocalDateTime représentent des dates et/ou des heures sans fuseau horaire. Ce sont des objets temporels locaux dans le sens où ils ne sont valides que dans un contexte particulier et ne sont pas utilisables en dehors de ce contexte : ce contexte est généralement la machine sur laquelle le code s'exécute. Le préfixe Local fait référence à local par rapport au système dans lequel le code s'exécute : elles ne prennent pas en compte de fuseau ou de décalage horaire.
Les classes LocalDate, LocalTime et LocalDateTime ont différentes méthodes permettant de récupérer une partie de la date, de la tester et d'effectuer des opérations dessus qui renvoient une copie altérée de l'instance.
La classe LocalDate encapsule de manière immuable une date sous la forme d'une année, d'un mois et d'un jour dans le calendrier ISO sans fuseau horaire. Elle est utile pour encapsuler une date sans heure, par exemple une date d'anniversaire.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Month;
public class TestLocalDate {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2014, Month.DECEMBER, 25);
System.out.println(date);
}
}
Résultat : |
2014-12-25
Comme elle ne possède pas de fuseau horaire, elle ne représente pas un point dans le temps.
Elle possède plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie du même type de l'objet temporel fourni en paramètre encapsulant une représentation de l'instance courante (année, mois, jour) |
LocalDateTime atStartOfDay() |
Renvoyer une instance de type LocalDatetime qui combine la date encapsulée et minuit comme heure |
ZonedDateTime atStartOfDay(ZoneId zone) |
Renvoyer une instance de type ZonedDateTime qui combine la date encapsulée et l'heure à minuit en tenant compte du fuseau horaire passé en paramètre |
LocalDateTime atTime(int hour, int minute) |
Renvoyer une instance de type LocalDatetime qui combine la date encapsulée et l'heure passée en paramètre |
OffsetDateTime atTime(OffsetTime time) |
Renvoyer une instance de type OffsetDateTime qui combine la date encapsulée et l'OffsetTime passé en paramètre |
int compareTo(ChronoLocalDate other) |
Comparer la date encapsulée avec celle fournie en paramètre |
boolean equals(Object obj) |
Vérifier l'égalité entre la date encapsulée et l'objet fourni en paramètre |
String format(DateTimeFormatter formatter) |
Formater la date encapsulée en utilisant le Formatter passé en paramètre |
static LocalDate from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet fourni en paramètre |
int get(TemporalField field) |
Obtenir un entier qui est la valeur du champ passé en paramètre de la date encapsulée |
IsoChronology getChronology() |
Obtenir le système calendaire (calendrier ISO) |
int getDayOfMonth() |
Obtenir un entier qui représente le jour du mois de la date encapsulée |
DayOfWeek getDayOfWeek() |
Renvoyer le jour de la semaine de la date encapsulée |
int getDayOfYear() |
Renvoyer un entier qui représente le numéro du jour dans l'année |
Era getEra() |
Renvoyer l'ère du système calendaire à laquelle la date encapsulée appartient |
long getLong(TemporalField field) |
Obtenir un entier long qui représente le jour du mois de la date encapsulée |
Month getMonth() |
Renvoyer le mois de l'année de la date encapsulée |
int getMonthValue() |
Renvoyer le mois de l'année de la date encapsulée sous la forme d'un entier (de 1 à 12) |
int getYear() |
Renvoyer l'année |
boolean isAfter(ChronoLocalDate other) |
Vérifier si la date encapsulée est postérieure à celle fournie en paramètre |
boolean isBefore(ChronoLocalDate other) |
Vérifier si la date encapsulée est antérieure à celle fournie en paramètre |
boolean isEqual(ChronoLocalDate other) |
Vérifier si la date encapsulée est égale à celle fournie en paramètre |
boolean isLeapYear() |
Vérifier si l'année de la date encapsulée est bissextile selon les règles du calendrier ISO |
boolean isSupported(TemporalField field) |
Vérifier si le champ temporel passé en paramètre est supporté par cette classe |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité temporelle passée en paramètre est supportée par cette classe |
int lengthOfMonth() |
Renvoyer le nombre de jours du mois de la date encapsulée |
int lengthOfYear() |
Renvoyer le nombre de jours de l'année de la date encapsulée |
LocalDate minus(long amountToSubtract, TemporalUnit unit) LocalDate minus(TemporalAmount amountToSubtract) |
Renvoyer une copie de l'objet avec une portion de temps soustraite |
LocalDate minusDays(long daysToSubtract) |
Renvoyer une copie de l'objet avec le nombre de jours fourni soustrait |
LocalDate minusMonths(long monthsToSubtract) |
Renvoyer une copie de l'objet avec le nombre de mois fourni soustrait |
LocalDate minusWeeks(long weeksToSubtract) |
Renvoyer une copie de l'objet avec le nombre de semaines fourni soustrait |
LocalDate minusYears(long yearsToSubtract) |
Renvoyer une copie de l'objet avec le nombre d'années fourni soustrait |
static LocalDate now() |
Renvoyer une instance encapsulant la date courante de l'horloge système en utilisant le fuseau horaire par défaut |
static LocalDate now(Clock clock) |
Renvoyer une instance encapsulant la date courante de l'horloge fournie en paramètre |
static LocalDate now(ZoneId zone) |
Renvoyer une instance encapsulant la date courante de l'horloge système en utilisant le fuseau horaire fourni en paramètre |
static LocalDate of(int year, int month, int dayOfMonth) |
Obtenir une instance à partir des éléments temporels fournis en paramètres |
static LocalDate parse(CharSequence text) |
Obtenir une instance à partir de l'analyse de la date fournie en paramètre sous la forme d'une chaîne de caractères en utilisant le Formatter par défaut (exemple : 2014-12-25) |
static LocalDate parse(CharSequence text, DateTimeFormatter formatter) |
Obtenir une instance à partir de l'analyse de la date fournie en paramètre sous la forme d'une chaîne de caractères en utilisant le Formatter fourni |
LocalDate plus(long amountToAdd, TemporalUnit unit) |
Renvoyer une copie de l'objet avec une portion de temps ajoutée |
LocalDate plusDays(long daysToAdd) |
Renvoyer une copie de l'objet avec le nombre de jours fourni ajouté |
LocalDate plusMonths(long monthsToAdd) |
Renvoyer une copie de l'objet avec le nombre de mois fourni ajouté |
LocalDate plusWeeks(long weeksToAdd) |
Renvoyer une copie de l'objet avec le nombre de semaines fourni ajouté |
LocalDate plusYears(long yearsToAdd) |
Renvoyer une copie de l'objet avec le nombre d'années fourni ajouté |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage de valeurs valides pour le champ fourni en paramètre |
long toEpochDay() |
Renvoyer le nombre de jours entre le 01/01/1970 et la date encapsulée |
String toString() |
Renvoyer une représentation textuelle de la date encapsulée en utilisant le formateur par défaut (exemple : 2014-12-25) |
Period until(ChronoLocalDate endDateExclusive) |
Calculer la période entre la date encapsulée et celle fournie en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité exprimée dans l'unité demandée entre la date encapsulée et l'objet temporel fourni en paramètre |
LocalDate with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet qui encapsule les traitements fourni en paramètre |
LocalDate with(TemporalField field, long newValue) |
Renvoyer une copie modifiée de la date en prenant en compte le jour de l'année fourni |
LocalDate withDayOfMonth(int dayOfMonth) |
Renvoyer une copie modifiée de la date en prenant en compte le jour du mois fourni |
LocalDate withDayOfYear(int dayOfYear) |
Renvoyer une copie modifiée de la date en prenant en compte le jour de l'année fourni |
LocalDate withMonth(int month) |
Renvoyer une copie modifiée de la date en prenant en compte le mois fourni |
LocalDate withYear(int year) |
Renvoyer une copie modifiée de la date en prenant en compte l'année fournie |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
public class TestLocalDate {
public static void main(String[] args) {
LocalDate date = LocalDate.now();
System.out.println("date = "+date);
date = LocalDate.now(ZoneId.of("America/Los_Angeles"));
System.out.println("date = "+date);
date = LocalDate.ofEpochDay(16428);
System.out.println("date = "+date);
date = LocalDate.of(2014, Month.DECEMBER, 25);
System.out.println("date = "+date);
System.out.println("Calendrier = "+date.getChronology());
System.out.println("startOfDay = " + date.atStartOfDay(ZoneId.systemDefault()));
System.out.println("dayOfMonth = "+date.getDayOfMonth());
System.out.println("dayOfWeek = "+date.getDayOfWeek());
System.out.println("dayOfYear = "+date.getDayOfYear());
System.out.println("ere = "+date.getEra());
System.out.println("support HOURS = "+date.isSupported(ChronoUnit.HOURS));
System.out.println("support HOUR_Of_DAY =
"+date.isSupported(ChronoField.HOUR_OF_DAY));
System.out.println("lengthOfMonth = "+date.lengthOfMonth());
System.out.println("lengthOfYear = "+date.lengthOfYear());
System.out.println("2 semaines avant = "+date.minusWeeks(2));
}
}
Résultat : |
date = 2015-02-19
date = 2015-02-19
date = 2014-12-24
date = 2014-12-25
Calendrier = ISO
startOfDay = 2014-12-25T00:00+01:00[Europe/Paris]
dayOfMonth = 25
dayOfWeek = THURSDAY
dayOfYear = 359
ere = CE
support HOURS = false
support HOUR_Of_DAY = false
lengthOfMonth = 31
lengthOfYear = 365
2 semaines avant = 2014-12-11
La classe LocalDate permet aussi un accès aux champs : day-of-year, day-of-week et week-of-year.
Il ne faut pas utiliser d'opérations sur une instance de type LocalDate requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
Remarque : il n'est pas possible de convertir une instance de type LocalDate en Instant car elle ne contient pas de données relatives à l'heure qui permettrait de déterminer le point dans le temps avec une précision à la seconde.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
public class TestLocalDate {
public static void main(String[] args) {
try {
LocalDate date = LocalDate.of(2014, 12, 25);
Instant instant = Instant.from(date);
} catch (DateTimeException dte) {
dte.printStackTrace();
}
}
}
Résultat : |
java.time.DateTimeException: Unable to obtain Instant from
TemporalAccessor: 2014-12-25 of type java.time.LocalDate
at java.time.Instant.from(Instant.java:383)
at com.jmdoudoux.test.java8.datetime.TestLocalDate.main(TestLocalDate.java:13)
La classe LocalDateTime encapsule une date (année, mois, jour) et une heure (heure, minute, seconde, nanoseconde) sans fuseau horaire.
Cette classe est immuable et thread-safe.
Elle possède plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans lequel les champs sont remplacés par ceux encapsulés |
OffsetDateTime atOffset(ZoneOffset offset) |
Obtenir une instance qui combine l'instance courante avec le décalage horaire passé en paramètre |
ZonedDateTime atZone(ZoneId zone) |
Obtenir une instance qui combine l'instance courante avec le fuseau horaire passé en paramètre |
int compareTo(ChronoLocalDateTime<?> other) |
Comparer l'instance avec l'objet temporel passé en paramètre |
boolean equals(Object obj) |
Vérifier l'égalité de l'instance avec l'objet passé en paramètre |
String format(DateTimeFormatter formatter) |
Formater l'instance pour obtenir une représentation textuelle dans le format précisé par l'objet passé en paramètre |
static LocalDateTime from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet temporel fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier |
int getDayOfMonth() |
Obtenir la valeur du champ jour du mois |
DayOfWeek getDayOfWeek() |
Obtenir la valeur du champ jour de la semaine encapsulé dans une instance de type DayOfWeek |
int getDayOfYear() |
Obtenir la valeur du champ jour de l'année |
int getHour() |
Obtenir la valeur du champ heures |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre |
int getMinute() |
Obtenir la valeur du champ minutes |
Month getMonth() |
Obtenir la valeur du champ mois encapsulé dans une instance de type Month |
int getMonthValue() |
Obtenir la valeur du champ mois (1-12) |
int getNano() |
Obtenir la valeur du champ nanosecondes |
int getSecond() |
Obtenir la valeur du champ secondes |
int getYear() |
Obtenir la valeur du champ année |
boolean isAfter(ChronoLocalDateTime<?> other) |
Vérifier si l'instance courante est postérieure à l'objet temporel fourni en paramètre |
boolean isBefore(ChronoLocalDateTime<?> other) |
Vérifier si l'instance courante est antérieure à l'objet temporel fourni en paramètre |
boolean isEqual(ChronoLocalDateTime<?> other) |
Vérifier si l'instance est égale à l'objet temporel fourni en paramètre |
boolean isSupported(TemporalField field) |
Vérifier si le champ passé en paramètre est supporté |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité temporel fournie en paramètre est supportée |
LocalDateTime minus(long amountToSubtract, TemporalUnit unit) LocalDateTime minus(TemporalAmount amountToSubtract) |
Renvoyer une copie pour laquelle la quantité temporelle passé en paramètre est soustraite |
LocalDateTime minusDays(long days) |
Renvoyer une copie pour laquelle le nombre de jours passé en paramètre est soustrait |
LocalDateTime minusHours(long hours) |
Renvoyer une copie pour laquelle le nombre d'heures passé en paramètre est soustrait |
LocalDateTime minusMinutes(long minutes) |
Renvoyer une copie pour laquelle le nombre de minutes passé en paramètre est soustrait |
LocalDateTime minusMonths(long months) |
Renvoyer une copie pour laquelle le nombre de mois passé en paramètre est soustrait |
LocalDateTime minusNanos(long nanos) |
Renvoyer une copie pour laquelle le nombre de nanosecondes passé en paramètre est soustrait |
LocalDateTime minusSeconds(long seconds) |
Renvoyer une copie pour laquelle le nombre de secondes passé en paramètre est soustrait |
LocalDateTime minusWeeks(long weeks) |
Renvoyer une copie pour laquelle le nombre de semaines passé en paramètre est soustrait |
LocalDateTime minusYears(long years) |
Renvoyer une copie pour laquelle le nombre d'années passé en paramètre est soustrait |
static LocalDateTime now() |
Obtenir une instance à partir de la date/heure système et du fuseau horaire par défaut |
static LocalDateTime now(Clock clock) |
Obtenir une instance à partir de la date/heure donné par l'objet en paramètre |
static LocalDateTime now(ZoneId zone) |
Obtenir une instance à partir de la date/heure système et du fuseau horaire passé en paramètre |
static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute) |
Obtenir une instance à partir des différentes valeurs des champs passées en paramètres |
static LocalDateTime of(LocalDate date, LocalTime time) |
Obtenir une instance à partir de la date et de l'heure passées en paramètres |
static LocalDateTime ofEpochSecond(long epochSecond, int nanoOfSecond, ZoneOffset offset) |
Obtenir une instance qui encapsulera la date-heure issue du calcul de l'ajout du nombre de secondes à l'EPOCH et de la prise en compte du décalage horaire passé en paramètre |
static LocalDateTime ofInstant(Instant instant, ZoneId zone) |
Obtenir une instance à partir d'un point dans le temps et d'un fuseau horaire fournis en paramètres |
static LocalDateTime parse(CharSequence text) |
Obtenir une instance à partir de l'analyse de la chaîne de caractères passée en paramètre. Exemple 2014-12-25T23:59:59 |
static LocalDateTime parse(CharSequence text, DateTimeFormatter formatter) |
Obtenir une instance à partir de l'analyse de la date fournie en paramètre sous la forme d'une chaîne de caractères en utilisant le Formatter fourni |
LocalDateTime plus(long amountToAdd, TemporalUnit unit) LocalDateTime plus(TemporalAmount amountToAdd) |
Renvoyer une copie à laquelle la quantité temporelle passée en paramètre est ajoutée |
LocalDateTime plusDays(long days) |
Renvoyer une copie à laquelle le nombre de jours passé en paramètre est ajouté |
LocalDateTime plusHours(long hours) |
Renvoyer une copie à laquelle le nombre d'heures passé en paramètre est ajouté |
LocalDateTime plusMinutes(long minutes) |
Renvoyer une copie à laquelle le nombre de minutes passé en paramètre est ajouté |
LocalDateTime plusMonths(long months) |
Renvoyer une copie à laquelle le nombre de mois passé en paramètre est ajouté |
LocalDateTime plusNanos(long nanos) |
Renvoyer une copie à laquelle le nombre de nanosecondes passé en paramètre est ajouté |
LocalDateTime plusSeconds(long seconds) |
Renvoyer une copie à laquelle le nombre de secondes passé en paramètre est ajouté |
LocalDateTime plusWeeks(long weeks) |
Renvoyer une copie à laquelle le nombre de semaine passé en paramètre est ajouté |
LocalDateTime plusYears(long years) |
Renvoyer une copie à laquelle le nombre d'année passé en paramètre est ajouté |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage des valeurs acceptables pour le champ fourni en paramètre |
LocalDate toLocalDate() |
Obtenir une instance de type LocalDate qui contient l'heure encapsulée |
LocalTime toLocalTime() |
Obtenir une instance de type LocalTime qui contient l'heure encapsulée |
String toString() |
Obtenir une représentation textuelle de l'instance |
LocalDateTime truncatedTo(TemporalUnit unit) |
Renvoyer une copie de l'instance dont les valeurs encapsulées sont tronquées à l'unité précisée en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps entre l'instance et l'objet temporel fourni en paramètre. Le résultat est obtenu dans l'unité précisée en paramètre |
LocalDateTime with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet qui encapsule les traitements fourni en paramètre |
LocalDateTime with(TemporalField field, long newValue) |
Renvoyer une copie de l'instance dont la valeur du champ précisé en paramètre est celle fournie |
LocalDateTime withDayOfMonth(int dayOfMonth) |
Renvoyer une copie de l'instance dont le jour du mois est celui fourni en paramètre |
LocalDateTime withDayOfYear(int dayOfYear) |
Renvoyer une copie de l'instance dont le jour de l'année est celui fourni en paramètre |
LocalDateTime withHour(int hour) |
Renvoyer une copie de l'instance dont l'heure est celle fournie en paramètre |
LocalDateTime withMinute(int minute) |
Renvoyer une copie de l'instance dont les minutes sont celles fournies en paramètre |
LocalDateTime withMonth(int month) |
Renvoyer une copie de l'instance dont le mois est celui fourni en paramètre |
LocalDateTime withNano(int nanoOfSecond) |
Renvoyer une copie de l'instance dont les nanosecondes sont celles fournies en paramètre |
LocalDateTime withSecond(int second) |
Renvoyer une copie de l'instance dont les secondes de la minute sont celles fournies en paramètre |
LocalDateTime withYear(int year) |
Renvoyer une copie de l'instance dont l'année est celle fournie en paramètre |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
public class TestLocalDateTime {
public static void main(String[] args) {
LocalDateTime date = LocalDateTime.now();
System.out.println("dateTime = "+date);
date = LocalDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println("dateTime = "+date);
date = LocalDateTime.of(2014, Month.DECEMBER, 25,23,59,59);
System.out.println("dateTime = "+date);
System.out.println("date = "+date.toLocalDate());
System.out.println("time = "+date.toLocalTime());
System.out.println("Calendrier = "+date.getChronology());
System.out.println("dayOfMonth = "+date.getDayOfMonth());
System.out.println("dayOfWeek = "+date.getDayOfWeek());
System.out.println("dayOfYear = "+date.getDayOfYear());
System.out.println("support HOURS = "+date.isSupported(ChronoUnit.HOURS));
System.out.println("support HOUR_Of_DAY = "
+date.isSupported(ChronoField.HOUR_OF_DAY));
System.out.println("support INSTANT_SECONDS =
"+date.isSupported(ChronoField.INSTANT_SECONDS));
System.out.println("hour = "+date.getHour());
System.out.println("2 semaines avant = "+date.minusWeeks(2));
System.out.println("1 heure avant = "+date.minusHours(1));
}
}
Résultat : |
dateTime = 2015-02-20T21:07:48.406
dateTime = 2015-02-20T12:07:48.437
dateTime = 2014-12-25T23:59:59
date = 2014-12-25
time = 23:59:59
Calendrier = ISO
dayOfMonth = 25
dayOfWeek = THURSDAY
dayOfYear = 359
support HOURS = true
support HOUR_Of_DAY = true
support INSTANT_SECONDS = false
hour = 23
2 semaines avant = 2014-12-11T23:59:59
1 heure avant = 2014-12-25T22:59:59
Il ne faut pas utiliser d'opérations sur une instance de type LocalDateTime requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe LocalTime encapsule une heure (heure, minute, seconde, nanoseconde) sans fuseau horaire. Cette classe est immuable et thread-safe.
Elle définit plusieurs constantes :
Valeur |
Rôle |
static LocalTime MAX |
la plus grande valeur encapsulée, '23:59:59.999999999' |
static LocalTime MIDNIGHT |
Minuit au début de la journée, '00:00' |
static LocalTime MIN |
la plus petite valeur encapsulée, '00:00' |
static LocalTime NOON |
Midi au milieu de la journée, '12:00' |
Elle possède de nombreuses méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans lequel les champs sont remplacés par ceux encapsulés |
LocalTime atDate(LocalDate date) |
Obtenir une instance qui combine l'instance courante et la date fournie en paramètre |
OffsetTime atOffset(ZoneOffset offset) |
Obtenir une instance qui combine l'instance courante avec le décalage horaire passé en paramètre |
int compareTo(LocalTime other) |
Comparer l'instance avec l'instance passée en paramètre |
boolean equals(Object obj) |
Vérifier l'égalité de l'instance avec l'objet passé en paramètre |
String format(DateTimeFormatter formatter) |
Formater l'instance pour obtenir une représentation textuelle dans le format précisé par l'objet passé en paramètre |
static LocalTime from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet temporel fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier |
int getDayOfMonth() |
Obtenir la valeur du champ jour du mois |
long getLong(TemporalField field) |
Obtenir la valeur du champ fourni en paramètre sous la forme d'un entier long |
int getMinute() |
Obtenir la valeur du champ minutes |
int getNano() |
Obtenir la valeur du champ nanosecondes |
int getSecond() |
Obtenir la valeur du champ secondes |
int getYear() |
Obtenir la valeur du champ année |
boolean isAfter(LocalTime other) |
Vérifier si l'instance courante est postérieure à l'objet temporel fourni en paramètre |
boolean isBefore(LocalTime other) |
Vérifier si l'instance courante est antérieure à l'objet temporel fourni en paramètre |
boolean isSupported(TemporalField field) |
Vérifier si le champ passé en paramètre est supporté |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité temporel fournie en paramètre est supportée |
LocalTime minus(long amountToSubtract, TemporalUnit unit) |
Renvoyer une copie pour laquelle la quantité temporelle passée en paramètre est soustraite |
LocalTime minusHours(long hours) |
Renvoyer une copie pour laquelle le nombre d'heures passé en paramètre est soustrait |
LocalTime minusMinutes(long minutes) |
Renvoyer une copie pour laquelle le nombre de minutes passé en paramètre est soustrait |
LocalTime minusNanos(long nanos) |
Renvoyer une copie pour laquelle le nombre de nanosecondes passé en paramètre est soustrait |
LocalTime minusSeconds(long seconds) |
Renvoyer une copie pour laquelle le nombre de secondes passé en paramètre est soustrait |
static LocalTime now() |
Obtenir une instance à partir de la date/heure système et du fuseau horaire par défaut |
static LocalTime now(Clock clock) |
Obtenir une instance à partir de la date/heure donnée par l'objet en paramètre |
static LocalTime now(ZoneId zone) |
Obtenir une instance à partir de la date/heure système et du fuseau horaire passé en paramètre |
static LocalTime of(int hour, int minute) |
Obtenir une instance à partir des différentes valeurs des champs passées en paramètres |
static LocalTime ofNanoOfDay(long nanoOfDay) |
Obtenir une instance à partir des nanosecondes de la journée passées en paramètre |
static LocalTime ofSecondOfDay(long secondOfDay) |
Obtenir une instance à partir des secondes de la journée passées en paramètre |
static LocalTime parse(CharSequence text) |
Obtenir une instance à partir de l'analyse de la chaîne de caractères passée en paramètres avec le Formatter par défaut. Exemple 14:39:59 |
static LocalTime parse(CharSequence text, DateTimeFormatter formatter) |
Obtenir une instance à partir de l'analyse de la chaîne de caractères passée en paramètres avec le Formatter fourni en paramètre |
LocalTime plus(long amountToAdd, TemporalUnit unit) |
Renvoyer une copie à laquelle la quantité temporelle passée en paramètre est ajoutée |
LocalTime plusHours(long hours) |
Renvoyer une copie à laquelle le nombre d'heures passé en paramètre est ajouté |
LocalTime plusMinutes(long minutes) |
Renvoyer une copie à laquelle le nombre de minutes passé en paramètre est ajouté |
LocalTime plusNanos(long nanos) |
Renvoyer une copie à laquelle le nombre de nanosecondes passé en paramètre est ajouté |
LocalTime plusSeconds(long seconds) |
Renvoyer une copie à laquelle le nombre de secondes passé en paramètre est ajouté |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance courante |
ValueRange range(TemporalField field) |
Obtenir la plage des valeurs acceptables pour le champ fourni en paramètre |
long toNanoOfDay() |
Obtenir le nombre de nanosecondes de la journée |
int toSecondOfDay() |
Obtenir le nombre de secondes de la journée |
String toString() |
Obtenir une représentation textuelle de l'instance |
LocalTime truncatedTo(TemporalUnit unit) |
Renvoyer une copie de l'instance dont les valeurs encapsulées sont tronquées à l'unité précisée en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps entre l'instance et l'objet temporel fourni en paramètre. Le résultat est obtenu dans l'unité précisé en paramètre |
LocalTime with(TemporalAdjuster adjuster) |
Renvoyer une copie dont la valeur est ajustée grâce à l'objet qui encapsule les traitements fourni en paramètre |
LocalTime with(TemporalField field, long newValue) |
Renvoyer une copie de l'instance dont la valeur du champ précisé en paramètre est celle fournie |
LocalTime withHour(int hour) |
Renvoyer une copie de l'instance dont l'heure est celle fournie en paramètre |
LocalTime withMinute(int minute) |
Renvoyer une copie de l'instance dont les minutes sont celles fournies en paramètre |
LocalTime withNano(int nanoOfSecond) |
Renvoyer une copie de l'instance dont les nanosecondes sont celles fournies en paramètre |
LocalTime withSecond(int second) |
Renvoyer une copie de l'instance dont les secondes sont celles fournies en paramètre |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
public class TestLocalTime {
public static void main(String[] args) {
LocalTime time = LocalTime.now();
System.out.println("time = "+time);
time = LocalTime.of(23,59,59,104534);
System.out.println("time = "+time);
System.out.println("support HOURS = " +time.isSupported(ChronoUnit.HOURS));
System.out.println("support HOUR_Of_DAY = "
+time.isSupported(ChronoField.HOUR_OF_DAY));
System.out.println("supportINSTANT_SECONDS = "
+time.isSupported(ChronoField.INSTANT_SECONDS));
System.out.println("hour = "+time.getHour());
System.out.println("1 heure avant = " +time.minusHours(1));
}
}
Résultat : |
time = 06:53:21.625
time = 23:59:59.000104534
support HOURS = true
support HOUR_Of_DAY = true
support INSTANT_SECONDS = false
hour = 23
1 heure avant = 22:59:59.000104534
Il ne faut pas utiliser d'opérations sur une instance de type LocalTime requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe Period encapsule une période dans le temps : c'est une quantité de temps exprimée sous la forme des différents champs permettant de représenter une période de manière humaine, telle qu'un an, cinq mois et huit jours.
Les unités temporelles supportées sont année, mois et jour : chaque quantité encapsulée est représentée avec une quantité dans ces trois unités, leur valeur pouvant être égale à zéro. Ces valeurs peuvent aussi être négatives. Plusieurs méthodes permettent d'obtenir la quantité dans les différentes unités de temps requises pour représenter cette quantité.
Elle possède de nombreuses méthodes pour obtenir une instance ou renvoyer une copie modifiée :
Méthode |
Rôle |
Temporal addTo(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre auquel la période a été ajoutée |
static Period between(LocalDate startDateInclusive, LocalDate endDateExclusive) |
Calculer la période entre les deux dates fournies en paramètres |
boolean equals(Object obj) |
Vérifier si l'instance courante est égale à l'objet fourni en paramètre |
static Period from(TemporalAmount amount) |
Obtenir une instance qui encapsule la période correspondant à la quantité temporelle fournie en paramètre |
long get(TemporalUnit unit) |
Obtenir la durée de la période dans l'unité fournie en paramètre |
IsoChronology getChronology() |
Obtenir le calendrier de l'instance (ISO) |
int getDays() |
Obtenir le nombre de jours de l'instance |
int getMonths() |
Obtenir le nombre de mois de l'instance |
List<TemporalUnit> getUnits() |
Obtenir la liste des unités supportées |
int getYears() |
Obtenir le nombre d'années de l'instance |
boolean isNegative() |
Vérifier si la valeur d'au moins un des champs est négative |
boolean isZero() |
Vérifier que la valeur de tous les champs est zéro |
Period minus(TemporalAmount amountToSubtract) |
Retourner une copie de l'instance pour laquelle la quantité temporelle fournie en paramètre est soustraite |
Period minusDays(long daysToSubtract) |
Retourner une copie de l'instance pour laquelle le nombre de jours fourni en paramètre est soustrait |
Period minusMonths(long monthsToSubtract) |
Retourner une copie de l'instance pour laquelle le nombre de mois fourni en paramètre est soustrait |
Period minusYears(long yearsToSubtract) |
Retourner une copie de l'instance pour laquelle le nombre d'année fourni en paramètre est soustrait |
Period multipliedBy(int scalar) |
Retourner une copie de l'instance pour laquelle la valeur de tous les champs est multipliée par la valeur fournie en paramètre |
Period negated() |
Renvoyer une nouvelle instance dans laquelle toutes les valeurs des champs sont négatives |
Period normalized() |
Retourner une copie de la période avec les années et les mois normalisés |
static Period of(int years, int months, int days) |
Obtenir une instance encapsulant le nombre d'années, mois et jours fournis en paramètres |
static Period ofDays(int days) |
Obtenir une instance encapsulant le nombre de jours fourni en paramètre |
static Period ofMonths(int months) |
Obtenir une instance encapsulant le nombre de mois fourni en paramètre |
static Period ofWeeks(int weeks) |
Obtenir une instance encapsulant le nombre de semaines fourni en paramètre |
static Period ofYears(int years) |
Obtenir une instance encapsulant le nombre d'années fourni en paramètre |
static Period parse(CharSequence text) |
Renvoyer une instance issue de l'analyse de la chaîne de caractères fournie en paramètre, exemple P1Y5M8D |
Period plus(TemporalAmount amountToAdd) |
Retourner une copie à laquelle la quantité temporelle fournie en paramètre est ajoutée |
Period plusDays(long daysToAdd) |
Retourner une copie à laquelle le nombre de jours fourni en paramètre est ajouté |
Period plusMonths(long monthsToAdd) |
Retourner une copie à laquelle le nombre de mois fourni en paramètre est ajouté |
Period plusYears(long yearsToAdd) |
Retourner une copie à laquelle le nombre d'années fourni en paramètre est ajouté |
Temporal subtractFrom(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre pour laquelle la période est soustraite |
String toString() |
Obtenir une représentation textuelle de la période, exemple P1Y5M8D |
long toTotalMonths() |
Obtenir le nombre total de mois de la période |
Period withDays(int days) |
Renvoyer une copie de la période ajustée avec le nombre de jours fourni en paramètre |
Period withMonths(int months) |
Renvoyer une copie de la période ajustée avec le nombre de mois fourni en paramètre |
Period withYears(int years) |
Renvoyer une copie de la période ajustée avec le nombre d'années fourni en paramètre |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Period;
import java.time.temporal.ChronoUnit;
public class TestPeriod {
public static void main(String[] args) {
Period period = Period.of(1, 2, 3);
System.out.println("period=" + period);
System.out.println("annees=" + period.getYears());
System.out.println("mois =" + period.getMonths());
System.out.println("jours =" + period.getDays());
System.out.println("annees=" + period.get(ChronoUnit.YEARS));
System.out.println("mois =" + period.get(ChronoUnit.MONTHS));
System.out.println("jours =" + period.get(ChronoUnit.DAYS));
System.out.println("-1 jour=" + period.minusDays(1));
System.out.println("nb mois="+period.toTotalMonths());
LocalDate debut = LocalDate.of(2015, 1, 1);
LocalDate fin = LocalDate.of(2016, 3, 4);
System.out.println("ecart deb fin ="+Period.between(debut, fin));
System.out.println("ecart fin deb ="+Period.between(fin, debut));
}
}
Résultat : |
period=P1Y2M3D
annees=1
mois =2
jours =3
annees=1
mois =2
jours =3
-1 jour=P1Y2M2D
nb mois=14
ecart deb fin =P1Y2M3D
ecart fin deb =P-1Y-2M-3D
La période encapsulée représente toujours la somme des valeurs des trois champs.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Month;
import java.time.Period;
public class TestPeriod {
public static void main(String[] args) {
LocalDate dateNaissance = LocalDate.of(1972, Month.JUNE, 5);
final LocalDate prochainAnniversaire = prochainAnniversaire(dateNaissance);
System.out.println("prochain anniversaire="+prochainAnniversaire);
Period period = Period.between(LocalDate.now(), prochainAnniversaire);
System.out.println("aura lieu dans "+period.getYears()
+" ans, "+ period.getMonths()+" mois et "
+period.getDays()+" jours");
}
public static LocalDate prochainAnniversaire(LocalDate dateNaissance) {
LocalDate aujourdhui = LocalDate.now();
LocalDate resultat = dateNaissance.withYear(aujourdhui.getYear());
if (resultat.isBefore(aujourdhui) || resultat.isEqual(aujourdhui)) {
resultat = resultat.plusYears(1);
}
return resultat;
}
}
Résultat : |
prochain anniversaire=2015-06-05
aura lieu dans 0 ans, 3 mois et 2 jours
Remarque : pour obtenir une période sous la forme d'une seule unité temporelle, il faut utiliser la méthode between() de la classe ChronoUnit.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
public class TestChronoUnit {
public static void main(String[] args) {
LocalDate debut = LocalDate.of(2015, 1, 1);
LocalDate fin = LocalDate.of(2015, 1, 2);
long nbJour = ChronoUnit.DAYS.between(debut, fin);
System.out.println("nbjour="+nbJour);
}
}
Résultat : |
nbjour=1
Remarque : la classe Duration encapsule aussi une quantité de temps mais sous une forme machine. Les classes Period et Duration gère le décalage horaire différemment lorsqu'elles sont ajoutées à un objet de type ZonedDateTime : Duration ajoute un nombre déterminé de secondes alors que Period tente d'être le plus proche possible de l'heure.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Period;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class TestPeriod {
public static void main(String[] args) {
Duration unJourD = Duration.ofDays(1);
Period unJourP = Period.ofDays(1);
ZonedDateTime dateTime = ZonedDateTime.of(
LocalDateTime.of(2015, 3, 28, 17, 00), ZoneId.systemDefault());
System.out.println("datetime ="+dateTime);
System.out.println("+ periode 1 jour ="+dateTime.plus(unJourP));
System.out.println("+ duree 1 jour ="+dateTime.plus(unJourD));
}
}
Résultat : |
datetime =2015-03-28T17:00+01:00[Europe/Paris]
+ periode 1 jour =2015-03-29T17:00+02:00[Europe/Paris]
+ duree 1 jour =2015-03-29T18:00+02:00[Europe/Paris]
Period est une classe de type value-based : il ne faut pas utiliser d'opérations sur une instance de type Period requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La gestion des fuseaux et des décalages horaires rend beaucoup plus complexe la gestion des objets temporaux.
La surface de la Terre est découpée virtuellement en portions appelées fuseaux horaires : dans chacune de ces portions des règles s'appliquent et l'heure communément utilisée est la même. Il existe une quarantaine de fuseaux horaires.
Chaque fuseau horaire possède :
L'API Date-time propose deux classes pour encapsuler un fuseau horaire ou un décalage horaire :
Avant Java 8, la classe java.util.TimeZone encapsulait un fuseau horaire. C'est toujours le cas mais l'API Date-Time ne l'utilise pas et propose la nouvelle classe ZoneId car TimeZone est mutable.
L'API Date-Time propose plusieurs classes temporelles qui prennent en charge un fuseau horaire :
Le plus simple est d'utiliser une ZonedDateTime qui gère le décalage horaire sur la base des règles encapsulées dans le ZoneId prenant par exemple en charge l'heure d'été/hiver. Les classes OffsetDatetime et OffsetTime gère un décalage fixe. Généralement les formats d'échanges tels que XML ou de stockage comme une base de données utilisent une OffsetDateTime ou OffsetTime.
La classe abstraite ZoneId encapsule un fuseau horaire identifié par son id unique : exemple « Europe/Paris»
Elle contient les règles pour transformer un Instant en LocalDateTime.
Il existe deux types de fuseaux horaires chacun encapsulé dans une classe fille :
La classe ZoneRules contient pour chaque id les règles à appliquer : celles-ci changent fréquemment car elles sont définies par chaque pays.
Il existe trois types d'id :
Plusieurs organismes collectent et diffusent régulièrement les changements sur les fuseaux horaires : IANA Time Zone Database (TZDB), IATA, ... Chaque organisme utilise son propre format : c'est celui du TZDB qui est prioritairement utilisé.
La classe ZoneId contient plusieurs méthodes :
Méthode |
Rôle |
boolean equals(Object obj) |
Vérifier l'égalité avec l'objet fourni en paramètre |
static ZoneId from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet temporel passé en paramètre |
static Set<String> getAvailableZoneIds() |
Obtenir la liste des identifiants de zones utilisables |
String getDisplayName(TextStyle style, Locale locale) |
Obtenir une représentation textuelle de la zone sous la forme de son nom ou d'un décalage horaire |
abstract String getId() |
Obtenir l'identifiant unique de la zone |
abstract ZoneRules getRules() |
Obtenir les règles de calculs à utiliser pour la zone |
ZoneId normalized() |
Normaliser l'instance en tentant de renvoyer si possible une instance type ZoneOffset |
static ZoneId of(String zoneId) |
Obtenir une instance à partir de son identifiant |
static ZoneId of(String zoneId, Map<String, String> aliasMap) |
Obtenir une instance à partir de son identifiant en utilisant la collections d'alias en remplacement des ID standard |
static ZoneId ofOffset(String prefix, ZoneOffset offset) |
Obtenir une instance qui encapsule la ZoneOffset passée en paramètre |
static ZoneId systemDefault() |
Obtenir la ZoneId du système |
La classe ZoneId peut être sérialisée : dans ce cas c'est l'ID qui est stocké. Il est possible que l'ID n'existe pas dans la JVM où l'objet est dé-sérialisé. Dans ce cas, l'invocation de la méthode getRules() lève une exception de type ZoneRulesException.
ZoneId est une classe de type value-based : il ne faut pas utiliser d'opérations sur une instance de type Period requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe ZoneOffset encapsule un décalage horaire.
Elle possède plusieurs méthodes :
Constante |
Rôle |
static ZoneOffset MAX |
Le décalage maximum : +18:00 |
static ZoneOffset MIN |
Le décalage minimum : -18:00 |
static ZoneOffset UTC |
Le décalage pour UTC : 00:00 avec l'ID «Z» |
Elle possède plusieurs méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre ajustée avec l'instance de type ZoneOffset |
int compareTo(ZoneOffset other) |
Comparer l'instance avec celle fournie en paramètre |
boolean equals(Object obj) |
Vérifier l'égalité avec l'objet fourni en paramètre |
static ZoneOffset from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet temporel fourni en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier |
String getId() |
Obtenir l'ID de l'instance |
long getLong(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier |
ZoneRules getRules() |
Obtenir les règles de calculs encapsulés dans une instance de type ZoneRules |
int getTotalSeconds() |
Obtenir le nombre de secondes correspondant au décalage |
boolean isSupported(TemporalField field) |
Vérifier si le champ passé en paramètre est supporté |
static ZoneOffset of(String offsetId) |
Obtenir une instance à partir de son ID |
static ZoneOffset ofHours(int hours) |
Obtenir une instance à partir du nombre d'heures fourni en paramètre |
static ZoneOffset ofHoursMinutes(int hours, int minutes) |
Obtenir une instance à partir du nombre d'heures et de minutes fournis en paramètres |
static ZoneOffset ofHoursMinutesSeconds(int hours, int minutes, int seconds) |
Obtenir une instance à partir du nombre d'heures, de minutes et de secondes fournis en paramètres |
static ZoneOffset ofTotalSeconds(int totalSeconds) |
Obtenir une instance à partir du nombre de secondes fourni en paramètre |
<R> R query(TemporalQuery<R> query) |
Exécuter la requête passée en paramètre sur l'instance |
ValueRange range(TemporalField field) |
Obtenir la plage de valeurs utilisables pour le champ passé en paramètre |
Le décalage horaire correspond à la différence de temps entre le fuseau horaire et celui de Greenwich : c'est généralement une quantité temporelle exprimée sous la forme d'heures et de minutes. Par exemple : +01:00 pour Paris en hiver et +02:00 en été.
Le fuseau horaire encapsulé dans une instance de type ZoneId possède une ou plusieurs instances de type ZoneOffset. Pour la ZoneId « Europe/Paris », il y a deux ZoneOffset : une pour l'heure d'été et une pour l'heure d'hiver.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.zone.ZoneRules;
public class TestZoneOffset {
public static void main(String[] args) {
ZoneId zoneId = ZoneId.of("Europe/Paris");
ZoneRules rules = zoneId.getRules();
System.out.println(rules.getOffset(LocalDateTime.of(2015,
Month.FEBRUARY, 10, 0, 0)));
System.out.println(rules.getOffset(LocalDateTime.of(2015,
Month.JUNE,10, 0, 0)));
}
}
Résultat : |
+01:00
+02:00
Cette classe est conçue pour être utilisée avec le calendrier ISO.
ZoneOffset est une classe de type value-based : il ne faut pas utiliser d'opérations sur une instance de type Period requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe ZonedDateTime encapsule une date, une heure et un fuseau horaire : elle peut être vue comme une combinaison d'une LocalDateTime et d'une ZoneId. Il est nécessaire d'utiliser une instance de type ZonedDateTime pour fournir une donnée temporelle qui soit indépendante de la machine sur laquelle elle est créée.
Elle permet la conversion d'une LocalDateTime en une Instant. Cette conversion requiert le calcul du décalage horaire à appliquer en fonction des règles encapsulées dans la propriété rules de l'instance de type ZoneId.
Le décalage peut être simple en correspondant directement au décalage entre le fuseau horaire et celui de Greenwich. Ce décalage peut aussi être dans certains cas plus complexe notamment lors du passage :
Elle possède de nombreuses méthodes :
Méthode |
Rôle |
String format(DateTimeFormatter formatter) |
Formater l'instance avec le Formatter passé en paramètre |
static ZonedDateTime from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet temporel passé en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier |
int getDayOfMonth() |
Obtenir le champ jour du mois |
int getDayOfYear() |
Obtenir le champ jour de l'année |
int getHour() |
Obtenir le champ heure |
long getLong(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier long |
int getMinute() |
Obtenir les minutes |
Month getMonth() |
Obtenir le mois de l'année |
int getMonthValue() |
Obtenir le mois de l'année (1 à 12) |
int getNano() |
Obtenir les nanosecondes |
ZoneOffset getOffset() |
Obtenir le décalage horaire. Exemple : «+01:00» |
int getSecond() |
Obtenir les secondes |
int getYear() |
Obtenir l'année |
ZoneId getZone() |
Obtenir le fuseau horaire. Exemple « Europe/Paris» |
boolean isSupported(TemporalField field) |
Vérifier si le champ passé en paramètre est supporté |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité passée en paramètre est supportée |
ZonedDateTime minus(long amountToSubtract, TemporalUnit unit) |
Renvoyer une copie de l'instance pour laquelle la quantité temporelle a été soustraite |
ZonedDateTime minusDays(long days) |
Renvoyer une copie de l'instance pour laquelle le nombre de jours a été soustrait |
ZonedDateTime minusHours(long hours) |
Renvoyer une copie de l'instance pour laquelle le nombre d'heures a été soustrait |
ZonedDateTime minusMinutes(long minutes) |
Renvoyer une copie de l'instance pour laquelle le nombre de minutes a été soustrait |
ZonedDateTime minusMonths(long months) |
Renvoyer une copie de l'instance pour laquelle le nombre de mois a été soustrait |
ZonedDateTime minusNanos(long nanos) |
Renvoyer une copie de l'instance pour laquelle le nombre de nanosecondes a été soustrait |
ZonedDateTime minusSeconds(long seconds) |
Renvoyer une copie de l'instance pour laquelle le nombre de secondes a été soustrait |
ZonedDateTime minusWeeks(long weeks) |
Renvoyer une copie de l'instance pour laquelle le nombre de semaines a été soustrait |
ZonedDateTime minusYears(long years) |
Renvoyer une copie de l'instance pour laquelle le nombre d'années a été soustrait |
static ZonedDateTime now() |
Obtenir une instance à partir de l'heure système et du fuseau horaire par défaut |
static ZonedDateTime now(Clock clock) |
Obtenir une instance à partir de l'heure obtenue de l'objet passé en paramètre |
static ZonedDateTime now(ZoneId zone) |
Obtenir une instance à partir de l'heure système et fuseau horaire passé en paramètre |
static ZonedDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) |
Obtenir une instance à partir de différents champs et du fuseau horaire passés en paramètres |
static ZonedDateTime of(LocalDate date, LocalTime time, ZoneId zone) |
Obtenir une instance à partir de l'objet temporel et du fuseau horaire passés en paramètres |
static ZonedDateTime ofInstant(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) |
Obtenir une instance à partir de l'objet temporel, du décalage horaire et du fuseau horaire fournis en paramètres |
static ZonedDateTime ofLocal(LocalDateTime localDateTime, ZoneId zone, ZoneOffset preferredOffset) |
Obtenir une instance à partir de l'objet temporel et du fuseau horaire fournis en paramètres en utilisant le décalage horaire précisé si possible |
static ZonedDateTime ofStrict(LocalDateTime localDateTime, ZoneOffset offset, ZoneId zone) |
Obtenir une instance respectant strictement la combinaison de paramètres fournie |
static ZonedDateTime parse(CharSequence text) |
Analyser le texte fourni en paramètre avec le Formatter par défaut pour obtenir une instance. Exemple «2014-12-25T23:59:59+01:00[Europe/Paris]» |
static ZonedDateTime parse(CharSequence text, DateTimeFormatter formatter) |
Analyser le texte avec le Formatter fournis en paramètre pour obtenir une instance |
ZonedDateTime plus(long amountToAdd, TemporalUnit unit) |
Obtenir une copie à laquelle la quantité temporelle a été ajoutée |
ZonedDateTime plusDays(long days) |
Obtenir une copie à laquelle le nombre de jours a été ajouté |
ZonedDateTime plusHours(long hours) |
Obtenir une copie à laquelle le nombre d'heures a été ajouté |
ZonedDateTime plusMinutes(long minutes) |
Obtenir une copie à laquelle le nombre de minutes a été ajouté |
ZonedDateTime plusMonths(long months) |
Obtenir une copie à laquelle le nombre de mois a été ajouté |
ZonedDateTime plusNanos(long nanos) |
Obtenir une copie à laquelle le nombre de nanosecondes a été ajouté |
ZonedDateTime plusSeconds(long seconds) |
Obtenir une copie à laquelle le nombre de secondes a été ajouté |
ZonedDateTime plusWeeks(long weeks) |
Obtenir une copie à laquelle le nombre de semaines a été ajouté |
ZonedDateTime plusYears(long years) |
Obtenir une copie à laquelle le nombre d'années a été ajouté |
<R> R query(TemporalQuery<R> query) |
Renvoyer le résultat de l'exécution de la requête fournie en paramètre sur l'instance |
ValueRange range(TemporalField field) |
Obtenir la plage de valeurs valides pour le champ fourni en paramètre |
LocalDate toLocalDate() |
Obtenir une instance de type LocalDate à partir des champs encapsulés |
LocalDateTime toLocalDateTime() |
Obtenir une instance de type LocalDateTime à partir des champs encapsulés |
LocalTime toLocalTime() |
Obtenir une instance de type LocalTime à partir des champs encapsulés |
OffsetDateTime toOffsetDateTime() |
Obtenir une instance de type OffsetDateTime à partir des champs encapsulés |
ZonedDateTime truncatedTo(TemporalUnit unit) |
Obtenir une copie de l'instance tronquée à l'unité fournie en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps exprimée dans l'unité fournie entre l'instance et l'objet temporel fourni en paramètres |
ZonedDateTime with(TemporalAdjuster adjuster) |
Renvoyer une copie de l'instance ajustée grâce à l'objet fourni en paramètre |
ZonedDateTime with(TemporalField field, long newValue) |
Renvoyer une copie de l'instance dans la valeur du champ fournie en paramètre est remplacée |
ZonedDateTime withDayOfMonth(int dayOfMonth) |
Renvoyer une copie de l'instance dont la valeur du champ jour du mois est remplacée avec celle fournie |
ZonedDateTime withDayOfYear(int dayOfYear) |
Renvoyer une copie de l'instance dont la valeur du champ jour de l'année est remplacée avec celle fournie |
ZonedDateTime withEarlierOffsetAtOverlap() |
|
ZonedDateTime withFixedOffsetZone() |
Retourne une copie de l'instance pour laquelle zone ID a la valeur de l'offset |
ZonedDateTime withHour(int hour) |
Renvoyer une copie de l'instance dont la valeur du champ heure est remplacée avec celle fournie |
ZonedDateTime withLaterOffsetAtOverlap() |
|
ZonedDateTime withMinute(int minute) |
Renvoyer une copie de l'instance dont la valeur du champ minute est remplacée avec celle fournie |
ZonedDateTime withMonth(int month) |
Renvoyer une copie de l'instance dont la valeur du champ mois est remplacée avec celle fournie |
ZonedDateTime withNano(int nanoOfSecond) |
Renvoyer une copie de l'instance dont la valeur du champ nanosecondes est remplacée avec celle fournie |
ZonedDateTime withSecond(int second) |
Renvoyer une copie de l'instance dont la valeur du champ seconde est remplacée avec celle fournie |
ZonedDateTime withYear(int year) |
Renvoyer une copie de l'instance dont la valeur du champ années est remplacée avec celle fournie |
ZonedDateTime withZoneSameInstant(ZoneId zone) |
Renvoyer une copie de l'instance représentant le même instant mais dans le fuseau horaire fourni en paramètre |
ZonedDateTime withZoneSameLocal(ZoneId zone) |
Renvoyer une copie de l'instance utilisant le fuseau horaire fourni en paramètre en tentant de conserver la même date/heure locale |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.zone.ZoneRules;
import java.util.Set;
public class TestZonedDateTime {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2014,12,25,0,0,0);
ZoneId zoneId = ZoneId.of("Europe/Paris");
ZonedDateTime zoneDateTime = ZonedDateTime.of(dateTime, zoneId);
System.out.println("zoneDateTime="+zoneDateTime);
System.out.println(zoneId.getRules().isDaylightSavings(zoneDateTime.toInstant()));
zoneId = ZoneId.of("America/Los_Angeles");
zoneDateTime = ZonedDateTime.of(dateTime, zoneId);
System.out.println("zoneDateTime L.A. ="+zoneDateTime);
System.out.println("zoneDateTime Paris="
+zoneDateTime.withZoneSameInstant(ZoneId.of("Europe/Paris")));
// Calcul heure locale d'arrivee apres un vol de 12h30
LocalDateTime dateTimeDepart = LocalDateTime.of(2015,3,11,8,0,0);
System.out.println("depart Paris "+dateTimeDepart+" heure locale");
ZoneId zoneIdDepart = ZoneId.of("Europe/Paris");
ZonedDateTime zoneDateTimeDepart = ZonedDateTime.of(dateTimeDepart, zoneIdDepart);
ZoneId zoneIdArrivee = ZoneId.of("America/Los_Angeles");
ZonedDateTime zoneDateT imeArrivee = zoneDateTimeDepart
.withZoneSameInstant(zoneIdArrivee).plusHours(12).plusMinutes(30);
LocalDateTime dateTimeArrivee = zoneDateTimeArrivee.toLocalDateTime();
System.out.println("Arrivee L.A. "+dateTimeArrivee+" heure locale");
}
}
Résultat : |
zoneDateTime=2014-12-25T00:00+01:00[Europe/Paris]
false
zoneDateTime L.A. =2014-12-25T00:00-08:00[America/Los_Angeles]
zoneDateTime Paris=2014-12-25T09:00+01:00[Europe/Paris]
depart Paris 2015-03-11T08:00
heure locale
Arrivee L.A. 2015-03-11T12:30 heure locale
ZonedDateTime est une classe de type value-based : il ne faut pas utiliser d'opérations sur une instance de type ZonedDateTime requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe OffsetDateTime encapsule de manière immuable une date, une heure et un décalage horaire à partir de l'heure UTC (méridien de Greenwich) : elle combine une LocalDateTime et une ZoneOffset telles que 2014-12-25T00:00:00+01:00.
Cette classes est utile pour sérialiser une donnée temporelle en dehors de la JVM tel que le stockage dans une base de données, l'échange entre plusieurs serveurs, ...
Les classes ZonedDateTime et OffsetDateTime sont très proches. La différence majeure est que la classe OffsetDateTime applique simplement le décalage : elle ne tient, par exemple, pas compte de l'heure d'été/hiver.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
public class TestOffsetDateTime {
public static void main(String[] args) {
LocalDateTime localDateTime = LocalDateTime.of(2014,Month.DECEMBER, 25, 4,00,0);
final ZoneOffset zoneOffset = ZoneOffset.of("+05:00");
OffsetDateTime offsetDateTime = OffsetDateTime.of(localDateTime, zoneOffset);
System.out.println(offsetDateTime);
offsetDateTime = OffsetDateTime.parse("2014-12-25T04:00+05:00");
offsetDateTime = offsetDateTime.withOffsetSameInstant(ZoneOffset.of("+01:00"));
System.out.println(offsetDateTime);
System.out.println(offsetDateTime.toLocalDateTime());
}
}
Résultat : |
2014-12-25T04:00+05:00
2014-12-25T00:00+01:00
2014-12-25T00:00
OffsetDateTime est une classe de type value-based : il ne faut pas utiliser d'opérations sur une instance de type OffsetDateTime requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
La classe OffsetTime encapsule une heure et un décalage horaire par rapport à l'heure UTC dans le calendrier ISO : elle combine une LocalTime et une ZoneOffset.
La précision de l'heure encapsulée est la nanoseconde. Elle est immuable et thread-safe.
Elle possède de nombreuses méthodes :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie de l'objet temporel fourni en paramètre dans lequel la valeur du champ encapsulé est remplacée par celle passée en paramètre |
OffsetDateTime atDate(LocalDate date) |
Combiner l'instance courante avec l'objet temporal fourni en paramètre pour obtenir une instance de type OffsetDateTime |
int compareTo(OffsetTime other) |
Comparer l'instance courante avec celle fournie en paramètre |
String format(DateTimeFormatter formatter) |
Formater l'instance avec le formateur passé en paramètre |
static OffsetTime from(TemporalAccessor temporal) |
Obtenir une instance à partir de l'objet temporel passé en paramètre |
int get(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier |
int getHour() |
Obtenir le champ heure |
long getLong(TemporalField field) |
Obtenir la valeur du champ passé en paramètre sous la forme d'un entier long |
int getMinute() |
Obtenir les minutes |
int getNano() |
Obtenir les nanosecondes |
ZoneOffset getOffset() |
Obtenir le décalage horaire. Exemple : «+01:00» |
int getSecond() |
Obtenir les secondes |
boolean isAfter(OffsetTime other) |
Vérifier si l'instance courante est postérieure à celle fournie en paramètre |
boolean isBefore(OffsetTime other) |
Vérifier si l'instance courante est antéreure à celle fournie en paramètre |
boolean isEqual(OffsetTime other) |
Vérifier si l'instance est égale à celle fournie en paramètre |
boolean isSupported(TemporalField field) |
Vérifier si le champ passé en paramètre est supporté |
boolean isSupported(TemporalUnit unit) |
Vérifier si l'unité passée en paramètre est supportée |
OffsetTime minus(long amountToSubtract, TemporalUnit unit) OffsetTime minus(TemporalAmount amountToSubtract) |
Renvoyer une copie de l'instance à laquelle la quantité temporelle a été soustraite |
OffsetTime minusHours(long hours) |
Renvoyer une copie de l'instance pour laquelle le nombre d'heures a été soustrait |
OffsetTime minusMinutes(long minutes) |
Renvoyer une copie de l'instance pour laquelle le nombre de minutes a été soustrait |
OffsetTime minusNanos(long nanos) |
Renvoyer une copie de l'instance pour laquelle le nombre de nanosecondes a été soustrait |
OffsetTime minusSeconds(long seconds) |
Renvoyer une copie de l'instance pour laquelle le nombre de secondes a été soustrait |
static OffsetTime now() |
Obtenir une instance à partir de l'heure système et fuseau horaire par défaut |
static OffsetTime now(Clock clock) |
Obtenir une instance à partir de l'heure obtenue de l'objet passé en paramètre |
static OffsetTime now(ZoneId zone) |
Obtenir une instance à partir de l'heure système et du fuseau horaire passés en paramètre |
static OffsetTime of(int hour, int minute, int second, int nanoOfSecond, ZoneOffset offset) |
Obtenir une instance à partir de différents champs et du fuseau horaire passés en paramètres |
static OffsetTime of(LocalTime time, ZoneOffset offset) |
Obtenir une instance à partir de l'objet temporel et du décalage horaire fournis en paramètres |
static OffsetTime ofInstant(Instant instant, ZoneId zone) |
Obtenir une instance à partir de l'objet temporel, et du fuseau horaire fournis en paramètres |
static OffsetTime parse(CharSequence text) |
Analyser le texte fourni en paramètre avec le Formatter par défaut pour obtenir une instance. Exemple «23:59:59+01:00» |
static OffsetTime parse(CharSequence text, DateTimeFormatter formatter) |
Analyser le texte avec le Formatter fourni en paramètre pour obtenir une instance |
OffsetTime plus(long amountToAdd, TemporalUnit unit) OffsetTime plus(TemporalAmount amountToAdd) |
Obtenir une copie à laquelle la quantité temporelle a été ajoutée |
OffsetTime plusHours(long hours) |
Obtenir une copie à laquelle le nombre d'heures a été ajouté |
OffsetTime plusMinutes(long minutes) |
Obtenir une copie à laquelle le nombre de minutes a été ajouté |
OffsetTime plusNanos(long nanos) |
Obtenir une copie à laquelle le nombre de nanosecondes a été ajouté |
OffsetTime plusSeconds(long seconds) |
Obtenir une copie à laquelle le nombre de secondes a été ajouté |
<R> R query(TemporalQuery<R> query) |
Renvoyer le résultat de l'exécution de la requête fournie en paramètre sur l'instance |
ValueRange range(TemporalField field) |
Obtenir la plage de valeurs valides pour le champ fourni en paramètre |
LocalTime toLocalTime() |
Obtenir une instance de type LocalTime à partir des champs encapsulés |
OffsetTime truncatedTo(TemporalUnit unit) |
Obtenir une copie de l'instance tronquée à l'unité fournie en paramètre |
long until(Temporal endExclusive, TemporalUnit unit) |
Calculer la quantité de temps exprimée dans l'unité fournie entre l'instance et l'objet temporel fournis en paramètres |
OffsetTime with(TemporalAdjuster adjuster) |
Renvoyer une copie de l'instance ajustée grâce à l'objet fourni en paramètre |
OffsetTime with(TemporalField field, long newValue) |
Renvoyer une copie de l'instance dont la valeur du champ fournie en paramètre est remplacée |
OffsetTime withHour(int hour) |
Renvoyer une copie de l'instance dont la valeur du champ heure est remplacée par celle fournie |
OffsetTime withMinute(int minute) |
Renvoyer une copie de l'instance dont la valeur du champ minute est remplacée par celle fournie |
OffsetTime withNano(int nanoOfSecond) |
Renvoyer une copie de l'instance dont la valeur du champ nanosecondes est remplacée par celle fournie |
OffsetTime withOffsetSameInstant(ZoneOffset offset) |
Renvoyer une copie de l'instance représentant le même instant mais dans le décalage horaire fourni en paramètre |
OffsetTime withOffsetSameLocal(ZoneOffset offset) |
Renvoyer une copie de l'instance représentant la même heure locale avec le décalage horaire fourni en paramètre |
OffsetTime withSecond(int second) |
Renvoyer une copie de l'instance dont la valeur du champ seconde est remplacée par celle fournie |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.OffsetTime;
import java.time.ZoneOffset;
public class TestOffsetTime {
public static void main(String[] args) {
OffsetTime time = OffsetTime.now();
System.out.println(time);
// changement du décalage en conservant le même point dans le temps
ZoneOffset offset = ZoneOffset.ofHours(5);
OffsetTime sameTimeDifferentOffset = time.withOffsetSameInstant(
offset);
System.out.println(sameTimeDifferentOffset);
// changement du décalage en conservant la date/heure locale
OffsetTime changeTimeWithNewOffset = time.withOffsetSameLocal(
offset);
System.out.println(changeTimeWithNewOffset);
}
}
Résultat : |
13:38:53.187+01:00
17:38:53.187+05:00
13:38:53.187+05:00
OffsetTime est une classe de type value-based : il ne faut pas utiliser d'opérations sur une instance de type OffsetTime requérant l'identité de l'objet : opérateur ==, hashCode ou synchronisation. La comparaison de deux instances doit se faire en utilisant la méthode equals().
Les classes qui encapsulent une donnée temporelle proposent :
Ces opérations sont fréquemment utilisées pour sérialiser/désérialiser des données temporelles.
L'analyse d'une chaîne de caractères pour obtenir un objet temporel ou le formatage d'un objet temporel pour obtenir une représentation textuelle se font grâce à des objets de type DateTimeFormatter.
Ces opérations se font sur la base d'un motif exprimé sous la forme d'une chaîne de caractères qui permet de préciser le format à utiliser.
La classe propose des constantes de type DateTimeFormatter qui encapsulent des motifs standard pour faciliter leurs utilisations.
Nom |
Rôle |
Exemple |
BASIC_ISO_DATE |
Format ISO de base pour date sans décalage horaire |
"20141225" |
ISO_DATE |
Format ISO de date avec ou sans décalage horaire |
"2014-12-25", "2014-12-25+01:00" |
ISO_DATE_TIME |
Format ISO de date-heure avec ou sans fuseau horaire et décalage horaire |
"2014-12-25T00:00:00", "2014-12-25T10:00:00+01:00", "2014-12-25T00:00:00+01:00[Europe/Paris]" |
ISO_INSTANT |
Format ISO de date-heure en UTC |
"2014-12-25T00:00:00Z" |
ISO_LOCAL_DATE |
Format ISO de date sans fuseau horaire ni décalage horaire |
"2014-12-25" |
ISO_LOCAL_DATE_TIME |
Format ISO de date-heure sans fuseau horaire ni décalage horaire |
"2014-12-25T00:00:00" |
ISO_LOCAL_TIME |
Format ISO d'heure sans fuseau horaire ni décalage horaire |
"20:15", "20:15:30" |
ISO_OFFSET_DATE |
Format ISO de date avec décalage horaire |
"2014-12-25+01:00". |
ISO_OFFSET_DATE_TIME |
Format ISO de date-heure avec décalage horaire |
"2014-12-25T00:00:00+01:00" |
ISO_OFFSET_TIME |
Format ISO d'heure avec décalage horaire |
"10:15+01:00", "10:15:30+01:00" |
ISO_ORDINAL_DATE |
Format ISO de date exprimée en jour d'une année sans décalage horaire |
"2014-359", "2014-359+01:00" |
ISO_TIME |
Format ISO d'heure avec ou sans décalage horaire |
"10:15", "10:15:30", "10:15:30+01:00" |
ISO_WEEK_DATE |
Format ISO de date exprimée en semaine d'une année sans décalage horaire |
"2014-W52-4", "2014-W52-4+01:00" |
ISO_ZONED_DATE_TIME |
Format ISO avec fuseau horaire et décalage horaire |
"2014-12-25T00:00:00+01:00[Europe/Paris]" |
RFC_1123_DATE_TIME |
Format RFC-1123 / RFC 822 |
"Thu, 25 Dec 2014 00:00:00 +0100" |
La classe DateTimeFormatter permet d'exprimer son propre motif pour définir le format de la date-heure :
Nom |
Rôle |
Type de format |
Exemple |
G |
Ere |
Texte |
AD |
u |
Année |
Année (Numérique) |
2014,14 |
y |
Année de l'ère |
Année (Numérique) |
2014,14 |
D |
Jour de l'année |
Numérique |
352 |
M |
Mois de l'année |
Numérique |
7,07 |
L |
Mois de l'année |
Texte |
J, Jul, July |
d |
Jour du mois |
Numérique |
12 |
Q |
Trimestre de l'année |
Numérique |
3,03 |
q |
Trimestre de l'année |
Texte |
Q3, 3rd quarter |
Y |
Année |
Année (Numérique) |
|
w |
Semaine de l'année |
Numérique |
35 |
W |
Semaine du mois |
Numérique |
4 |
E |
Jour de la semaine |
Texte |
J, Jeu, Jeudi |
e |
Jour de la semaine selon la Locale |
Numérique |
2; 02 |
c |
Jour de la semaine selon la Locale |
Texte |
|
F |
Semaine du mois |
Numérique |
3 |
a |
Matin ou après midi |
Texte |
AM, PM |
h |
Heure de la demi-journée |
Numérique (1-12) |
1 10 |
K |
Heure de la demi-journée |
Numérique (0-11) |
0 10 |
k |
Heure de la journée |
Numérique (1-24) |
1 18 |
H |
Heure de la journée |
Numérique (0-23) |
0 18 |
m |
Minute de l'heure |
Numérique |
45 |
s |
Seconde de l'heure |
Numérique |
59 |
S |
Fraction de la seconde |
Fraction (Numérique) |
875 |
A |
Milliseconde de la journée |
Numérique |
|
n |
Nanoseconde de la seconde |
Numérique |
|
N |
Nanoseconde de la journée |
Numérique |
|
V |
Identifiant du fuseau horaire |
Zone-id (Texte) |
America/Los_Angeles |
z |
Nom du fuseau horaire |
Nom-Zone (Texte) |
Pacific Standard Time; PST |
O |
Décalage horaire |
Offset-O (Texte) |
GMT+8; GMT+08:00; UTC-08:00; |
X |
Décalage horaire |
Offset-X (Texte) |
Z; -08; -0830; -08:30; -083015; -08:30:15; |
x |
Décalage horaire |
Offset-x (Texte) |
+0000; -08; -0830; -08:30; -083015; -08:30:15; |
Z |
Décalage horaire |
Offset-Z (Texte) |
+0000; -0800; -08:00; |
p |
Padding |
||
' |
Délimiteur d'une chaîne de caractères |
Texte |
|
'' |
Simple quote (doublée) |
Texte |
|
[ |
Début d'une partie optionnelle |
||
] |
Fin d'une partie optionnelle |
||
# |
Réservé |
||
{ |
Réservé |
||
} |
Réservé |
Toutes les lettres minuscules et majuscules sont réservées pour exprimer des données dans le motif.
Le nombre d'occurrences d'une lettre permet de préciser le format utilisé selon le type de format :
Type de format |
Nb occurrences |
Format |
Texte |
Inférieure à 4 |
Short |
4 |
Full |
|
5 |
Narrow |
|
Numérique |
1 |
Minimum de chiffres sans padding Obligatoire pour c (jour de la semaine) et F (semaine du mois) |
Supérieur à 1 |
Le nombre d'occurrences précise la taille complétée avec des zéros au besoin Maximum 2 pour d, H, h, K, k, m et s Maximun 3 pour D |
|
Numérique/texte |
Supérieur ou égal à 3 |
Format texte |
Inférieur à 3 |
Format numérique |
|
Fraction |
9 |
Affichage intégral |
Inférieur à 9 |
Tronquée au nombre d'occurrences |
|
Year |
2 |
Deux chiffres complétés au besoin par un zéro |
Supérieur à 2 |
Format intégral |
|
Zone-Id |
2 |
Format intégral |
Différent de 2 |
IllegalArgumentException |
|
Nom-zone |
1, 2, 3 |
Nom court |
4 |
Nom long |
|
Supérieur à 4 |
IllegalArgumentException |
|
Offset-X et Offset-x (x affiche des zéros, X affiche Z pour un zéro) |
1 |
Uniquement les heures et éventuellement les minutes si elles ne sont pas égales à zéro. Exemple +01 |
2 |
Heures et minutes sans séparateur. Exemple +0145 |
|
3 |
Heures et minutes avec un caractère deux points comme séparateur. Exemple +01:45 |
|
4 |
Heures, minutes et secondes sans séparateur. Exemple +014530 |
|
5 |
Heures, minutes et secondes avec un caractère deux points comme séparateur. Exemple +01:45:30 |
|
Supérieur à 5 |
IllegalArgumentException |
|
Offset-O |
1 |
Format court avec les heures non précédées par un zéro. Les minutes et les secondes sont optionnelles. Exemple GMT+1 |
4 |
Format long avec les heures et les minutes précédées par un zéro au besoin. Les secondes sont optionnelles. Elles sont séparées par un caractère deux points. Exemple GMT+01:00 |
|
Autres valeurs |
IllegalArgumentException |
|
Offset-Z |
1, 2, 3 |
Uniquement les heures et les minutes. Exemple +0100, +0000 |
4 |
Format long avec les heures et les minutes précédées par un zéro au besoin. Les secondes sont optionnelles. Elles sont séparées par un caractère deux points. Exemple GMT+01:00, GMT+00:00 |
|
5 |
Idem mais Z si l'offset est zéro |
|
Supérieur à 5 |
IllegalArgumentException |
La lettre p permet de modifier le mode de padding par espaces de l'élément suivant. Le nombre d'occurrences de la lettre p permet de préciser la taille de l'élément.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class TestDateTimeFormatter {
public static void main(String[] args) {
ZonedDateTime date = ZonedDateTime.of(LocalDateTime.of(2014,
Month.DECEMBER, 25, 0,0,0), ZoneId.of("GMT"));
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("dd ppppppppppMMM yyyy xxx");
String dateTimeStr = date.format(formatter);
System.out.println(dateTimeStr);
}
}
Résultat : |
25 déc. 2014 +00:00
Toutes les lettres non reconnues lèvent une exception. Tous les caractères différents de '[', ']', '{', '}' et '#' sont utilisés tels quels. Il est cependant recommandé d'entourés ces caractères par des quotes simples pour garantir la compatibilité avec de futures versions.
En plus du motif du format à utiliser, il est possible de préciser plusieurs éléments à utiliser dans les traitements :
La classe DateTimeFormatter possède de nombreuses méthodes :
Méthode |
Rôle |
String format(TemporalAccessor temporal) |
Formater l'objet temporel fourni en paramètre |
void formatTo(TemporalAccessor temporal, Appendable appendable) |
Formater l'objet temporel fourni en paramètre et l'ajouter à l'Appendable |
Chronology getChronology() |
Obtenir le calendrier utilisé lors des traitements |
DecimalStyle getDecimalStyle() |
Obtenir l'objet de type DecimalStyle utilisé lors des traitements |
Locale getLocale() |
Obtenir la Locale utilisée lors des traitements |
Set<TemporalField> getResolverFields() |
Permet de définir les champs qui seront utilisés pour définir l'objet temporel lors de la phase d'analyse. Par défaut, une instance de type DateTimeFormatter ne possède aucun resolver |
ResolverStyle getResolverStyle() |
Obtenir le style de résolution utilisé par l'instance. Par défaut, une instance de type DataTimeFormatter possède le style SMART |
ZoneId getZone() |
Obtenir le fuseau horaire utilisé lors des traitements |
static DateTimeFormatter ofLocalizedDate(FormatStyle dateStyle) |
Obtenir une instance pour le calendrier ISO tenant compte de la Locale pour des dates |
static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateTimeStyle) static DateTimeFormatter ofLocalizedDateTime(FormatStyle dateStyle, FormatStyle timeStyle) |
Obtenir une instance pour le calendrier ISO tenant compte de la Locale pour des dates heures |
static DateTimeFormatter ofLocalizedTime(FormatStyle timeStyle) |
Obtenir une instance pour le calendrier ISO tenant compte de la Locale pour des heures |
static DateTimeFormatter ofPattern(String pattern) |
Obtenir une instance qui utilise le motif passé en paramètre |
static DateTimeFormatter ofPattern(String pattern, Locale locale) |
Obtenir une instance qui utilise le motif et la Locale passés en paramètres |
TemporalAccessor parse(CharSequence text) |
Analyser la chaîne de caractères fournie en paramètre pour obtenir une instance d'un objet temporel |
TemporalAccessor parse(CharSequence text, ParsePosition position) |
Analyser le texte fourni à partir de la position précisée |
<T> T parse(CharSequence text, TemporalQuery<T> query) |
Analyser le texte pour obtenir une instance du type précisé par la requête |
TemporalAccessor parseBest(CharSequence text, TemporalQuery<?>... queries) |
Analyser au mieux le texte pour renvoyer une instance d'un des types précisés par les requêtes fournies |
static TemporalQuery<Period> parsedExcessDays() |
Retourner une requête qui permet d'obtenir une instance de type Period contenant un nombre de jours supplémentaires résultant de l'analyse. Cette période peut par exemple valoir un jour selon le ResolutionStyle et la valeur 24:00 pour l'heure |
static TemporalQuery<Boolean> parsedLeapSecond() |
Retourner une requête qui permet de déterminer si l'analyse à trouver une leap-second (la valeur de la seconde est 60) |
TemporalAccessor parseUnresolved(CharSequence text, ParsePosition position) |
Effectuer l'analyse pour extraire les champs sans les résoudre |
Format toFormat() |
Assurer une certaine compatibilité en retournant une implémentation de type java.text.Format de l'instance |
Format toFormat(TemporalQuery<?> parseQuery) |
|
DateTimeFormatter withChronology(Chronology chrono) |
Renvoyer une copie de l'instance qui utilise le calendrier fourni en paramètre |
DateTimeFormatter withDecimalStyle(DecimalStyle decimalStyle) |
Renvoyer une copie de l'instance qui utilise le DecimalStyle fourni en paramètre |
DateTimeFormatter withLocale(Locale locale) |
Renvoyer une copie de l'instance qui utilise la Locale fournie en paramètre |
DateTimeFormatter withResolverFields(Set<TemporalField> resolverFields) DateTimeFormatter withResolverFields(TemporalField... resolverFields) |
Renvoyer une copie de l'instance dans laquelle les champs qui seront utilisés pour définir l'objet temporel lors de la phase d'analyse sont précisés |
DateTimeFormatter withResolverStyle(ResolverStyle resolverStyle) |
Renvoyer une copie de l'instance dans laquelle le style de résolution est modifié avec celui fourni en paramètre (LENIENT, SMART, STRICT) |
DateTimeFormatter withZone(ZoneId zone) |
Renvoyer une copie de l'instance qui utilise le fuseau horaire fourni en paramètre |
L'analyse d'une chaîne de caractères par rapport au motif se fait en deux phases :
La méthode parseUnresolved() ne réalise que la première étape, ce qui réserve son utilisation à des besoins très spécifiques de bas niveau.
L'étape de résolution peut être configurée grâce à deux propriétés :
La résolution est un processus complexe. Il est possible que l'analyse extrait plusieurs champs : année, mois, jour du mois et jour de l'année. Pour composer la date, il est possible d'utiliser :
La définition de la propriété resolverFields permet de définir les champs à utiliser. Si la propriété n'est pas définie dans le cas ci-dessus, alors les deux résolutions sont effectuées et doivent renvoyer le même résultat.
Les méthodes d'analyse et de formatage lève une exception en cas de problème durant leur exécution :
Plusieurs méthodes de l'API permettent de faciliter l'utilisation d'un DateTimeFormatter.
La méthode format() d'un objet de type ChronoLocalDate permet d'obtenir une représentation de l'instance sous une forme textuelle en utilisant le DateTimeFormatter fourni en paramètre.
La méthode parse() de la classe LocalDate qui attend en paramètre la chaîne de caractères à analyser utilise le Formatter ISO_LOCAL_DATE. Une surcharge de la méthode parse() attend un second paramètre de type DateTimeFormatter qui permet de préciser le formateur à utiliser.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
public class TestDateTimeFormatter {
public static void main(String[] args) {
String dateStr = "";
LocalDateTime dateTime = LocalDateTime.of(2014, Month.DECEMBER, 25, 0,0,0);
System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
}
}
Résultat : |
20141225
Il est aussi possible de définir son propre format.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
public class TestDateTimeFormatter {
public static void main(String[] args) {
LocalDateTime dateTime = LocalDateTime.of(2014, Month.DECEMBER, 25, 0,0,0);
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("dd MMM yyyy");
String dateTimeStr = dateTime.format(formatter);
System.out.println(dateTimeStr);
LocalDate date = LocalDate.parse(dateTimeStr, formatter);
System.out.println(date);
}
}
Résultat : |
25 déc. 2014
2014-12-25
La méthode parseBest() permet d'obtenir un objet temporel dont les types possibles sont fournis en paramètre sous la forme de requêtes. Ceci est pratique lorsque le motif possède des parties optionnelles : selon le texte fourni, plusieurs types d'objets temporels peuvent être obtenus. Cette méthode tente de renvoyer la plus complète possible en fonction du texte.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
public class TestDateTimeFormatter {
public static void main(String[] args) {
parseBest("2014-12-25 00:00");
parseBest("2014-12-25 00:00[Europe/Paris]");
}
private static void parseBest(String str) {
System.out.println("Texte = " + str);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm['['VV']']");
TemporalAccessor dt = formatter.parseBest(str, ZonedDateTime::from, LocalDateTime::from);
if (dt instanceof ZonedDateTime) {
System.out.println("ZonedDateTime obtenu = " + dt);
} else {
System.out.println("LocalDateTime obtenu = " + dt);
}
}
}
Résultat : |
Texte = 2014-12-25 00:00
LocalDateTime obtenu = 2014-12-25T00:00
Texte = 2014-12-25 00:00[Europe/Paris]
ZonedDateTime obtenu =
2014-12-25T00:00+01:00[Europe/Paris]
La classe DateTimeFormatter est immuable et thread-safe : elle peut donc sans soucis être définie comme variable static.
La classe DateTimeFormatterBuilder permet de faciliter la composition dynamique d'un motif complexe encapsulé dans une instance de type DateTimeFormatter.
Elle possède de nombreuses méthodes à invoquer successivement pour composer le motif. La méthode toFormatter() permet d'obtenir l'instance une fois la composition réalisée.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.chrono.Chronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.FormatStyle;
import java.time.temporal.ChronoField;
import java.util.Locale;
public class TestDateTimeFormatterBuilder {
public static void main(String[] args) {
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendText(ChronoField.DAY_OF_MONTH);
builder.appendLiteral(" ");
builder.appendText(ChronoField.MONTH_OF_YEAR);
builder.appendLiteral(" ");
builder.appendText(ChronoField.YEAR);
DateTimeFormatter formatter = builder.toFormatter();
System.out.println(formatter.toString());
System.out.println(formatter.format(LocalDate.now()));
String motif = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
FormatStyle.FULL, FormatStyle.FULL, Chronology.of("ISO"), Locale.FRENCH);
System.out.println(motif);
formatter = DateTimeFormatter.ofPattern(motif);
System.out.println(formatter.format(ZonedDateTime.now()));
}
}
Résultat : |
Text(DayOfMonth)' 'Text(MonthOfYear)' 'Text(Year)
25 février 2015
EEEE d MMMM yyyy HH' h 'mm z
mercredi 25 février 2015 23 h 57 CET
Par défaut, l'API Date-Time utilise le calendrier ISO-8601 qui repose sur le calendrier Grégorien.
Le package java.time.chrono propose plusieurs autres implémentations de calendriers : Hijrah, Japanese, Minguo, ThaiBuddhist.
L'interface Chronology définit les fonctionnalités d'un calendrier. Un calendrier permet de représenter de manière humaine un point dans le temps.
L'interface Era définit les fonctionnalités d'une ère. Généralement un calendrier contient deux ères mais certains calendriers sont divisés en plusieurs ères comme par exemple le calendrier japonais.
Plusieurs interfaces permettent de définir les fonctionnalités d'un objet temporel pour un calendrier :
L'utilisation de ces interfaces est à réserver pour des besoins calendaires spécifiques et ne devrait pas être utilisée dans un contexte courant.
Chaque calendrier doit proposer une implémentation de Chronology, ChronoLocalDate et Era. Le JDK 8 fournit en standard plusieurs implémentations de calendriers :
Calendrier |
Chronology |
ChronoLocalDate |
Era |
ISO |
IsoChronology |
LocalDate |
IsoEra |
Hijrah |
HijrahChronology |
HijrahDate |
HijrahEra |
Japanese |
JapaneseChronology |
JapaneseDate |
JapaneseEra |
Minguo |
MinguoChronology |
MinguoDate |
MinguoEra |
ThaiBuddhist |
ThaiBuddhistChronology |
ThaiBuddhistDate |
ThaiBuddhistEra |
La méthode from() d'un calendrier permet de convertir l'objet temporel fourni en paramètre pour obtenir sa représentation dans le calendrier. Elle lève une exception de type DateTimeException si cette conversion échoue.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Month;
import java.time.chrono.HijrahChronology;
import java.time.chrono.HijrahDate;
import java.time.chrono.JapaneseChronology;
import java.time.chrono.JapaneseDate;
import java.time.chrono.MinguoChronology;
import java.time.chrono.MinguoDate;
import java.time.chrono.ThaiBuddhistChronology;
import java.time.chrono.ThaiBuddhistDate;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;
public class TestChronology {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2014, Month.DECEMBER, 25);
DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
.withLocale(Locale.getDefault(Locale.Category.FORMAT));
JapaneseDate jdate = JapaneseDate.from(date);
dtf = dtf.withChronology(JapaneseChronology.INSTANCE);
System.out.println("Japanese : "+dtf.format(jdate));
HijrahDate hdate = HijrahDate.from(date);
dtf = dtf.withChronology(HijrahChronology.INSTANCE);
System.out.println("Hijrah : "+dtf.format(hdate));
MinguoDate mdate = MinguoDate.from(date);
dtf = dtf.withChronology(MinguoChronology.INSTANCE);
System.out.println("Minguo : "+dtf.format(mdate));
ThaiBuddhistDate tdate = ThaiBuddhistDate.from(date);
dtf = dtf.withChronology(ThaiBuddhistChronology.INSTANCE);
System.out.println("Thai Buddhist : "+dtf.format(tdate));
}
}
Résultat : |
Japanese : 25/12/26 H
Hijrah : 3/3/1436 AH
Minguo : 25/12/103 1
Thai Buddhist : 25/12/2557
Inversement, il est possible de convertir une date d'un calendrier différent d'ISO en la passant en paramètre de la méthode from() de la classe LocalDate.
L'API Date-Time propose quelques classes et interfaces pour des fonctionnalités spécifiques.
Une instance de type Clock permet d'obtenir une date-heure «courante» dans un fuseau horaire.
L'implémentation par défaut est équivalente à une utilisation de la méthode currentTimeMillis() de la classe System et de la méthode getDefault() de la classe TimeZone.
L'utilisation d'une instance de type Clock est facultative car la plupart des classes qui encapsulent des données temporelles possèdent une méthode now() qui permet de créer une instance encapsulant tout ou partie de la date-heure système dans le fuseau horaire par défaut. Cette méthode utilise une instance de type Clock pour obtenir ces informations du système d'exploitation.
Une surcharge de la méthode now() attend en paramètre un objet de type Clock qui permet de fournir sa propre implémentation.
Cependant, le but de la classe Clock est de fournir facilement une implémentation alternative : ceci peut être particulièrement intéressant notamment pour les tests automatisés. Une bonne pratique est alors de permettre l'injection d'une instance de type Clock et d'utiliser cette dernière pour obtenir l'instant courant. Cela permet par exemple d'utiliser l'implémentation par défaut en production et d'utiliser une instance obtenue en invoquant la fabrique fixed() ou offset() lors des tests automatisés. Ceci pour, par exemple, tester le code dans différents fuseaux horaires ou renvoyer systématiquement le même instant à chaque invocation.
La classe Clock est abstraite : il n'est pas possible d'en créer une instance en utilisant l'opérateur new. Il est obligatoire d'utiliser une des méthodes statiques qui sont des fabriques : systemDefaultZone(), system(ZoneId), offset(Clock, Duration), systemUTC(), fixed(Instant, ZoneId), tick(Clock, Duration), tickMinutes(ZoneId) et tickSeconds(ZoneId).
Elle possède plusieurs méthodes :
Méthode |
Rôle |
boolean equals(Object obj) |
Vérifier l'égalité avec l'objet fourni en paramètre |
static Clock fixed(Instant fixedInstant, ZoneId zone) |
Obtenir une instance qui renvoie toujours le même instant |
abstract ZoneId getZone() |
Obtenir le fuseau horaire utilisé lors de la détermination des dates et heures |
abstract Instant instant() |
Obtenir l'instant courant de l'horloge |
long millis() |
Obtenir le nombre de millisecondes courantes de l'horloge depuis le 1er janvier 1970 |
static Clock offset(Clock baseClock, Duration offsetDuration) |
Obtenir une instance qui va utiliser l'instance de type Clock passée en paramètre pour obtenir la date-heure de base et appliquée la durée fournie comme décalage |
static Clock system(ZoneId zone) |
Obtenir une instance qui utilise l'horloge système |
static Clock systemDefaultZone() |
Obtenir une instance qui utilise l'horloge système dans le fuseau horaire par défaut |
static Clock systemUTC() |
Obtenir une instance qui utilise l'horloge système dans le fuseau horaire UTC |
static Clock tick(Clock baseClock, Duration tickDuration) |
Obtenir une instance qui renvoie l'instant de la Clock fournie en paramètre tronquée avec la durée fournie |
static Clock tickMinutes(ZoneId zone) |
Obtenir l'instant courant de l'horloge avec les nanosecondes et les secondes toujours à zéro |
static Clock tickSeconds(ZoneId zone) |
Obtenir l'instant courant de l'horloge avec les nanosecondes toujours à zéro |
abstract Clock withZone(ZoneId zone) |
Renvoyer une copie de l'instance courante qui utilise le fuseau horaire passé en paramètre |
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
public class TestClock {
public static void main(String[] args) {
Clock clock = Clock.systemDefaultZone();
Instant instant = clock.instant();
System.out.println("instant= "+instant);
DateTimeFormatter df =
DateTimeFormatter.ofPattern("uuuu-MM-dd hh:mm:ss",Locale.FRENCH);
System.out.println(" "
+df.format(ZonedDateTime.ofInstant(instant, ZoneId.systemDefault())));
clock = Clock.systemUTC();
System.out.println("systemUTC= "+clock.instant());
clock = Clock.system(ZoneId.of("America/Los_Angeles"));
System.out.println("system= "+clock.instant());
clock = Clock.fixed(Instant.parse("2014-12-25T23:59:59Z"), ZoneId.systemDefault());
System.out.println("fixed= "+clock.instant());
clock = Clock.tick(Clock.systemDefaultZone(), Duration.ofDays(400L));
System.out.println("tick= "+clock.instant());
clock = Clock.tick(Clock.systemDefaultZone(), Duration.ofHours(2L));
System.out.println("tick= "+clock.instant());
clock = Clock.tickMinutes(ZoneId.systemDefault());
System.out.println("tickMinutes="+clock.instant());
}
}
Résultat : |
instant= 2015-02-10T06:18:52.156Z
2015-02-10 07:18:52
systemUTC= 2015-02-10T06:18:52.203Z
system= 2015-02-10T06:18:52.203Z
fixed= 2014-12-25T23:59:59Z
tick= 2014-11-26T00:00:00Z
tick= 2015-02-10T06:00:00Z
tickMinutes=2015-02-10T06:18:00Z
Par défaut, les instances obtenues avec l'implémentation fournie par le JDK tentent d'utiliser la meilleure horloge disponible sur le système : par défaut c'est la méthode currentTimeMillis() de la classe System ou une autre solution si une meilleure est disponible sur le système pour obtenir l'instant courant. La méthode currentTimeMillis() de la classe System possède cependant des limites en termes de précision et de véracité. Il est possible de définir ses propres implémentations de la classe Clock pour par exemple utiliser un serveur NTP.
Une implémentation personnelle de classe Clock doit respecter plusieurs recommandations :
L'interface java.time.temporal.TemporalAdjuster définit le contrat d'un objet qui contient des traitements pour ajuster une date encapsulée sous la forme d'un type Temporal tel que le premier ou le dernier jour du mois, le prochain mardi, le troisième vendredi du mois, ...
Elle ne définit qu'une seule méthode :
Méthode |
Rôle |
Temporal adjustInto(Temporal temporal) |
Renvoyer une copie ajustée de l'objet temporel fourni en paramètre |
La classe TemporalAdjusters propose plusieurs méthodes statiques qui renvoient des implémentations de type TemporalAdjuster pour réaliser plusieurs ajustements courants :
Méthode |
Rôle |
static TemporalAdjuster dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek) |
Renvoyer une instance qui encapsule la date courante ajustée au N-ième (précisé par le paramètre ordinal) jour de la semaine (précisé par le paramètre dayOfWeek) du même mois |
static TemporalAdjuster firstDayOfMonth() |
Renvoyer une instance qui encapsule la date courante ajustée au premier jour du mois |
static TemporalAdjuster firstDayOfNextMonth() |
Renvoyer une instance qui encapsule la date courante ajustée au premier jour du mois suivant |
static TemporalAdjuster firstDayOfNextYear() |
Renvoyer une instance qui encapsule la date courante ajustée au premier jour de l'année suivante |
static TemporalAdjuster firstDayOfYear() |
Renvoyer une instance qui encapsule la date courante ajustée au premier jour de l'année |
static TemporalAdjuster firstInMonth(DayOfWeek dayOfWeek) |
Renvoyer une instance qui encapsule la date courante ajustée sur le premier dayOfWeek du mois |
static TemporalAdjuster lastDayOfMonth() |
Renvoyer une instance qui encapsule la date courante ajustée au dernier jour du mois |
static TemporalAdjuster lastDayOfYear() |
Renvoyer une instance qui encapsule la date courante ajustée au dernier jour de l'année |
static TemporalAdjuster lastInMonth(DayOfWeek dayOfWeek) |
Renvoyer une instance qui encapsule la date courante ajustée sur le dernier dayOfWeek du même mois |
static TemporalAdjuster next(DayOfWeek dayOfWeek) |
Renvoyer une instance qui encapsule la date courante ajustée au prochain dayOfWeek |
static TemporalAdjuster nextOrSame(DayOfWeek dayOfWeek) |
Renvoyer une instance qui ajuste la date à la première occurrence du dayOfWeek qui suit la date ajustée sauf si ce jour est déjà atteint auquel cas la même instance est retournée |
static TemporalAdjuster ofDateAdjuster(UnaryOperator<LocalDate> dateBasedAdjuster) |
Obtenir une instance qui encapsule l'objet de type UnaryOperator<LocalDate> réalisant l'ajustement |
static TemporalAdjuster previous(DayOfWeek dayOfWeek) |
Renvoyer une instance qui ajuste la date sur la première occurrence du dayOfWeek fourni qui précède la date à ajuster |
static TemporalAdjuster previousOrSame(DayOfWeek dayOfWeek) |
Renvoyer une instance qui ajuste la date sur la première occurrence du dayOfWeek fourni qui précède la date ajustée sauf si ce jour est déjà atteint auquel cas la même instance est retournée |
L'ajustement de ces méthodes ne concerne que la date : par exemple, si l'instance fournie en paramètre est de type ZonedDateTime alors la valeur retournée aura l'heure et le fuseau horaire originaux.
Toutes les instances retournées par les méthodes statiques sont immuables.
Il existe deux manières d'utiliser un TemporalAdjuster :
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import static java.time.temporal.TemporalAdjusters.*;
public class TestTemporalAdjuster {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2015, Month.JANUARY, 31);
System.out.println("premier jour du mois : "
+ date.with(firstDayOfMonth()));
System.out.println("premier lundi du mois : "
+ date.with(firstInMonth(DayOfWeek.MONDAY)));
System.out.println("dernier jour du mois : "
+ date.with(lastDayOfMonth()));
System.out.println("premier jour de l'année : "
+ date.with(firstDayOfYear()));
System.out.println("troisieme mardi du mois : "
+ date.with(dayOfWeekInMonth(3, DayOfWeek.FRIDAY)));
System.out.println("prochain vendredi du mois : "
+ date.with(next(DayOfWeek.FRIDAY)));
System.out.println("prochain samedi du mois ou lui meme : "
+ date.with(nextOrSame(DayOfWeek.SATURDAY)));
System.out.println("premier jour du mois suivant : "
+ date.with(firstDayOfNextMonth()));
System.out.println("premier jour de l'année suivante : "
+ date.with(firstDayOfNextYear()));
System.out.println("premier jour du mois : "
+ firstDayOfMonth().adjustInto(date));
System.out.println("premier lundi du mois : "
+ firstInMonth(DayOfWeek.MONDAY).adjustInto(date));
System.out.println("dernier jour du mois : "
+ lastDayOfMonth().adjustInto(date));
System.out.println("troisieme mardi du mois : "
+ dayOfWeekInMonth(3, DayOfWeek.MONDAY).adjustInto(date));
System.out.println("prochain vendredi du mois : "
+ next(DayOfWeek.FRIDAY).adjustInto(date));
System.out.println("prochain samedi du mois ou lui-meme : "
+ nextOrSame(DayOfWeek.SATURDAY).adjustInto(date));
System.out.println("premier jour de l'année : "
+ firstDayOfYear().adjustInto(date));
System.out.println("premier jour du mois suivant : "
+ firstDayOfNextMonth().adjustInto(date));
System.out.println("premier jour de l'année suivante : "
+ firstDayOfNextYear().adjustInto(date));
}
}
Résultat : |
premier jour du mois : 2015-01-01
premier lundi du mois : 2015-01-05
dernier jour du mois : 2015-01-31
premier jour de l'année : 2015-01-01
troisieme mardi du mois : 2015-01-16
prochain vendredi du mois : 2015-02-06
prochain samedi du mois ou lui meme : 2015-01-31
premier jour du mois suivant : 2015-02-01
premier jour de l'année suivante : 2016-01-01
premier jour du mois : 2015-01-01
premier lundi du mois : 2015-01-05
dernier jour du mois : 2015-01-31
troisieme mardi du mois : 2015-01-19
prochain vendredi du mois : 2015-02-06
prochain samedi du mois ou lui-meme : 2015-01-31
premier jour de l'année : 2015-01-01
premier jour du mois suivant : 2015-02-01
premier jour de l'année suivante : 2016-01-01
Il est possible de développer ses propres TemporalAdjuster pour des besoins spécifiques. Pour cela, il faut créer une classe qui implémente l'interface TemporalAdjuster.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import static java.time.temporal.TemporalAdjusters.*;
public class JourDePaieAdjuster implements TemporalAdjuster {
@Override
public Temporal adjustInto(Temporal date) {
LocalDate resultat = LocalDate.from(date);
if (resultat.getDayOfMonth() >= 20) {
resultat = resultat.with(firstDayOfNextMonth());
}
resultat = resultat.withDayOfMonth(20);
if (resultat.getDayOfWeek() == DayOfWeek.SATURDAY
|| resultat.getDayOfWeek() == DayOfWeek.SUNDAY) {
resultat = resultat.with(previous(DayOfWeek.FRIDAY));
}
return date.with(resultat);
}
}
Dans l'exemple ci-dessus, la classe JourDePaieAdjuster permet de déterminer la date de prochaine paie par rapport à la date fournie en paramètre selon les règles suivantes :
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class JourDePaieAdjusterTest {
private JourDePaieAdjuster sut;
public JourDePaieAdjusterTest() {
}
@Before
public void setUp() {
sut = new JourDePaieAdjuster();
}
/**
* Test la date est avant le 20 du mois
*/
@Test
public void testAdjustIntoAvantLe20() {
// given
Temporal date = LocalDate.of(2015, Month.JANUARY, 15);
Temporal expResult = LocalDate.of(2015, Month.JANUARY, 20);
// when
Temporal result = sut.adjustInto(date);
// then
assertEquals("Mauvaise date ajustee", expResult, result);
}
/**
* Test la date est le 20 du mois
*/
@Test
public void testAdjustIntoLe20() {
// given
Temporal date = LocalDate.of(2015, Month.JANUARY, 20);
Temporal expResult = LocalDate.of(2015, Month.FEBRUARY, 20);
// when
Temporal result = sut.adjustInto(date);
// then
assertEquals("Mauvaise date ajustee", expResult, result);
}
/**
* Test la date est le 21 du mois
*/
@Test
public void testAdjustIntoLe21() {
// given
Temporal date = LocalDate.of(2015, Month.JANUARY, 21);
Temporal expResult = LocalDate.of(2015, Month.FEBRUARY, 20);
// when
Temporal result = sut.adjustInto(date);
// then
assertEquals("Mauvaise date ajustee", expResult, result);
}
/**
* Test la date est le 15 juin (le 20 juin est un week end)
*/
@Test
public void testAdjustIntoLe15Juin() {
// given
Temporal date = LocalDate.of(2015, Month.JUNE, 15);
Temporal expResult = LocalDate.of(2015, Month.JUNE, 19);
// when
Temporal result = sut.adjustInto(date);
// then
assertEquals("Mauvaise date ajustee", expResult, result);
}
}
Il est recommandé que l'implémentation soit immuable.
L'interface java.time.temporal.TemporalQuery définit les fonctionnalités qui permettent d'obtenir des informations d'un objet temporel sous la forme d'une requête. C'est une interface fonctionnelle.
La requête peut retourner n'importe quel type.
Remarque : l'interface TemporalField définit aussi les fonctionnalités pour obtenir la valeur d'un champ d'une date mais sa valeur de retour se limite à un entier long.
Elle ne définit qu'une seule méthode
Méthode |
Rôle |
R queryFrom(TemporalAccessor temporal) |
Traiter la requête sur l'instance fournie en paramètre et renvoyer le résultat sous la forme d'une instance du type generic R |
La classe java.time.temporal.TemporalQueries propose plusieurs méthodes statiques qui renvoient des implémentations de type TemporalQuery pour réaliser des requêtes courantes :
Méthode |
Rôle |
static TemporalQuery<Chronology> chronology() |
Appliquer une requête qui renvoie le calendrier ou null si aucun n'est trouvé |
static TemporalQuery<LocalDate> localDate() |
Appliquer une requête qui renvoie la LocalDate ou null si aucune n'est trouvée |
static TemporalQuery<LocalTime> localTime() |
Appliquer une requête qui renvoie la LocalTime ou null si aucune n'est trouvée |
static TemporalQuery<ZoneOffset> offset() |
Appliquer une requête qui renvoie la ZoneOffset ou null si aucune n'est trouvée |
static TemporalQuery<TemporalUnit> precision() |
Appliquer une requête qui renvoie la plus petite unité temporelle supportée |
static TemporalQuery<ZoneId> zone() |
Appliquer une requête qui renvoie la ZoneId ou la ZoneOffset si aucune n'est trouvée |
static TemporalQuery<ZoneId> zoneId() |
Appliquer une requête qui renvoie la ZoneId ou null si aucune n'est trouvée |
Ces méthodes sont utiles notamment lorsque l'on ne connaît pas précisément le type de l'objet temporel.
Il existe deux manières d'utiliser un TemporalQuery :
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.Year;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.chrono.Chronology;
import static java.time.temporal.TemporalQueries.*;
import java.time.temporal.TemporalQuery;
import java.time.temporal.TemporalUnit;
public class TestTemporalQuery {
public static void main(String[] args) {
TemporalQuery<TemporalUnit> queryPrecision = precision();
System.out.println("Precision : ");
System.out.println("LocalDate : " + LocalDate.now().query(queryPrecision));
System.out.println("LocalDateTime : " + LocalDateTime.now().query(queryPrecision));
System.out.println(" Year : " + Year.now().query(queryPrecision));
System.out.println(" YearMonth : " + YearMonth.now().query(queryPrecision));
System.out.println(" Instant : " + Instant.now().query(queryPrecision));
TemporalQuery<ZoneId> queryZoneId = zoneId();
System.out.println("ZoneId : ");
System.out.println(" ZonedDateTime : " + ZonedDateTime.now().query(queryZoneId));
System.out.println(" LocalDate : " + LocalDate.now().query(queryZoneId));
TemporalQuery<Chronology> queryChronology = chronology();
System.out.println("Chronology : ");
System.out.println(" ZonedDateTime : " + ZonedDateTime.now().query(queryChronology));
System.out.println(" Instant : " + Instant.now().query(queryChronology));
TemporalQuery<ZoneId> queryZone = zone();
System.out.println("Zone : ");
System.out.println(" ZonedDateTime : " + ZonedDateTime.now().query(queryZone));
System.out.println(" OffsetDateTime : " + OffsetDateTime.now().query(queryZone));
System.out.println(" Instant : " + Instant.now().query(queryZone));
TemporalQuery<ZoneOffset> queryOffset = offset();
System.out.println("Offset : ");
System.out.println(" ZonedDateTime : " + ZonedDateTime.now().query(queryOffset));
System.out.println(" OffsetDateTime : " + OffsetDateTime.now().query(queryOffset));
System.out.println(" Instant : " + Instant.now().query(queryOffset));
TemporalQuery<LocalDate> queryLocalDate = localDate();
System.out.println("LocalDateTime : ");
System.out.println(" LocalDateTime : " + ZonedDateTime.now().query(queryLocalDate));
System.out.println(" Instant : " + Instant.now().query(queryLocalDate));
TemporalQuery<LocalTime> queryLocalTime = localTime();
System.out.println("LocalTime : ");
System.out.println(" LocalTime : " + ZonedDateTime.now().query(queryLocalTime));
System.out.println(" Instant : " + Instant.now().query(queryLocalTime));
}
}
Résultat : |
Precision :
LocalDate : Days
LocalDateTime : Nanos
Year : Years
YearMonth : Months
Instant : Nanos
ZoneId :
ZonedDateTime : Europe/Paris
LocalDate : null
Chronology :
ZonedDateTime : ISO
Instant : null
Zone :
ZonedDateTime :
Europe/Paris
OffsetDateTime : +01:00
Instant : null
Offset :
ZonedDateTime : +01:00
OffsetDateTime : +01:00
Instant : null
LocalDateTime :
LocalDateTime : 2015-02-11
Instant : null
LocalTime :
LocalTime : 18:43:20.250
Instant : null
Il est possible de définir ses propres requêtes en créant une classe qui implémente l'interface TemporalQuery. Cette implémentation contient les traitements réalisés par la requête : elle doit être thread-safe. L'objet fourni en paramètre ne doit pas être modifié. Celui-ci peut ne pas utiliser le calendrier ISO : l'implémentation doit en tenir compte et éventuellement ne pas exécuter la requête si ce n'est pas le cas.
La valeur de retour peut être null pour indiquer qu'il n'y a pas de résultat à la requête.
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Month;
import java.time.MonthDay;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQuery;
public class EstPrintempsQuery implements TemporalQuery<Boolean> {
@Override
public Boolean queryFrom(TemporalAccessor date) {
Boolean resultat = null;
int jour = date.get(ChronoField.DAY_OF_MONTH);
int mois = date.get(ChronoField.MONTH_OF_YEAR);
MonthDay courant = MonthDay.of(mois, jour);
MonthDay debut = MonthDay.of(Month.MARCH, 20);
MonthDay fin = MonthDay.of(Month.JUNE, 21);
return (courant.isAfter(debut) && courant.isBefore(fin));
}
}
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.Temporal;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class EstPrintempsQueryTest {
private EstPrintempsQuery sut;
@Before
public void setUp() {
sut = new EstPrintempsQuery();
}
/**
* Test date avant le printemps
*/
@Test
public void testQueryFromAvantPrintemps() {
// given
Temporal date = LocalDate.of(2015, Month.JANUARY, 15);
// when
Boolean result = sut.queryFrom(date);
// then
assertEquals("Mauvaise reponse", Boolean.FALSE, result);
}
/**
* Test date apres le printemps
*/
@Test
public void testQueryFromApresPrintemps() {
// given
Temporal date = LocalDate.of(2015, Month.OCTOBER, 15);
// when
Boolean result = sut.queryFrom(date);
// then
assertEquals("Mauvaise reponse", Boolean.FALSE, result);
}
/**
* Test date le premier jour du printemps
*/
@Test
public void testQueryFromPremierJourDuPrintemps() {
// given
Temporal date = LocalDate.of(2015, Month.MARCH, 21);
// when
Boolean result = sut.queryFrom(date);
// then
assertEquals("Mauvaise reponse", Boolean.TRUE, result);
}
/**
* Test date le dernier jour du printemps
*/
@Test
public void testQueryFromDernierJourDuPrintemps() {
// given
Temporal date = LocalDate.of(2015, Month.JUNE, 20);
// when
Boolean result = sut.queryFrom(date);
// then
assertEquals("Mauvaise reponse", Boolean.TRUE, result);
}
/**
* Test date le dernier jour de l'hiver
*/
@Test
public void testQueryFromDernierJourHiver() {
// given
Temporal date = LocalDate.of(2015, Month.MARCH, 20);
// when
Boolean result = sut.queryFrom(date);
// then
assertEquals("Mauvaise reponse", Boolean.FALSE, result);
}
/**
* Test date le premier jour de l'été
*/
@Test
public void testQueryFromPermierJourEte() {
// given
Temporal date = LocalDate.of(2015, Month.JUNE, 21);
// when
Boolean result = sut.queryFrom(date);
// then
assertEquals("Mauvaise reponse", Boolean.FALSE, result);
}
}
Certaines méthodes de classes temporelles respectent la signature de l'interface TemporalQuery et peuvent donc être utilisées sous la forme d'une référence de méthode dans une expression lambda de type TemporalQuery. C'est par exemple le cas des méthodes from() des classes LocalDate et ZoneId.
La définition d'une API aussi riche que l'API Date-Time pose nécessairement plusieurs questions relatives à son utilisation :
L'API Date-Time contient de nombreuses classes répondant à la plupart des besoins en matière de gestion de données temporelles utilisant le calendrier ISO :
Cette richesse oblige à choisir la classe la plus adaptée à son besoin selon plusieurs critères :
Le tableau ci-dessous synthétise les principaux champs supportés par chaque objet temporel :
Class |
Année |
Mois |
Jour |
Heure |
Min |
Sec |
Nano |
ZoneOffset |
ZoneID |
Instant |
X |
X |
|||||||
LocalDate |
X |
X |
X |
||||||
LocalDateTime |
X |
X |
X |
X |
X |
X |
X |
||
ZonedDateTime |
X |
X |
X |
X |
X |
X |
X |
X |
X |
LocalTime |
X |
X |
X |
X |
|||||
MonthDay |
X |
X |
|||||||
Year |
X |
||||||||
YearMonth |
X |
X |
|||||||
Month |
X |
||||||||
OffsetDateTime |
X |
X |
X |
X |
X |
X |
X |
X |
|
OffsetTime |
X |
X |
X |
X |
X |
||||
Duration |
1 |
1 |
1 |
X |
|||||
Period |
X |
X |
X |
(1) : la classe Duration ne stocke pas ces données mais propose des méthodes pour convertir la valeur dans ces unités
Il est préférable d'utiliser autant que possible des instances de type LocalDate, LocalTime, LocalDateTime et Instant. L'utilisation d'objets temporels qui gèrent un fuseau horaire rend les calculs beaucoup plus complexes. Ainsi, il est préférable de faire les calculs sur des objets sans fuseau puis de les convertir pour par exemple les afficher dans l'interface graphique.
L'utilisation principale des classes OffsetTime et OffsetDateTime est d'échanger une donnée temporelle sur le réseau ou de la stocker dans une base de données.
Généralement, l'API tente de respecter plusieurs règles dans l'utilisation des exceptions :
L'exception de type java.time.temporal.UnsupportedTemporalTypeException est levée par une méthode d'un objet temporel si ce dernier ne supporte pas un champ (ChronoField) ou une unité (ChronoUnit). Pour éviter cette exception, il faut s'assurer que les champs ou les unités utilisées directement ou indirectement sont supportés par l'objet temporel : ce n'est pas toujours évident au premier abord.
Avant Java 8, la gestion des données temporelles était assurée par les classes java.util.Date, java.util.Calendar, java.util.TimeZone et leurs sous-classes.
La conception et l'implémentation de l'API Date-Time est complètement différente des classes historiques de gestion des données temporelles contenues dans le package java.util. Il n'est donc pas toujours facile de trouver une correspondance direct entre les fonctionnalités des deux API. Le tableau ci-dessous tente de fournir une approche assez globale de cette correspondance :
java.util |
java.time |
Commentaires |
Date |
Instant |
Elles encapsulent toutes les deux un point dans le temps de manière indépendante de tout fuseau horaire |
GregorianCalendar |
ZonedDateTime |
Les méthodes from() et to() de la classe GregorianCalendar permettent de faire les conversions |
TimeZone |
ZoneId ou ZoneOffset |
ZoneId pour un fuseau horaire, ZoneOffset pour un décalage horaire |
GregorianCalendar avec l'heure à 00:00 |
LocalDate |
Pour permettre de faciliter l'intégration avec du code antérieur à Java 8, plusieurs méthodes ont été ajoutées :
Exemple ( code Java 8 ) : |
package com.jmdoudoux.test.java8.datetime;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
public class TestAvecCodeLegacy {
public static void main(String[] args) {
Calendar maintenant = Calendar.getInstance();
ZonedDateTime zonedDateTime =
ZonedDateTime.ofInstant(maintenant.toInstant(), ZoneId.systemDefault());
System.out.println("zonedDateTime="+zonedDateTime);
Date date = new Date();
Instant instant = date.toInstant();
System.out.println("instant="+instant);
date = Date.from(instant);
System.out.println("date="+date);
GregorianCalendar calendar = (GregorianCalendar) GregorianCalendar.getInstance();
TimeZone timezone = calendar.getTimeZone();
System.out.println("timezone="+timezone);
int offset = calendar.get(Calendar.ZONE_OFFSET);
System.out.println("offset="+offset);
zonedDateTime = calendar.toZonedDateTime();
System.out.println("zonedDateTime="+zonedDateTime);
LocalDateTime localDateTime = zonedDateTime.toLocalDateTime();
System.out.println("localDateTime="+localDateTime);
calendar = GregorianCalendar.from(zonedDateTime);
System.out.println("date="+calendar.getTime());
}
}
Résultat : |
zonedDateTime=2015-03-09T23:32:17.328+01:00[Europe/Paris]
instant=2015-03-09T22:32:17.500Z
date=Mon Mar 09 23:32:17 CET 2015
timezone=sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,
dstSavings=3600000,useDaylight=true,transitions=184,
lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,
dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,
startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,
endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
offset=3600000
zonedDateTime=2015-03-09T23:32:17.515+01:00[Europe/Paris]
localDateTime=2015-03-09T23:32:17.515
date=Mon Mar 09 23:32:17 CET 2015
L'API JDBC n'a pas été modifiée spécifiquement pour le support de l'API Date-Time : il suffit simplement d'utiliser les méthodes setObject() et getObject().
Le tableau ci-dessous montre la correspondance entre les types ANSI SQL et Java 8 :
ANSI SQL |
Java SE 8 |
DATE |
LocalDate |
TIME |
LocalTime |
TIMESTAMP |
LocalDateTime |
TIME WITH TIMEZONE |
OffsetTime |
TIMESTAMP WITH TIMEZONE |
OffsetDateTime |
Attention : les types "TIME WITH TIME ZONE" et "TIMESTAMP WITH TIME ZONE" sont relativement mal nommés car ils ne contiennent pas de fuseau horaire mais uniquement un offset. Il n'est donc pas possible à partir d'une donnée d'un de ces types de déterminer le fuseau horaire. Si celui-ci est requis, il faut le stocker dans une colonne dédiée.