Java Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

In this quick write-up, we'll explore multiple possibilities of calculating the difference between two dates in Java.

Further reading:

Increment Date in Java

An overview of various core and 3rd party methods for adding days to a date

Check If a String Is a Valid Date in Java

Have a look at different ways to check if a String is a valid date in Java

2. Core Java

2.1. Using java.util.Date to Find the Difference in Days

Let's start by using the core Java APIs to do the calculation and determine the number of days between the two 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. Using java.time.temporal.ChronoUnit to Find the Difference

The Time API in Java 8 represents a unit of date-time, e.g. seconds or days, using TemporalUnit interface. Each unit provides an implementation for a method named between to calculate the amount of time between two temporal objects in terms of that specific unit.

For example, in order to calculate the seconds between two LocalDateTimes:

@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 provides a set of concrete time units by implementing the TemporalUnit interface. It's highly recommended to static import the ChronoUnit enum values to achieve better readability:

import static java.time.temporal.ChronoUnit.SECONDS;

// omitted
long diff = SECONDS.between(now, tenSecondsLater);

Also, we can pass any two compatible temporal objects to the between method, even the ZonedDateTime.

What's great about ZonedDateTime is that the calculation will work even if they are set to different timezones:

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

Any Temporal object, such as LocalDate or ZonedDateTime, provides an until method to calculate the amount of time until another Temporal in terms of the specified unit:

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

The Temporal#until and TemporalUnit#between are two different APIs for the same functionality.

2.4. Using java.time.Duration and java.time.Period

In Java 8, the Time API introduced two new classes: Duration and Period.

If we want to calculate the difference between two date-times in a time-based (hour, minutes, or seconds) amount of time, we can use the Duration class:

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

However, we should be wary of a pitfall if we try using the Period class to represent the difference between two dates.

An example will explain it quickly. Let's calculate how many days between two dates using the Period class:

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

If we run the test above, it'll pass. We may think the Period class is convenient to solve our problem. So far, so good.

If this way works with a difference of six days, we won't doubt it'll work for 60 days as well.

Next, let's change the 6 in the test above to 60, and see what happens:

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

Now, if we run the test again, we'll see:

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

Oops! Why did the Period class report the difference as 29 days?

This is because the Period class represents a date-based amount of time in the format of “x years, y months and z days“. When we call its getDays() method, it returns only the “z days” part.

Therefore, the period object in the test above holds the value: “0 years, 1 month and 29 days“:

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

If we want to calculate the difference in days using Java 8's Time API, the ChronoUnit.DAYS.between() method is the most straightforward way.

3. External Libraries

3.1. JodaTime

We can also do a relatively straightforward implementation with JodaTime:

<dependency>
    <groupId>joda-time</groupId>
    <artifactId>joda-time</artifactId>
    <version>2.9.9</version>
</dependency>

You can get the latest version of Joda-time from Maven Central.

LocalDate case:

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

Similarly, with LocalDateTime it is:

@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 also provides a straightforward and simple implementation, without the note that, in this case, we need to explicitly provide a TimeZone.

Let's start with the Maven dependency:

<dependency>
    <groupId>com.darwinsys</groupId>
    <artifactId>hirondelle-date4j</artifactId>
    <version>1.5.1</version>
</dependency>

Here's a quick test working with the 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. Conclusion

In this tutorial, we illustrated a few ways of calculating the difference between dates (with and without time), both in plain Java as well as using external libraries.

The full source code of the article is available over on GitHub.
Java bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Bineli Hervé
Bineli Hervé
3 years ago

Very interesting tutorial. Keep doing this great job

Comments are closed on this article!