Différence entre deux dates en Java

1. Vue d'ensemble

Dans cet article rapide, nous explorerons plusieurs possibilités de calcul de la différence entre deux dates en Java.

2. Core Java

2.1. Utilisation de java.util.Date pour trouver la différence en jours

Commençons par utiliser les API Java de base pour faire le calcul et déterminer le nombre de jours entre les deux dates:

@Test public void givenTwoDatesBeforeJava8_whenDifferentiating_thenWeGetSix() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH); Date firstDate = sdf.parse("06/24/2017"); Date secondDate = sdf.parse("06/30/2017"); long diffInMillies = Math.abs(secondDate.getTime() - firstDate.getTime()); long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS); assertEquals(6, diff); }

2.2. Utilisation de java.time.temporal.ChronoUnit pour trouver la différence

L'API Time dans Java 8 représente une unité de date-heure, par exemple des secondes ou des jours, en utilisant l' interface TemporalUnit . Chaque unité fournit une implémentation pour une méthode nommée entre pour calculer la quantité de temps entre deux objets temporels en termes de cette unité spécifique .

Par exemple, pour calculer les secondes entre deux LocalDateTime :

@Test public void givenTwoDateTimesInJava8_whenDifferentiatingInSeconds_thenWeGetTen() { LocalDateTime now = LocalDateTime.now(); LocalDateTime tenSecondsLater = now.plusSeconds(10); long diff = ChronoUnit.SECONDS.between(now, tenSecondsLater); assertEquals(10, diff); }

ChronoUnit fournit un ensemble d'unités de temps concrètes en implémentant l' interface TemporalUnit . Il est fortement recommandé d' importer statique les ChronoUnit ENUM valeurs pour atteindre une meilleure lisibilité:

import static java.time.temporal.ChronoUnit.SECONDS; // omitted long diff = SECONDS.between(now, tenSecondsLater);

De plus, nous pouvons transmettre deux objets temporels compatibles à la méthode between , même ZonedDateTime .

Ce qui est génial avec ZonedDateTime, c'est que le calcul fonctionnera même s'ils sont définis sur des fuseaux horaires différents:

@Test public void givenTwoZonedDateTimesInJava8_whenDifferentiating_thenWeGetSix() { LocalDateTime ldt = LocalDateTime.now(); ZonedDateTime now = ldt.atZone(ZoneId.of("America/Montreal")); ZonedDateTime sixMinutesBehind = now .withZoneSameInstant(ZoneId.of("Asia/Singapore")) .minusMinutes(6); long diff = ChronoUnit.MINUTES.between(sixMinutesBehind, now); assertEquals(6, diff); } 

2.3. Utilisation de java.time.temporal.Temporal # jusqu'à ()

Tout objet temporel , tel que LocalDate ou ZonedDateTime, fournit une méthode jusqu'à pour calculer la durée jusqu'à un autre Temporal en termes de l'unité spécifiée:

@Test public void givenTwoDateTimesInJava8_whenDifferentiatingInSecondsUsingUntil_thenWeGetTen() { LocalDateTime now = LocalDateTime.now(); LocalDateTime tenSecondsLater = now.plusSeconds(10); long diff = now.until(tenSecondsLater, ChronoUnit.SECONDS); assertEquals(10, diff); }

Le Temporal # jusqu'à et le TemporalUnit # entre sont deux API différentes pour la même fonctionnalité.

2.4. Utilisation de java.time.Duration et java.time.Period

Dans Java 8, l'API Time a introduit deux nouvelles classes: Durée et Période .

Si nous voulons calculer la différence entre deux dates-heures dans une période de temps (heure, minutes ou secondes), nous pouvons utiliser la classe Duration :

@Test public void givenTwoDateTimesInJava8_whenDifferentiating_thenWeGetSix() { LocalDateTime now = LocalDateTime.now(); LocalDateTime sixMinutesBehind = now.minusMinutes(6); Duration duration = Duration.between(now, sixMinutesBehind); long diff = Math.abs(duration.toMinutes()); assertEquals(6, diff); }

Cependant, nous devons nous méfier d'un piège si nous essayons d'utiliser la classe Period pour représenter la différence entre deux dates .

Un exemple l'expliquera rapidement. Calculons le nombre de jours entre deux dates en utilisant la classe Period :

