Разлика между две дати в Java

1. Общ преглед

В това бързо писане ще проучим множество възможности за изчисляване на разликата между две дати в Java.

2. Основна Java

2.1. Използване на java.util.Date за намиране на разликата в дни

Нека започнем с използването на основните Java API, за да направим изчислението и да определим броя на дните между двете дати:

@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. Използване на java.time.temporal.ChronoUnit за намиране на разликата

API на времето в Java 8 представлява единица дата-час, например секунди или дни, използвайки интерфейса TemporalUnit . Всяка единица осигурява изпълнение за метод, посочен между, за да се изчисли времето между два времеви обекта от гледна точка на тази конкретна единица .

Например, за да се изчислят секундите между две LocalDateTime s:

@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 предоставя набор от конкретни времеви единици чрез внедряване на интерфейса TemporalUnit . Тя е силно препоръчително да статично внос на ChronoUnit ENUM стойности за постигане на по-добра четливост:

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

Също така, можем да предадем всеки два съвместими временни обекта на метода между , дори ZonedDateTime .

Страхотното при ZonedDateTime е, че изчислението ще работи, дори ако са настроени на различни часови зони:

@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. Използване на java.time.temporal.Temporal # до ()

Всяко Времеви обект, като LOCALDATE или ZonedDateTime, осигурява до метод за изчисляване на период от време, докато друг във времето по отношение на определен блок:

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

В Времеви # докато и TemporalUnit # между два различни интерфейси за същата функционалност.

2.4. Използване на java.time.Duration и java.time.Period

В Java 8 API за време въведе два нови класа: Продължителност и Период .

Ако искаме да изчислим разликата между две дати и часове в период от време (час, минути или секунди), можем да използваме класа Продължителност :

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

Трябва обаче да бъдем предпазливи, ако се опитаме да използваме клас Period, за да представим разликата между две дати .

Един пример ще го обясни бързо. Нека изчислим колко дни между две дати, като използваме периода 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); }

Ако пуснем теста по-горе, той ще премине. Може да мислим, че класът Period е удобен за решаване на нашия проблем. Дотук добре.

Ако този начин работи с разлика от шест дни, няма да се съмняваме, че ще работи и 60 дни.

След това нека променим 6 в теста по-горе на 60 и да видим какво ще се случи:

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

Сега, ако пуснем теста отново, ще видим:

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

Ами сега! Защо класът Period отчете разликата като 29 дни?

Това е така, защото класът Period представлява период от време, базиран на датата, във формат „ x години, y месеци и z дни “. Когато извикаме неговия метод getDays () , той връща само частта „ z дни “.

Следователно, периодът на обекта в горния тест съдържа стойността: „ 0 години, 1 месец и 29 дни “:

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

Ако искаме да изчислим разликата в дни, използвайки API на Java 8 Time, методът ChronoUnit.DAYS.between () е най-ясният начин.

3. Външни библиотеки

3.1. JodaTime

Също така можем да направим относително ясна реализация с JodaTime :

 joda-time joda-time 2.9.9 

Можете да получите най-новата версия на Joda-time от Maven Central.

Дело 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); } 

По същия начин с 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. Дата4J

Date4j също така осигурява лесно и просто изпълнение, без да се отбележи, че в този случай трябва изрично да предоставим TimeZone.

Нека започнем с зависимостта на Maven:

 com.darwinsys hirondelle-date4j 1.5.1 

Ето бърз тест, работещ със стандартния 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. Заключение

В този урок илюстрирахме няколко начина за изчисляване на разликата между датите (със и без време), както в обикновена Java, така и с помощта на външни библиотеки.

Пълният изходен код на статията е достъпен в GitHub.