Java 8 Date – LocalDate, LocalDateTime, Instant

L’API Date et Heure est l’une des plus grandes fonctionnalités de la version Java 8. Java manquait d’une approche cohérente pour les dates et heures depuis le début, et l’API Date et Heure de Java 8 est un ajout bienvenu aux API Java de base.

Pourquoi avons-nous besoin de la nouvelle API Java Date et Heure ?

Avant de commencer à examiner l’API Date et Heure de Java 8, voyons pourquoi nous avons besoin d’une nouvelle API pour cela. Il y a eu plusieurs problèmes avec les classes de date et d’heure existantes en Java, certains d’entre eux sont les suivants :

  • Les classes Java Date et Heure ne sont pas définies de manière cohérente, nous avons la classe Date à la fois dans les packages java.util et java.sql. De plus, les classes de formatage et d’analyse sont définies dans le package java.text.
  • java.util.Date contient à la fois des valeurs de date et d’heure alors que java.sql.Date ne contient que des valeurs de date. Avoir cela dans le package java.sql n’a aucun sens. De plus, les deux classes ont le même nom, ce qui est une conception très mauvaise en soi.
  • Il n’y a pas de classes clairement définies pour le temps, le timestamp, le formatage et l’analyse. Nous avons la classe abstraite java.text.DateFormat pour les besoins d’analyse et de formatage. Habituellement, la classe SimpleDateFormat est utilisée pour l’analyse et le formatage.
  • Toutes les classes Date sont mutables, donc elles ne sont pas thread-safe. C’est l’un des plus gros problèmes avec les classes Date et Calendar de Java.
  • La classe Date ne fournit pas d’internationalisation, il n’y a pas de support de fuseau horaire. C’est pourquoi les classes java.util.Calendar et java.util.TimeZone ont été introduites, mais elles présentent également tous les problèmes énumérés ci-dessus.

Il existe d’autres problèmes avec les méthodes définies dans les classes Date et Calendar, mais les problèmes ci-dessus montrent clairement qu’une API Date Time robuste était nécessaire en Java. C’est pourquoi Joda Time a joué un rôle clé en tant que remplacement de qualité pour les besoins de Date Time en Java.

Principes de conception de Java 8 Date Time

L’API Java 8 Date Time est une implémentation de JSR-310. Elle est conçue pour surmonter tous les défauts des implémentations de date et heure héritées. Certains des principes de conception de la nouvelle API Date Time sont les suivants :

  1. Immutabilité: Toutes les classes de la nouvelle API Date-Time sont immuables et adaptées aux environnements multi-threads.

  2. Séparation des préoccupations: La nouvelle API sépare clairement entre la date et l’heure lisibles par l’homme et le temps machine (horodatage Unix). Elle définit des classes séparées pour Date, Time, DateTime, Timestamp, Timezone, etc.

  3. Clarté: Les méthodes sont clairement définies et effectuent la même action dans toutes les classes. Par exemple, pour obtenir l’instance actuelle, nous avons la méthode now(). Il y a des méthodes format() et parse() définies dans toutes ces classes plutôt que d’avoir une classe séparée pour elles.

    Toutes les classes utilisent le patron de conception Factory et le patron de conception Strategy pour une meilleure gestion. Une fois que vous avez utilisé les méthodes dans l’une des classes, travailler avec d’autres classes ne sera pas difficile.

  4. Opérations utilitaires: Toutes les nouvelles classes API de date-heure sont accompagnées de méthodes pour effectuer des tâches courantes, telles que l’ajout, la soustraction, le formatage, l’analyse, l’obtention de la partie séparée dans la date/heure, etc.

  5. Extensible: La nouvelle API Date Heure fonctionne sur le système de calendrier ISO-8601 mais nous pouvons également l’utiliser avec d’autres calendriers non ISO.

Forfaits API Date Heure

