101. La validation des données 103. La planification de tâches Imprimer Sommaire Consulter avec table des matières
Développons en Java   v 2.10  
Copyright (C) 1999-2016 .  

 

102. L'utilisation des dates

 

chapitre 1 0 2

 

Niveau : niveau 2 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 :

 

102.1. Les classes standard du JDK pour manipuler des dates

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 :

 

102.1.1. La classe java.util.Date

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.

 

102.1.2. La classe java.util.Calendar

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 :

 

102.1.3. La classe java.util.GregorianCalendar

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.

 

102.1.4. Les classes java.util.TimeZone et java.util.SimpleTimeZone

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().

 

102.1.5. La classe java.text.DateFormat

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.

 

102.1.6. La classe java.util.SimpleDateFormat

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)

 

102.1.7. Les classes java.sql.Date, java.sql.Time, java.sql.TimeStamp

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.

 

102.2. Des exemples de manipulations de dates

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.

 

102.3. La classe SimpleDateFormat

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é.

 

102.3.1. L'utilisation de la classe SimpleDateFormat

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 :

 

102.3.2. Les points faibles de la classe SimpleDateFormat

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)

 

102.4. Joda Time

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.

 

102.4.1. Les principales classes de JodaTime

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)

 

102.4.2. Le concept d'Instant

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.

 

102.4.2.1. 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.

 

102.4.2.2. La classe DateTime

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]

 

102.4.3. Le concept de Partial

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);

 

102.4.4. Les concepts d'intervalle, de durée et de période

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.

 

102.4.4.1. La classe Interval

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.

 

102.4.4.2. La classe Period

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.

 

102.4.4.3. La classe Duration

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.

 

102.4.5. Les calendriers et les fuseaux horaires

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.

 

102.4.5.1. La classe Chronology

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).

 

102.4.5.2. La classe DateTimeZone

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.htm

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.

 

102.4.5.3. Le système calendaire ISO8601

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);

 

102.4.5.4. La calendrier Bouddhiste

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

 

102.4.5.5. Le calendrier Copte

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

 

102.4.5.6. Le calendrier Ethiopien

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

 

102.4.5.7. Le calendrier Grégorien

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

 

102.4.5.8. Le système calendaire Grégorien/Julien

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é.

 

102.4.5.9. Le calendrier Islamique

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.

 

102.4.5.10. Le calendrier Julien

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

 

102.4.6. La manipulation des dates

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());

 

102.4.7. L'intéropabilité avec les classes du JDK

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);

 

102.4.8. Le formattage des dates

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.

 

102.4.9. D'autres fonctionnalités de Joda Time

Joda Time propose aussi des fonctionnalités pour modifier la date/heure par défaut ou utiliser des objets mutables.

 

102.4.9.1. La modification de l'heure de la JVM

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.

 

102.4.9.2. Les objets mutables

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.

 

102.5. La classe FastDateFormat du projet Apache commons.lang

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); }

 

102.6. L'API Date and Time

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 :

 

102.6.1. Le besoin d'une nouvelle API

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.

 

102.6.2. 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.
Son but est de permettre de pouvoir être remplacée par une autre implémentation notamment pour faciliter les tests unitaires


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

 

102.6.3. Les interfaces et classes de bas niveau de l'API

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.

 

 

102.6.3.1. L'interface TemporalAccessor

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

 

102.6.3.2. L'interface Temporal

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.

 

102.6.3.3. L'interface TemporalAmount

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 :

 

102.6.3.4. L'interface TemporalField

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.

 

102.6.3.5. L'énumération ChronoField

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

 

102.6.3.6. L'interface TemporalUnit

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.

 

102.6.3.7. L 'énumération ChronoUnit

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

 

102.6.4. Les classes spécifiques aux dates

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 :

 

102.6.4.1. L'énumération DayOfWeek

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

 

102.6.4.2. L'énumération Month

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.

 

102.6.4.3. La classe YearMonth

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().

 

102.6.4.4. La classe MonthDay

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().

 

102.6.4.5. La classe Year

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)
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 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().

 

