Differenza tra due date in Java

1. Panoramica

In questo breve articolo, esploreremo molteplici possibilità di calcolare la differenza tra due date in Java.

2. Core Java

2.1. Utilizzo di java.util.Date per trovare la differenza in giorni

Iniziamo utilizzando le API Java di base per eseguire il calcolo e determinare il numero di giorni tra le due date:

@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. Utilizzo di java.time.temporal.ChronoUnit per trovare la differenza

L'API Time in Java 8 rappresenta un'unità di data-ora, ad esempio secondi o giorni, utilizzando l' interfaccia TemporalUnit . Ogni unità fornisce un'implementazione per un metodo denominato tra per calcolare la quantità di tempo tra due oggetti temporali in termini di quella specifica unità .

Ad esempio, per calcolare i secondi tra due 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 fornisce una serie di unità di tempo concrete implementando l' interfaccia TemporalUnit . Si raccomanda di importazione statica i ChronoUnit enum valori per ottenere una migliore leggibilità:

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

Inoltre, possiamo passare qualsiasi due oggetti temporali compatibili al metodo between , anche ZonedDateTime .

La cosa fantastica di ZonedDateTime è che il calcolo funzionerà anche se sono impostati su fusi orari diversi:

@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. Utilizzo di java.time.temporal.Temporal # until ()

Qualsiasi oggetto temporale , come LocalDate o ZonedDateTime, fornisce un metodo until per calcolare la quantità di tempo fino a un altro temporale in termini di unità specificata:

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

Il temporale # fino a quando e TemporalUnit # fra sono due API diverse per la stessa funzionalità.

2.4. Utilizzando java.time.Duration e java.time.Period

In Java 8, l'API Time ha introdotto due nuove classi: Duration e Period .

Se vogliamo calcolare la differenza tra due date-ora in una quantità di tempo basata sul tempo (ora, minuti o secondi), possiamo usare 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); }

Tuttavia, dovremmo diffidare di una trappola se proviamo a utilizzare la classe Period per rappresentare la differenza tra due date .

Un esempio lo spiegherà rapidamente. Calcoliamo quanti giorni tra due date utilizzando 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); }

Se eseguiamo il test sopra, passerà. Potremmo pensare che la classe Period sia conveniente per risolvere il nostro problema. Fin qui tutto bene.

Se in questo modo funziona con una differenza di sei giorni, non dubiteremo che funzionerà anche per 60 giorni.

Quindi, cambiamo il 6 nel test sopra a 60 e vediamo cosa succede:

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

Ora, se eseguiamo di nuovo il test, vedremo:

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

Oops! Perché la classe Period ha riportato la differenza di 29 giorni?

Questo perché la classe Period rappresenta una quantità di tempo basata sulla data nel formato " x anni, y mesi e z giorni ". Quando chiamiamo il suo metodo getDays () , restituisce solo la parte " z days ".

Pertanto, l' oggetto del periodo nel test di cui sopra contiene il valore: " 0 anni, 1 mese e 29 giorni ":

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

Se vogliamo calcolare la differenza in giorni utilizzando l'API Time di Java 8, il metodo ChronoUnit.DAYS.between () è il modo più diretto.

3. Biblioteche esterne

3.1. JodaTime

Possiamo anche eseguire un'implementazione relativamente semplice con JodaTime :

 joda-time joda-time 2.9.9 

È possibile ottenere l'ultima versione di Joda-time da Maven Central.

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

Allo stesso modo, con LocalDateTime è:

@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 fornisce anche un'implementazione diretta e semplice, senza la nota che, in questo caso, dobbiamo fornire esplicitamente un fuso orario .

Cominciamo con la dipendenza Maven:

 com.darwinsys hirondelle-date4j 1.5.1 

Ecco un rapido test che funziona con lo standard DateTime :

@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. Conclusione

In questo tutorial, abbiamo illustrato alcuni modi per calcolare la differenza tra le date (con e senza tempo), sia in Java semplice che utilizzando librerie esterne.

Il codice sorgente completo dell'articolo è disponibile su GitHub.