• 40 heures
  • Difficile

Ce cours est visible gratuitement en ligne.

Ce cours existe en livre papier.

course.header.alt.is_certifying

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 05/07/2019

Découvrez la nouvelle API de gestion des dates de Java 8

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

En L'API time est l'une des fonctionnalitées les plus attendus depuis plus de 10 ans. Avant cette API il était très compliqué de gérer le temps dans Java. Ce temps là est maintenant révolu !

Introduction

Dans cette nouvelle API, deux conceptions du temps sont implémentées :

  • Temps machine (nombre de seconde depuis le premier janvier 1970) ou timestamp ;

  • Temps humain (plusieurs champs, jour - mois - année, ...)

Ce qui est particulièrement bien avec cette API, c'est que les méthodes et objets qui la composent parlent d'eux-mêmes. Dans cette partie il n'y aura pas besoin de beaucoup d'explications, les codes seront suffisamment parlant.
Je ne montrerai pas comment nous gérions les date avant cette API, je vous propose donc de rentrer directement dans le cœur du sujet.

Gestion du temps machine

 La classe   java.time.Instant  représente un point relatif au premier Janvier 1970. Elle dispose de plusieurs énumération comme  Instant.EPOCH  qui représente le point de départ de ce référentiel de temps, à savoir 1970-01-01T00:00:00Z. Mais aussi des méthodes comme  Instant.now()  qui nous donne la date exacte et d'autres méthodes comme   isAfter()  ,  isBefore, parse()  ,etc...
Bon, rien de très difficile ni de très pratique pour le moment. Regardons donc la gestion du temps humain.

Gestion du temps humain

Ici, les choses vont se compliquer car il y a des notions humaines que nous allons devoir prendre en considération, comme les fuseaux horaires. Enfin, non. Grâce à cette API ça ne sera pas difficile du tout.
Nous allons tout de même débuter par la gestion du temps et des date locale via les objets  LocalDateTime  et  LocatDate  .
Voici un exemple d'utilisation de ces objets :

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;

public class Test {
	public static void main(String[] args) {
      // Get the current date and time
      LocalDateTime currentTime = LocalDateTime.now();
      System.out.println("Date et heure courante : " + currentTime);
		
      LocalDate date1 = currentTime.toLocalDate();
      System.out.println("Date courante : " + date1);
		
      Month mois = currentTime.getMonth();
      int jour = currentTime.getDayOfMonth();
      int heure = currentTime.getHour();
		
      System.out.println("Mois : " + mois +", jour : " + jour +", heure : " + heure);
	
      //Avoir le 25 décembre 2023
      LocalDateTime date2 = currentTime.withDayOfMonth(25).withYear(2023).withMonth(12);
      System.out.println("Date modifiée : " + date2);
		
      //une autre façon
      LocalDate date3 = LocalDate.of(2023, Month.DECEMBER, 25);
      System.out.println("Autre façon de faire : " + date3);
		
      //On peut même parser une date depuis un String
      LocalTime parsed = LocalTime.parse("20:15:30");
      System.out.println("Date parsée : " + parsed);
	}
}

Ce qui nous donne le résultat suivant :

Date et heure courante : 2018-01-19T09:29:44.150862600
Date courante : 2018-01-19
Mois : JANUARY, jour : 19, heure : 9
Date modifiée : 2023-12-25T09:29:44.150862600
Autre façon de faire : 2023-12-25
Date parsée : 20:15:30

Il est également possible de manipuler ces objets afin d'ajouter ou de retirer du temps, que ce soit en jour, mois, année, heure... Ceci peut se faire grâce aux méthodes présente dans les objet  LocalDate  et  LocalDateTime  ainsi que la classe  ChronoUnit.

import java.time.LocalDateTime;
import java.time.Month;
import java.time.temporal.ChronoUnit;

public class DateTimeManipulation {

	public static void main(String[] args) {
		//Le 25 Décembre 2018 a 13H37
		LocalDateTime ldt = LocalDateTime.of(2018, Month.DECEMBER, 25, 13, 37, 0);
		System.out.println("Date de référence : " + ldt);
		//Utilisation de l'objet ChronoUnit pour changer l'objet
		System.out.println("+1 semaine : " + ldt.plus(1, ChronoUnit.WEEKS));
		System.out.println("+2 mois : " + ldt.plus(2, ChronoUnit.MONTHS));
		System.out.println("+3 ans : " + ldt.plus(3, ChronoUnit.YEARS));
		System.out.println("+4 heures : " + ldt.plus(4, ChronoUnit.HOURS));
		System.out.println("-5 secondes : " + ldt.minus(5, ChronoUnit.SECONDS));
		System.out.println("-38 minutes : " + ldt.minusMinutes(38));
	}
}