@Test public void givenTwoDatesInJava8_whenUsingPeriodGetDays_thenWorks() { LocalDate aDate = LocalDate.of(2020, 9, 11); LocalDate sixDaysBehind = aDate.minusDays(6); Period period = Period.between(aDate, sixDaysBehind); int diff = Math.abs(period.getDays()); assertEquals(6, diff); }

Si nous exécutons le test ci-dessus, il passera. Nous pouvons penser que la classe Period est pratique pour résoudre notre problème. Jusqu'ici tout va bien.

Si cette méthode fonctionne avec une différence de six jours, nous ne doutons pas qu'elle fonctionnera également pendant 60 jours.

Ensuite, changeons le 6 du test ci-dessus en 60 , et voyons ce qui se passe:

@Test public void givenTwoDatesInJava8_whenUsingPeriodGetDays_thenDoesNotWork() { LocalDate aDate = LocalDate.of(2020, 9, 11); LocalDate sixtyDaysBehind = aDate.minusDays(60); Period period = Period.between(aDate, sixtyDaysBehind); int diff = Math.abs(period.getDays()); assertEquals(60, diff); }

Maintenant, si nous exécutons à nouveau le test, nous verrons:

java.lang.AssertionError: Expected :60 Actual :29

Oops! Pourquoi la classe Période a-t -elle indiqué que la différence était de 29 jours?

Cela est dû au fait que la classe Period représente une durée basée sur la date au format « x années, y mois et z jours ». Lorsque nous appelons sa méthode getDays () , elle ne renvoie que la partie « z jours ».

Par conséquent, l' objet de période dans le test ci-dessus contient la valeur: " 0 an, 1 mois et 29 jours ":

@Test public void givenTwoDatesInJava8_whenUsingPeriod_thenWeGet0Year1Month29Days() { LocalDate aDate = LocalDate.of(2020, 9, 11); LocalDate sixtyDaysBehind = aDate.minusDays(60); Period period = Period.between(aDate, sixtyDaysBehind); int years = Math.abs(period.getYears()); int months = Math.abs(period.getMonths()); int days = Math.abs(period.getDays()); assertArrayEquals(new int[] { 0, 1, 29 }, new int[] { years, months, days }); }

Si nous voulons calculer la différence en jours à l'aide de l'API Time de Java 8, la méthode ChronoUnit.DAYS.between () est le moyen le plus simple.

3. Bibliothèques externes

3.1. JodaTime

Nous pouvons également faire une implémentation relativement simple avec JodaTime :

 joda-time joda-time 2.9.9 

Vous pouvez obtenir la dernière version de Joda-time auprès de Maven Central.

Cas LocalDate :

@Test public void givenTwoDatesInJodaTime_whenDifferentiating_thenWeGetSix() { org.joda.time.LocalDate now = org.joda.time.LocalDate.now(); org.joda.time.LocalDate sixDaysBehind = now.minusDays(6); long diff = Math.abs(Days.daysBetween(now, sixDaysBehind).getDays()); assertEquals(6, diff); } 

De même, avec LocalDateTime, c'est:

@Test public void givenTwoDateTimesInJodaTime_whenDifferentiating_thenWeGetSix() { org.joda.time.LocalDateTime now = org.joda.time.LocalDateTime.now(); org.joda.time.LocalDateTime sixMinutesBehind = now.minusMinutes(6); long diff = Math.abs(Minutes.minutesBetween(now, sixMinutesBehind).getMinutes()); assertEquals(6, diff); } 

3.2. Date4J

Date4j fournit également une implémentation simple et directe, sans noter que, dans ce cas, nous devons explicitement fournir une TimeZone.

Commençons par la dépendance Maven:

 com.darwinsys hirondelle-date4j 1.5.1 

Voici un test rapide fonctionnant avec le DateTime standard :

@Test public void givenTwoDatesInDate4j_whenDifferentiating_thenWeGetSix() { DateTime now = DateTime.now(TimeZone.getDefault()); DateTime sixDaysBehind = now.minusDays(6); long diff = Math.abs(now.numDaysFrom(sixDaysBehind)); assertEquals(6, diff); }

4. Conclusion

Dans ce tutoriel, nous avons illustré quelques façons de calculer la différence entre les dates (avec et sans heure), à ​​la fois en Java simple et en utilisant des bibliothèques externes.

Le code source complet de l'article est disponible à l'adresse over sur GitHub.