Java 8 Date Heure API se compose des forfaits suivants.

  1. java.time: Il s’agit du forfait de base de la nouvelle API Date Heure Java. Toutes les principales classes de base font partie de ce forfait, telles que LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration, etc. Toutes ces classes sont immuables et thread-safe. La plupart du temps, ces classes seront suffisantes pour gérer les exigences courantes.
  2. java.time.chrono: Ce forfait définit des API génériques pour les systèmes de calendrier non ISO. Nous pouvons étendre la classe AbstractChronology pour créer notre propre système de calendrier.
  3. java.time.format: Ce package contient des classes utilisées pour le formatage et l’analyse des objets date-heure. La plupart du temps, nous ne les utilisons pas directement car les classes principales du package java.time fournissent des méthodes de formatage et d’analyse.
  4. java.time.temporal: Ce package contient des objets temporels et nous pouvons l’utiliser pour trouver des dates ou heures spécifiques liées aux objets date/heure. Par exemple, nous pouvons les utiliser pour trouver le premier ou le dernier jour du mois. Vous pouvez facilement identifier ces méthodes car elles ont toujours le format « withXXX ».
  5. Package java.time.zone: Ce package contient des classes pour prendre en charge les différents fuseaux horaires et leurs règles.

Exemples de classes de l’API de date et heure Java 8

Nous avons examiné la plupart des parties importantes de l’API de date et heure Java. Il est maintenant temps d’examiner les classes les plus importantes de l’API de date et heure avec des exemples.

1. LocalDate

LocalDate est une classe immuable qui représente une date avec le format par défaut yyyy-MM-dd. Nous pouvons utiliser la méthode now() pour obtenir la date actuelle. Nous pouvons également fournir des arguments d’entrée pour l’année, le mois et le jour pour créer une instance de LocalDate.

Cette classe fournit une méthode surchargée pour now() où nous pouvons passer ZoneId pour obtenir des dates dans un fuseau horaire spécifique. Cette classe fournit la même fonctionnalité que java.sql.Date.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;

/** * Exemples de LocalDate * @author pankaj */
public class LocalDateExample {

	public static void main(String[] args) {
		
		//Date actuelle
		LocalDate today = LocalDate.now();
		System.out.println("Current Date="+today);
		
		//Création de LocalDate en fournissant des arguments d'entrée
		LocalDate firstDay_2014 = LocalDate.of(2014, Month.JANUARY, 1);
		System.out.println("Specific Date="+firstDay_2014);
		
		
		//Essayez de créer une date en fournissant des entrées invalides
		//LocalDate feb29_2014 = LocalDate.of(2014, Month.FEBRUARY, 29);
		//Exception dans le thread "main" java.time.DateTimeException: 
		//Date invalide '29 février' car '2014' n'est pas une année bissextile
		
		//Date actuelle à "Asia/Kolkata", vous pouvez l'obtenir depuis la documentation de ZoneId
		LocalDate todayKolkata = LocalDate.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: Identifiant de fuseau horaire inconnu : IST
		//LocalDate aujourd'huiIST = LocalDate.now(ZoneId.of("IST"));
		
		//Obtention de la date à partir de la date de base, c'est-à-dire le 01/01/1970
		LocalDate dateFromBase = LocalDate.ofEpochDay(365);
		System.out.println("365th day from base date= "+dateFromBase);
		
		LocalDate hundredDay2014 = LocalDate.ofYearDay(2014, 100);
		System.out.println("100th day of 2014="+hundredDay2014);
	}

}

Des explications sur les méthodes de LocalDate sont fournies dans les commentaires. Lorsque nous exécutons ce programme, nous obtenons la sortie suivante.

Current Date=2014-04-28
Specific Date=2014-01-01
Current Date in IST=2014-04-29
365th day from base date= 1971-01-01
100th day of 2014=2014-04-10

2. LocalTime

LocalTime est une classe immuable dont l’instance représente une heure au format lisible par l’homme. Son format par défaut est hh:mm:ss.zzz. Tout comme LocalDate, cette classe prend en charge les fuseaux horaires et permet de créer une instance en passant les heures, les minutes et les secondes en tant qu’arguments d’entrée.

package com.journaldev.java8.time;

import java.time.LocalTime;
import java.time.ZoneId;

/**
 * Exemples de LocalTime
 * @author pankaj
 *
 */
public class LocalTimeExample {

