1. Introduction

In this tutorial, we'll focus on how to compare dates using the Java 8 Date/Time API. We'll dive into different methods to check whether two dates are equal and how to compare dates.

2. Comparing Dates

The basic way to express a date in Java is LocalDate. Let's consider two LocalDate object instances, representing the 10th of August 2019 and the 1st of July 2019:

LocalDate firstDate = LocalDate.of(2019, 8, 10);
LocalDate secondDate = LocalDate.of(2019, 7, 1);

*We're going to compare two LocalDate objects by utilizing the isAfter(), isBefore(), and isEqual() methods, as well as equals() and compareTo()*.

We use the isAfter() method to check if the date instance is after the other specified date. Therefore, the next JUnit assertion will pass:

assertThat(firstDate.isAfter(secondDate), is(true));

Analogously, the method isBefore() checks if the date instance is before the other specified date:

assertThat(firstDate.isBefore(secondDate), is(false));

The method isEqual() checks if a date represents the same point on the local timeline as the other specified date:

assertThat(firstDate.isEqual(firstDate), is(true));
assertThat(firstDate.isEqual(secondDate), is(false));

2.1. Comparing Dates Using the Comparable Interface

The equals() method will give the same result as isEqual(), but only if the argument passed is of the same type (in this case, LocalDate):

assertThat(firstDate.equals(secondDate), is(false));

The isEqual() method can be used instead to compare with objects of a different type, such as JapaneseDate, ThaiBuddhistDate, etc.

We can compare two date instances by using the compareTo() method, as defined by the Comparable interface:

assertThat(firstDate.compareTo(secondDate), is(1));
assertThat(secondDate.compareTo(firstDate), is(-1));

3. Comparing Date Instances Containing the Time Component

This section will explain how to compare two LocalDateTime instances. LocalDateTime instances contain the date as well as the time component.

*Similarly to LocalDate, we're comparing two LocalDateTime instances with the methods isAfter(), isBefore() and isEqual()*. Additionally, equals() and compareTo() can be used in a similar fashion as described for LocalDate.

Likewise, we can use the same methods for comparing two ZonedDateTime instances. Let's compare 8:00 local time in New York and 14:00 local time in Berlin, on the same day:

ZonedDateTime timeInNewYork = 
  ZonedDateTime.of(2019, 8, 10, 8, 0, 0, 0, ZoneId.of("America/New_York"));
ZonedDateTime timeInBerlin = 
  ZonedDateTime.of(2019, 8, 10, 14, 0, 0, 0, ZoneId.of("Europe/Berlin"));

assertThat(timeInNewYork.isAfter(timeInBerlin), is(false));
assertThat(timeInNewYork.isBefore(timeInBerlin), is(false));
assertThat(timeInNewYork.isEqual(timeInBerlin), is(true));

Although both ZonedDateTime instances represent the same moment in time, they do not represent equal Java objects. They have different LocalDateTime and ZoneId fields internally:

assertThat(timeInNewYork.equals(timeInBerlin), is(false)); 
assertThat(timeInNewYork.compareTo(timeInBerlin), is(-1));

4. Additional Comparisons

Let's create a simple utility class for slightly more complex comparisons.

Firstly, we'll check if instances of LocalDateTime and LocalDate are on the same day:

public static boolean isSameDay(LocalDateTime timestamp, 
  LocalDate localDateToCompare) {
    return timestamp.toLocalDate().isEqual(localDateToCompare);
}

Secondly, we'll check if two instances of LocalDateTime are on the same day:

public static boolean isSameDay(LocalDateTime timestamp, 
  LocalDateTime timestampToCompare) {
    return timestamp.truncatedTo(DAYS)
      .isEqual(timestampToCompare.truncatedTo(DAYS));
}

The truncatedTo(TemporalUnit) method truncates a date on the given level, which in our example is a day.

Thirdly, we can implement a comparison at the level of an hour:

public static boolean isSameHour(LocalDateTime timestamp, 
  LocalDateTime timestampToCompare) {
    return timestamp.truncatedTo(HOURS)
      .isEqual(timestampToCompare.truncatedTo(HOURS));
}

Finally, in a similar way, we can check if two ZonedDateTime instances happen within the same hour:

public static boolean isSameHour(ZonedDateTime zonedTimestamp, 
  ZonedDateTime zonedTimestampToCompare) {
    return zonedTimestamp.truncatedTo(HOURS)
      .isEqual(zonedTimestampToCompare.truncatedTo(HOURS));
}

We can see that two ZonedDateTime objects are actually happening within the same hour, even if their local times are different (8:30 and 14:00, respectively):

ZonedDateTime zonedTimestamp = 
  ZonedDateTime.of(2019, 8, 10, 8, 30, 0, 0, ZoneId.of("America/New_York"));
ZonedDateTime zonedTimestampToCompare = 
  ZonedDateTime.of(2019, 8, 10, 14, 0, 0, 0, ZoneId.of("Europe/Berlin"));

assertThat(DateTimeComparisonUtils.
  isSameHour(zonedTimestamp, zonedTimestampToCompare), is(true));

5. Comparison in the Old Java Date API

Before Java 8, we had to use java.util.Date and java.util.Calendar classes for manipulating date/time information. The design of the old Java Date API has many flaws, such as being complex and not thread-safe. The java.util.Date instance represents an “instant in time” and not a real date.

One of the solutions was to use the Joda Time library. Since the release of Java 8, it is recommended to migrate to the Java 8 Date/Time API.

*Similarly to LocalDate and LocalDateTime, both java.util.Date and java.util.Calendar objects have after(), before(), compareTo() and equals() methods for comparing two date instances*. The dates are compared as the instants in time, on the level of a millisecond:

Date firstDate = toDate(LocalDateTime.of(2019, 8, 10, 0, 00, 00));
Date secondDate = toDate(LocalDateTime.of(2019, 8, 15, 0, 00, 00));

assertThat(firstDate.after(secondDate), is(false));
assertThat(firstDate.before(secondDate), is(true));
assertThat(firstDate.compareTo(secondDate), is(-1));
assertThat(firstDate.equals(secondDate), is(false));

For more complex comparisons, we can use DateUtils from the Apache Commons Lang library. This class contains many handy methods for dealing with Date and Calendar objects:

public static boolean isSameDay(Date date, Date dateToCompare) {
    return DateUtils.isSameDay(date, dateToCompare);
}

public static boolean isSameHour(Date date, Date dateToCompare) {
    return DateUtils.truncatedEquals(date, dateToCompare, Calendar.HOUR);
}

To compare date objects originating from the different APIs, we should first do a proper conversion and only then apply the comparison. We can find more details in our Convert Date to LocalDate or LocalDateTime and Back tutorial.

6. Conclusion

In this article, we've explored different ways of comparing date instances in Java.

The Java 8 Date/Time classes have rich APIs for comparing dates, with or without time and time zones. We've also seen how to compare dates on the granularity of a day, hour, minute, etc.

All of the code snippets mentioned in the article, including additional examples, are available over on GitHub.