Vous avez sûrement remarqué qu'il existe les méthodes utilisant l'objet  ChronoUnit  ou le premier paramètre correspond à la durée d'unité que nous souhaitons ajouter ou retirer mais il existe aussi des méthodes spécifiques comme  minusMinutes()  .

Duration et Period

Ces deux objets permettent de gérer les intervales de temps. Period va gérer des intervales de dates (2 ans, 1 mois, 15 jours...) alors de Duration va gérer des intervales de temps machine, donc en seconde. Ces classes possèdent également de quoi faire la différence entre deux dates, voyons tout de suite ceci en action :

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.Period;
import java.time.temporal.ChronoUnit;

public class PeriodDurationTest {

	public static void main(String[] args) {
		//Toujours notre 25 Décembre 2018 a 13H37
		LocalDateTime ldt = LocalDateTime.of(2018, Month.DECEMBER, 25, 13, 37, 0);
		LocalDateTime ldt2 = ldt.plus(3, ChronoUnit.YEARS);
		LocalDateTime ldt3 = ldt.minusMinutes(1337);
		
		Period p = Period.between(ldt.toLocalDate(), ldt2.toLocalDate());
		Duration d = Duration.between(ldt.toLocalTime(), ldt3.toLocalTime());
		System.out.println("Période : " + p);
		System.out.println("Durée : " + d.getSeconds());
		System.out.println("Ecart en jour : " + ChronoUnit.DAYS.between(ldt, ldt2));
		
	}
}

Ce qui nous donne :

Période : 3
Durée : 6180

Vous avez pu remarquer que nous avons utilisé de nouveau l'objet  ChronoUnit  afin de savoir de combien de jour notre période est faite. Nous avons fait comme ceci car l'objet période ne calcul pas automatiquement le nombre de jour d'un écart demandé en année.

TemporalAdjusters

Cette classe va sûrement vous sauver la vie à maintes reprise car elle permet d'ajuster une date en fonction d'éléments relatif comme avoir le prochain mardi, le premier samedi du mois, etc. Vu sa nature, vous aurez compris que cet objet manipule des objets  LocalDate  et non des objets  LocalDateTime  .
Voici un code d'exemple depuis notre date fétiche.

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;

public class TemporalAdjustersTest {

	public static void main(String[] args) {
		//Toujours notre 25 Décembre 2018 a 13H37
		LocalDate ldt = LocalDate.of(2018, Month.DECEMBER, 25);
		
		//Le prochain samedi
		LocalDate prochainSamedi = ldt.with(TemporalAdjusters.next(DayOfWeek.SATURDAY));
		System.out.println(prochainSamedi);
		
		//Le troisième mardi du mois suivant
		//On ajoute un mois à notre date
		ldt = ldt.plus(1, ChronoUnit.MONTHS);
		//On en créer une nouvelle au premier jour du mois
		LocalDate ldt2 = LocalDate.of(ldt.getYear(), ldt.getMonth(), 1);
		//On avance de trois mardi
		LocalDate troisiemeMardi = String.valueOf(ldtLocalBis.getDayOfWeek()).equals("TUESDAY") ? 
        ldt2 
        .with(TemporalAdjusters.next(DayOfWeek.TUESDAY)) 
    .with(TemporalAdjusters.next(DayOfWeek.TUESDAY)) : ldt2.with(TemporalAdjusters.next(DayOfWeek.TUESDAY)) 
        .with(TemporalAdjusters.next(DayOfWeek.TUESDAY)) 
        .with(TemporalAdjusters.next(DayOfWeek.TUESDAY)); 
        System.out.println(troisiemeMardi);
		
	}

}

Ce qui nous donne :

2018-12-29
2019-01-15

Jusque là, tout va bien. Regardons maintenant comment nous pouvons gérer les fuseaux horaires.

Les objets ZoneId et ZoneDateTime

Vous l'aurez sans doute deviné mais, pour pouvoir récupérer une heure et une date en fonction d'un fuseau horaire nous allons devoir spécifier ce fuseau. Heureusement, la classe  ZoneId  contient un objet de type  Map  qui contient tous les fuseau que nous pouvons utiliser.

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Map;

public class TestZone {

