Forskellen mellem to datoer i Java

1. Oversigt

I denne hurtige opskrivning undersøger vi flere muligheder for at beregne forskellen mellem to datoer i Java.

2. Core Java

2.1. Ved brug af java.util.Date at finde forskellen i dage

Lad os starte med at bruge Java-API'erne til at beregne og bestemme antallet af dage mellem de to datoer:

@Test offentlig ugyldighed givenTwoDatesBeforeJava8_whenDifferentiating_thenWeGetSix () kaster ParseException {SimpleDateFormat sdf = ny SimpleDateFormat ("MM / dd / åååå", Sprog. ENGLISH); Dato firstDate = sdf.parse ("06/24/2017"); Dato secondDate = sdf.parse ("06/30/2017"); lang diffInMillies = Math.abs (secondDate.getTime () - firstDate.getTime ()); lang diff = TimeUnit.DAYS.convert (diffInMillies, TimeUnit.MILLISECONDS); assertEquals (6, diff); }

2.2. Ved brug af java.time.temporal.ChronoUnit at finde forskellen

Time API i Java 8 repræsenterer en enhed af dato-tid, f.eks. sekunder eller dage ved hjælp af TemporalUnit interface. Hver enhed giver en implementering af en navngivet metode mellem at beregne tidsrummet mellem to tidsmæssige objekter i form af den specifikke enhed.

For eksempel for at beregne sekunderne mellem to LocalDateTimes:

@Test offentlig ugyldighed givenTwoDateTimesInJava8_whenDifferentiatingInSeconds_thenWeGetTen () {LocalDateTime nu = LocalDateTime.now (); LocalDateTime tenSecondsLater = now.plusSeconds (10); lang diff = ChronoUnit.SECONDS.between (nu, tenSecondsLater); assertEquals (10, diff); }

ChronoUnit giver et sæt konkrete tidsenheder ved at implementere TemporalUnit interface. Det anbefales stærkt statisk import det ChronoUnitenum værdier for at opnå bedre læsbarhed:

import af statisk java.time.temporal.ChronoUnit.SECONDS; // udeladt lang diff = SECONDS.between (nu, tenSecondsLater);

Vi kan også videregive to kompatible tidsmæssige objekter til mellem metode, selv den ZonedDateTime.

Hvad er godt ved ZonedDateTime er, at beregningen fungerer, selvom de er indstillet til forskellige tidszoner:

@Test offentlig ugyldighed givenTwoZonedDateTimesInJava8_whenDifferentiating_thenWeGetSix () {LocalDateTime ldt = LocalDateTime.now (); ZonedDateTime nu = ldt.atZone (ZoneId.of ("Amerika / Montreal")); ZonedDateTime sixMinutesBehind = nu .withZoneSameInstant (ZoneId.of ("Asien / Singapore")) .minusMinutes (6); lang diff = ChronoUnit.MINUTES.between (sixMinutesBehind, now); assertEquals (6, diff); } 

2.3. Ved brug af java.time.temporal.Temporal # indtil ()

Nogen Temporal objekt, såsom LocalDate eller ZonedDateTime, giver en så længe metode til at beregne tid, indtil en anden Temporal med hensyn til den specificerede enhed:

@Test offentlig ugyldighed givenTwoDateTimesInJava8_whenDifferentiatingInSecondsUsingUntil_thenWeGetTen () {LocalDateTime nu = LocalDateTime.now (); LocalDateTime tenSecondsLater = now.plusSeconds (10); lang diff = now.until (tenSecondsLater, ChronoUnit.SECONDS); assertEquals (10, diff); }

Det Temporal # indtil og TemporalUnit # mellem er to forskellige API'er til den samme funktionalitet.

2.4. Ved brug af java.time.Duration og java.time.Period

I Java 8 introducerede Time API to nye klasser: Varighed og Periode.

Hvis vi vil beregne forskellen mellem to dato-tider i en tidsbaseret (time, minutter eller sekunder) tid, kan vi bruge Varighed klasse:

@Test offentlig ugyldighed givenTwoDateTimesInJava8_whenDifferentiating_thenWeGetSix () {LocalDateTime nu = LocalDateTime.now (); LocalDateTime sixMinutesBehind = now.minusMinutes (6); Varighed varighed = Varighed. Imellem (nu, sixMinutesBehind); lang diff = Math.abs (duration.toMinutes ()); assertEquals (6, diff); }