	public static void main(String[] args) {
		
		//Heure actuelle
		LocalTime time = LocalTime.now();
		System.out.println("Current Time="+time);
		
		//Création de LocalTime en fournissant des arguments d'entrée
		LocalTime specificTime = LocalTime.of(12,20,25,40);
		System.out.println("Specific Time of Day="+specificTime);
		
		
		//Essayez de créer une heure en fournissant des entrées non valides
		//LocalTime invalidTime = LocalTime.of(25,20);
		//Exception dans le thread "main" java.time.DateTimeException: 
		//Valeur non valide pour l'heure du jour (valeurs valides 0 - 23) : 25
		
		//Date actuelle à "Asia/Kolkata", vous pouvez l'obtenir à partir de la documentation de ZoneId
		LocalTime timeKolkata = LocalTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Time in IST="+timeKolkata);

		//java.time.zone.ZoneRulesException: ID de fuseau horaire inconnu : IST
		//LocalTime todayIST = LocalTime.now(ZoneId.of("IST"));
		
		//Obtention de la date à partir de la date de base c'est-à-dire 01/01/1970
		LocalTime specificSecondTime = LocalTime.ofSecondOfDay(10000);
		System.out.println("10000th second time= "+specificSecondTime);

	}

}

Sortie :

Current Time=15:51:45.240
Specific Time of Day=12:20:25.000000040
Current Time in IST=04:21:45.276
10000th second time= 02:46:40

3. LocalDateTime

LocalDateTime est un objet date-heure immuable qui représente une date-heure avec le format par défaut yyyy-MM-dd-HH-mm-ss.zzz. Il fournit une méthode de fabrique qui prend en entrée des arguments LocalDate et LocalTime pour créer une instance de LocalDateTime.

package com.journaldev.java8.time;

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

public class LocalDateTimeExample {

	public static void main(String[] args) {
		
		//Date actuelle
		LocalDateTime today = LocalDateTime.now();
		System.out.println("Current DateTime="+today);
		
		//Date actuelle en utilisant LocalDate et LocalTime
		today = LocalDateTime.of(LocalDate.now(), LocalTime.now());
		System.out.println("Current DateTime="+today);
		
		//Création de LocalDateTime en fournissant des arguments d'entrée
		LocalDateTime specificDate = LocalDateTime.of(2014, Month.JANUARY, 1, 10, 10, 30);
		System.out.println("Specific Date="+specificDate);
		
		
		//Essayer de créer une date en fournissant des entrées non valides
		//LocalDateTime feb29_2014 = LocalDateTime.of(2014, Month.FEBRUARY, 28, 25,1,1);
		//Exception in thread "main" java.time.DateTimeException: 
		//Valeur non valide pour l'heure du jour (valeurs valides 0 - 23): 25

		
		//Date actuelle en "Asia/Kolkata", vous pouvez l'obtenir à partir de la documentation de ZoneId
		LocalDateTime todayKolkata = LocalDateTime.now(ZoneId.of("Asia/Kolkata"));
		System.out.println("Current Date in IST="+todayKolkata);

		//java.time.zone.ZoneRulesException: Identifiant de fuseau horaire inconnu : IST
		//LocalDateTime todayIST = LocalDateTime.now(ZoneId.of("IST"));
		
		//Obtention de la date à partir de la date de base, c'est-à-dire 01/01/1970
		LocalDateTime dateFromBase = LocalDateTime.ofEpochSecond(10000, 0, ZoneOffset.UTC);
		System.out.println("10000th second time from 01/01/1970= "+dateFromBase);

	}

}

Dans tous les trois exemples, nous avons vu que si nous fournissons des arguments non valides pour la création de Date/Heure, alors cela lance une java.time.DateTimeException, qui est une RuntimeException donc nous n’avons pas besoin de l’attraper explicitement.

Nous avons également constaté que nous pouvons obtenir des données de date/heure en passant ZoneId, vous pouvez obtenir la liste des valeurs ZoneId prises en charge depuis sa JavaDoc. Lorsque nous exécutons la classe ci-dessus, nous obtenons la sortie suivante.

Current DateTime=2014-04-28T16:00:49.455
Current DateTime=2014-04-28T16:00:49.493
Specific Date=2014-01-01T10:10:30
Current Date in IST=2014-04-29T04:30:49.493
10000th second time from 01/01/1970= 1970-01-01T02:46:40