	public static void main(String[] args) {
		Map<String, String> maps = ZoneId.SHORT_IDS;
		maps.values().stream().forEach((x) -> {System.out.println(x + " -- " + ZoneId.of(x).getRules());});
		
		//Et connaître notre fuseau
		System.out.println("");
		System.out.println("Fuseau horaire courant : "+ZoneId.systemDefault());
		System.out.println("Règle appliquer aux heures : " + ZoneId.systemDefault().getRules());
	}
}

Nous récupérons toutes les valeurs de la  Map  contenant les fuseaux et nous l'affichons ainsi que la règle à appliquer sur les heures. Ceci peut être fait en récupérant une instance de  ZoneId  grâce à sa méthode   of()  . Ceci permet de lister tous les fuseaux utilisables, que voici :

-10:00 -- ZoneRules[currentStandardOffset=-10:00]
Australia/Darwin -- ZoneRules[currentStandardOffset=+09:30]
Pacific/Guadalcanal -- ZoneRules[currentStandardOffset=+11:00]
America/Indiana/Indianapolis -- ZoneRules[currentStandardOffset=-05:00]
Africa/Cairo -- ZoneRules[currentStandardOffset=+02:00]
Asia/Tokyo -- ZoneRules[currentStandardOffset=+09:00]
America/Phoenix -- ZoneRules[currentStandardOffset=-07:00]
Pacific/Auckland -- ZoneRules[currentStandardOffset=+12:00]
Asia/Shanghai -- ZoneRules[currentStandardOffset=+08:00]
Australia/Sydney -- ZoneRules[currentStandardOffset=+10:00]
Africa/Addis_Ababa -- ZoneRules[currentStandardOffset=+03:00]
America/Puerto_Rico -- ZoneRules[currentStandardOffset=-04:00]
Asia/Dhaka -- ZoneRules[currentStandardOffset=+06:00]
America/Chicago -- ZoneRules[currentStandardOffset=-06:00]
Asia/Yerevan -- ZoneRules[currentStandardOffset=+04:00]
Africa/Harare -- ZoneRules[currentStandardOffset=+02:00]
Europe/Paris -- ZoneRules[currentStandardOffset=+01:00]
America/St_Johns -- ZoneRules[currentStandardOffset=-03:30]
America/Anchorage -- ZoneRules[currentStandardOffset=-09:00]
-05:00 -- ZoneRules[currentStandardOffset=-05:00]
Asia/Ho_Chi_Minh -- ZoneRules[currentStandardOffset=+07:00]
Pacific/Apia -- ZoneRules[currentStandardOffset=+13:00]
America/Sao_Paulo -- ZoneRules[currentStandardOffset=-03:00]
Asia/Karachi -- ZoneRules[currentStandardOffset=+05:00]
-07:00 -- ZoneRules[currentStandardOffset=-07:00]
America/Los_Angeles -- ZoneRules[currentStandardOffset=-08:00]
Asia/Kolkata -- ZoneRules[currentStandardOffset=+05:30]
America/Argentina/Buenos_Aires -- ZoneRules[currentStandardOffset=-03:00]

Fuseau horaire courant : Europe/Paris
Règle appliquer aux heures : ZoneRules[currentStandardOffset=+01:00]

Maintenant que nous connaissons cela, nous allons pouvoir jouer avec des dates. Dans l'exemple qui suit, j'ai utilisé trois fuseau différents : Europe/Paris, Asia/Tokyo et America/Anchorage.

Voici un code qui parse une date/heure et qui nous donne sa valeur en fonction d'un fuseau horaire :

LocalDateTime ldt = LocalDateTime.parse("2018-01-01T01:33:00");
List<ZoneId> lzi = Arrays.asList(
		ZoneId.of("Europe/Paris"),
		ZoneId.of("Asia/Tokyo"),
		ZoneId.of("America/Anchorage")
);	

lzi	.stream()
	.forEach((x) -> {
		System.out.println(x + " : \t" + ldt.atZone(x).toInstant());
	});

J'ai volontairement choisi un moment ou la date est complètement différente en fonction du fuseau, ainsi, nous obtenons ceci :

Europe/Paris : 2018-01-01T00:33:00Z
Asia/Tokyo : 2017-12-31T10:33:00Z
America/Anchorage : 2018-01-01T16:33:00Z

Nous sommes encore en 2017 à Tokyo...
Et voilà, nous avons fait le tour le l'API time de Java 8 !

En résumé

  • Cette nouvelle API simplifie énormément la gestion des dates dans Java.

  • Elle est apparu depuis Java 8.

  • Elle gère les différences de temps avec des fuseaux horaires.

  • Vous pouvez facilement ajouter ou retirer du temps avec cette API.

Exemple de certificat de réussite
Exemple de certificat de réussite