Imidlertid, vi bør være forsigtige med en faldgrube, hvis vi prøver at bruge Periode klasse for at repræsentere forskellen mellem to datoer.

Et eksempel forklarer det hurtigt. Lad os beregne hvor mange dage mellem to datoer ved hjælp af Periode klasse:

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

Hvis vi kører testen ovenfor, vil den bestå. Vi tror måske, at Periode klasse er praktisk at løse vores problem. Så langt så godt.

Hvis denne måde fungerer med en forskel på seks dage, tvivler vi ikke på, at den også fungerer i 60 dage.

Lad os derefter ændre 6 i testen ovenfor til 60, og se hvad der sker:

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

Hvis vi nu kører testen igen, ser vi:

java.lang.AssertionError: Forventet: 60 Faktisk: 29

Ups! Hvorfor gjorde Periode klasse rapporterer forskellen som 29 dage?

Dette skyldes, at Periode klasse repræsenterer en datobaseret tidsperiode i formatet “x år, y måneder og z dage“. Når vi kalder det getDays () metode returnerer den kun “z dage" en del.

Derfor er den periode objekt i testen ovenfor har værdien: “0 år, 1 måned og 29 dage“:

@Test offentlig ugyldighed givenTwoDatesInJava8_whenUsingPeriod_thenWeGet0Year1Month29Days () {LocalDate aDate = LocalDate.of (2020, 9, 11); LocalDate sixtyDaysBehind = aDate.minusDays (60); Periode periode = Period.between (aDate, sixtyDaysBehind); int år = Math.abs (period.getYears ()); int måneder = Math.abs (period.getMonths ()); int dage = Math.abs (period.getDays ()); assertArrayEquals (ny int [] {0, 1, 29}, ny int [] {år, måneder, dage}); }

Hvis vi vil beregne forskellen i dage ved hjælp af Java 8s Time API, er ChronoUnit.DAYS.between () metoden er den mest ligefremme måde.

3. Eksterne biblioteker

3.1. JodaTime

Vi kan også foretage en relativt ligefrem implementering med JodaTime:

 joda-tid joda-tid 2.9.9 

Du kan få den nyeste version af Joda-time fra Maven Central.

LocalDate sag:

@Test offentligt ugyldigt givenTwoDatesInJodaTime_whenDifferentiating_thenWeGetSix () {org.joda.time.LocalDate nu = org.joda.time.LocalDate.now (); org.joda.time.LocalDate sixDaysBehind = now.minusDays (6); lang diff = Math.abs (Days.daysBetween (nu, sixDaysBehind) .getDays ()); assertEquals (6, diff); } 

Tilsvarende med LocalDateTime det er:

@Test offentlig ugyldighed givenTwoDateTimesInJodaTime_whenDifferentiating_thenWeGetSix () {org.joda.time.LocalDateTime nu = org.joda.time.LocalDateTime.now (); org.joda.time.LocalDateTime sixMinutesBehind = now.minusMinutes (6); lang diff = Math.abs (Minutes.minutesBetween (nu, sixMinutesBehind) .getMinutes ()); assertEquals (6, diff); } 

3.2. Dato4J

Dato4j giver også en ligetil og enkel implementering uden bemærkning om, at vi i dette tilfælde eksplicit skal levere en Tidszone.

Lad os starte med Maven-afhængigheden:

 com.darwinsys hirondelle-date4j 1.5.1 

Her er en hurtig test, der arbejder med standarden Dato tid:

@Test offentlig ugyldighed givenTwoDatesInDate4j_whenDifferentiating_thenWeGetSix () {DateTime nu = DateTime.now (TimeZone.getDefault ()); DateTime sixDaysBehind = now.minusDays (6); lang diff = Math.abs (now.numDaysFrom (sixDaysBehind)); assertEquals (6, diff); }

4. Konklusion

I denne vejledning illustrerede vi et par måder at beregne forskellen mellem datoer (med og uden tid), både i almindelig Java såvel som ved hjælp af eksterne biblioteker.

Den fulde kildekode for artiklen er tilgængelig på GitHub.

$config[zx-auto] not found$config[zx-overlay] not found