4. Instantané

La classe Instantané est utilisée pour travailler avec un format de temps lisible par machine. Instantané stocke la date et l’heure au format de timestamp UNIX.

package com.journaldev.java8.time;

import java.time.Duration;
import java.time.Instant;

public class InstantExample {

	public static void main(String[] args) {
		//Timestamp actuel
		Instant timestamp = Instant.now();
		System.out.println("Current Timestamp = "+timestamp);
		
		//Instantané à partir du timestamp
		Instant specificTime = Instant.ofEpochMilli(timestamp.toEpochMilli());
		System.out.println("Specific Time = "+specificTime);
		
		//Exemple de durée
		Duration thirtyDay = Duration.ofDays(30);
		System.out.println(thirtyDay);
	}

}

Sortie :

Current Timestamp = 2014-04-28T23:20:08.489Z
Specific Time = 2014-04-28T23:20:08.489Z
PT720H

Utilitaires API de Date Java 8

La plupart des classes de principes de date et d’heure fournissent diverses méthodes utilitaires telles que plus/moins jours, semaines, mois, etc. Il existe d’autres méthodes utilitaires pour ajuster la date en utilisant TemporalAdjuster et pour calculer la période entre deux dates.

package com.journaldev.java8.time;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Period;
import java.time.temporal.TemporalAdjusters;

public class DateAPIUtilities {

	public static void main(String[] args) {
		
		LocalDate today = LocalDate.now();
		
		//Obtenez l'année, vérifiez si c'est une année bissextile
		System.out.println("Year "+today.getYear()+" is Leap Year? "+today.isLeapYear());
		
		//Comparer deux LocalDate pour avant et après
		System.out.println("Today is before 01/01/2015? "+today.isBefore(LocalDate.of(2015,1,1)));
		
		//Créer LocalDateTime à partir de LocalDate
		System.out.println("Current Time="+today.atTime(LocalTime.now()));
		
		//opérations d'addition et de soustraction
		System.out.println("10 days after today will be "+today.plusDays(10));
		System.out.println("3 weeks after today will be "+today.plusWeeks(3));
		System.out.println("20 months after today will be "+today.plusMonths(20));

		System.out.println("10 days before today will be "+today.minusDays(10));
		System.out.println("3 weeks before today will be "+today.minusWeeks(3));
		System.out.println("20 months before today will be "+today.minusMonths(20));
		
		//Adapteurs temporels pour ajuster les dates
		System.out.println("First date of this month= "+today.with(TemporalAdjusters.firstDayOfMonth()));
		LocalDate lastDayOfYear = today.with(TemporalAdjusters.lastDayOfYear());
		System.out.println("Last date of this year= "+lastDayOfYear);
		
		Period period = today.until(lastDayOfYear);
		System.out.println("Period Format= "+period);
		System.out.println("Months remaining in the year= "+period.getMonths());		
	}
}

Sortie:

Year 2014 is Leap Year? false
Today is before 01/01/2015? true
Current Time=2014-04-28T16:23:53.154
10 days after today will be 2014-05-08
3 weeks after today will be 2014-05-19
20 months after today will be 2015-12-28
10 days before today will be 2014-04-18
3 weeks before today will be 2014-04-07
20 months before today will be 2012-08-28
First date of this month= 2014-04-01
Last date of this year= 2014-12-31
Period Format= P8M3D
Months remaining in the year= 8

Java 8 Analyse et Formatage des Dates