102.6.5. La gestion du temps machine

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.

 

102.6.5.1. La classe Instant

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);
  }
}

 

102.6.5.2. La classe Duration

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 ».

 

102.6.6. La gestion du temps humain

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.

 

102.6.6.1. La classe LocalDate

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)
LocalDateTime atTime(int hour, int minute, int second)
LocalDateTime atTime(int hour, int minute, int second, int nanoOfSecond)
LocalDateTime atTime(LocalTime time)

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)
static LocalDate of(int year, Month month, int dayOfMonth)
static LocalDate ofEpochDay(long epochDay)
static LocalDate ofYearDay(int year, int dayOfYear)

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)
LocalDate plus(TemporalAmount amountToAdd)

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)

 

102.6.6.2. La classe LocalDateTime

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)
static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second)
static LocalDateTime of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond)
static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute)
static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second)
static LocalDateTime of(int year, Month month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond)

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().

 

102.6.6.3. La classe LocalTime

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)
LocalTime minus(TemporalAmount amountToSubtract)

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)
static LocalTime of(int hour, int minute, int second)
static LocalTime of(int hour, int minute, int second, int nanoOfSecond)

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)
LocalTime plus(TemporalAmount amountToAdd)

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().

 

102.6.6.4. La classe Period

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().

 

102.6.7. Les fuseaux et les décalages horaires

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.

 

102.6.7.1. La classe ZoneId

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().

 

102.6.7.2. La classe ZoneOffset

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().

 

102.6.7.3. La classe ZonedDateTime

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()
DayOfWeek getDayOfWeek()

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)
ZonedDateTime minus(TemporalAmount amountToSubtract)

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)
static ZonedDateTime of(LocalDateTime localDateTime, ZoneId zone)
static ZonedDateTime ofInstant(Instant instant, 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)
ZonedDateTime plus(TemporalAmount amountToAdd)

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().

 

102.6.7.4. La classe OffsetDateTime

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().

 

102.6.7.5. La classe OffsetTime

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().

 

102.6.8. L'analyse et le formatage

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.

 

102.6.8.1. La classe DateTimeFormatter

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.

 

102.6.8.2. La classe DateTimeFormatterBuilder

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

 

102.6.9. Les calendriers

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.

 

102.6.10. Les autres classes de l'API

L'API Date-Time propose quelques classes et interfaces pour des fonctionnalités spécifiques.

 

102.6.10.1. La classe Clock

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 :

 

102.6.10.2. L'interface TemporalAdjuster et la classe TemporalAdjusters

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.

 

102.6.10.3. L'interface TemporalQuery et la classe TemporalQueries

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.

 

102.6.11. L'utilisation de l'API Date-Time

La définition d'une API aussi riche que l'API Date-Time pose nécessairement plusieurs questions relatives à son utilisation :

 

102.6.11.1. Le choix du type d'objet temporel à utiliser

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
2014-12-25T17:46:39.567Z

         

X

X

   

LocalDate
2014-12-25

X

X

X

           

LocalDateTime
2014-12-25T17:46:39.567

X

X

X

X

X

X

X

   

ZonedDateTime
2014-12-25T17:46:39.567+01:00[Europe/Paris]

X

X

X

X

X

X

X

X

X

LocalTime
17:46:39.567

     

X

X

X

X

   

MonthDay
--12-25

 

X

X

           

Year
2014

X

               

YearMonth
2014-12

X

X

             

Month
DECEMBER

 

X

             

OffsetDateTime
2014-12-25T17:46:39.567+01:00

X

X

X

X

X

X

X

X

 

OffsetTime
17:66:39.567+01:00

     

X

X

X

X

X

 

Duration
PT10H

     

1

1

1

X

   

Period
P15D

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.

 

102.6.11.2. Les exceptions de l'API

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.

 

102.6.11.3. L'intégration avec le code avant Java 8

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.

 


  101. La validation des données 103. La planification de tâches Imprimer Sommaire Consulter avec table des matières Développons en Java   v 2.10  
Copyright (C) 1999-2016 .