Il est très courant de formater une date dans différents formats, puis de convertir une chaîne en objets Date Time.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateParseFormatExample {

	public static void main(String[] args) {
		
		//Exemples de formatage
		LocalDate date = LocalDate.now();
		//format par défaut
		System.out.println("Default format of LocalDate="+date);
		//format spécifique
		System.out.println(date.format(DateTimeFormatter.ofPattern("d::MMM::uuuu")));
		System.out.println(date.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		
		LocalDateTime dateTime = LocalDateTime.now();
		//format par défaut
		System.out.println("Default format of LocalDateTime="+dateTime);
		//format spécifique
		System.out.println(dateTime.format(DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss")));
		System.out.println(dateTime.format(DateTimeFormatter.BASIC_ISO_DATE));
		
		Instant timestamp = Instant.now();
		//format par défaut
		System.out.println("Default format of Instant="+timestamp);
		
		//Exemples de conversion
		LocalDateTime dt = LocalDateTime.parse("27::Apr::2014 21::39::48",
				DateTimeFormatter.ofPattern("d::MMM::uuuu HH::mm::ss"));
		System.out.println("Default format after parsing = "+dt);
	}

}

Sortie:

Default format of LocalDate=2014-04-28
28::Apr::2014
20140428
Default format of LocalDateTime=2014-04-28T16:25:49.341
28::Apr::2014 16::25::49
20140428
Default format of Instant=2014-04-28T23:25:49.342Z
Default format after parsing = 2014-04-27T21:39:48

Support de date et heure hérité de l’API Java

Les classes de date/heure héritées sont utilisées dans presque toutes les applications, donc avoir une compatibilité ascendante est indispensable. C’est pourquoi il existe plusieurs méthodes utilitaires permettant de convertir les classes héritées en nouvelles classes et vice versa.

package com.journaldev.java8.time;

import java.time.Instant;
import java.time.LocalDateTime;
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 DateAPILegacySupport {

	public static void main(String[] args) {
		
		//Date vers Instant
		Instant timestamp = new Date().toInstant();
		//Maintenant, nous pouvons convertir Instant en LocalDateTime ou d'autres classes similaires
		LocalDateTime date = LocalDateTime.ofInstant(timestamp, 
						ZoneId.of(ZoneId.SHORT_IDS.get("PST")));
		System.out.println("Date = "+date);
		
		//Calendrier vers Instant
		Instant time = Calendar.getInstance().toInstant();
		System.out.println(time);
		//Fuseau horaire vers ZoneId
		ZoneId defaultZone = TimeZone.getDefault().toZoneId();
		System.out.println(defaultZone);
		
		//ZonedDateTime à partir d'un calendrier spécifique
		ZonedDateTime gregorianCalendarDateTime = new GregorianCalendar().toZonedDateTime();
		System.out.println(gregorianCalendarDateTime);
		
		//API de date vers les classes héritées
		Date dt = Date.from(Instant.now());
		System.out.println(dt);
		
		TimeZone tz = TimeZone.getTimeZone(defaultZone);
		System.out.println(tz);
		
		GregorianCalendar gc = GregorianCalendar.from(gregorianCalendarDateTime);
		System.out.println(gc);
		
	}

}

Sortie:

Date = 2014-04-28T16:28:54.340
2014-04-28T23:28:54.395Z
America/Los_Angeles
2014-04-28T16:28:54.404-07:00[America/Los_Angeles]
Mon Apr 28 16:28:54 PDT 2014
sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]]
java.util.GregorianCalendar[time=1398727734404,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="America/Los_Angeles",offset=-28800000,dstSavings=3600000,useDaylight=true,transitions=185,lastRule=java.util.SimpleTimeZone[id=America/Los_Angeles,offset=-28800000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=8,startDayOfWeek=1,startTime=7200000,startTimeMode=0,endMode=3,endMonth=10,endDay=1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=2,minimalDaysInFirstWeek=4,ERA=1,YEAR=2014,MONTH=3,WEEK_OF_YEAR=18,WEEK_OF_MONTH=5,DAY_OF_MONTH=28,DAY_OF_YEAR=118,DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=4,HOUR_OF_DAY=16,MINUTE=28,SECOND=54,MILLISECOND=404,ZONE_OFFSET=-28800000,DST_OFFSET=3600000]

Comme vous pouvez le voir, les méthodes toString() des classes héritées TimeZone et GregorianCalendar sont trop verbeuses et peu conviviales pour l’utilisateur.

Conclusion

I like this new Date Time API a lot. Some of the most used classes will be LocalDate and LocalDateTime. It’s very easy to work with the new classes. And, having similar methods that does a particular job makes it easy to find. It will take some time from moving legacy classes to new Date Time classes, but I believe it will be worth the time and effort.

Source:
https://www.digitalocean.com/community/tutorials/java-8-date-localdate-localdatetime-instant