Days Between Dates in Java 8

I would like to address a problem that is quite topical at the moment: calculating the number of days between two events. The new Date-Time API in Java 8 makes this process much simpler than it used to be.

The Date-Time API includes the interface java.time.temporal.TemporalUnit, which is implemented by the enum ChronoUnit in the same package. The between method on that interface takes two TemporalUnit instances and returns a long.

long between(Temporal temporal1Inclusive,
             Temporal temporal2Exclusive)

The start and end times must be of compatible types. The implementation converts the second argument to be an instance of the first type before calculating the amount. The result is negative if the second argument occurs before the first argument.

The return value is the number of “units” between the arguments. This becomes convenient when using the constants in the ChronoUnit enum.

For example, say you want to know how many days you need to wait until a particular date. Since you’re interested in days, use the ChronoUnit.DAYS constant from the enum.

import java.time.LocalDate;

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

public class DaysToElection {
    public static void main(String[] args) {
        LocalDate electionDay = LocalDate.of(2020, Month.NOVEMBER, 3);
        LocalDate today = LocalDate.now();

        System.out.printf("%d days to go...%n",
            DAYS.between(today, electionDay));
    }
}

Since the between method is invoked on the DAYS enum value, this will return the number of days. Other constants in ChronoUnit include HOURS, WEEKS, MONTHS, YEARS, DECADES, CENTURIES, and more (including, believe it or not, FOREVER; if you ever need that value, please send me a message — I’d love to know what the use case was).

At the time of this writing, the result is 1377 days. That’s too many to easily process, but it’s easy enough to use the additional constants to convert them to something more practical.

import java.time.LocalDate;
import java.time.Month;

import static java.time.temporal.ChronoUnit.DAYS;
import static java.time.temporal.ChronoUnit.MONTHS;
import static java.time.temporal.ChronoUnit.YEARS;

public class DaysToElection {
    private static String pluralize(long num) {
        return num == 1 ? "" : "s";
    }

    public static void main(String[] args) {
        LocalDate electionDay = LocalDate.of(2020, Month.NOVEMBER, 3);
        LocalDate today = LocalDate.now();

        System.out.printf("%d days to go...%n",
            DAYS.between(today, electionDay));

        long years  = YEARS.between(today, electionDay);
        long months = MONTHS.between(today.plusYears(years), electionDay);
        long days   = DAYS.between(
            today.plusYears(years).plusMonths(months), electionDay);

        System.out.printf("%d year%s, %d month%s, and %d day%s%n",
                years,  pluralize(years),
                months, pluralize(months),
                days,   pluralize(days));
    }
}

The output of this code is 3 years, 9 months, and 8 days, which is at least more intuitive. Feel free to adjust your time period to suit.

The java.time.temporal.TimeUnit interface is implemented by LocalDate, LocalTime, LocalDateTime, and many other classes. It has a method called until, whose behavior is entirely equivalent to between. It returns a long in the proper units.

There is an alternative, however, if the instance is of type LocalDate (or analogously LocalTime or LocalDateTime), whose methods are declared in the ChronoLocalDate (or ChronoLocalTime or ChronoLocalDateTime) interfaces. In that case there is an overload of until that returns a Period.

// In java.time.LocalDate
Period	until(ChronoLocalDate endDateExclusive)

In the example above, the returned Period prints at P3Y9M8D directly, which can be decomposed in a similar manner.

Period until = today.until(electionDay);

years  = until.getYears();
months = until.getMonths();
days   = until.getDays();
System.out.printf("%d year%s, %d month%s, and %d day%s%n",
        years,  pluralize(years),
        months, pluralize(months),
        days,   pluralize(days));

The Period class also has a static method called between that works the same way. The recommendation is to use whichever style makes the code more readable.

By the way, this is one of those cases where using Groovy doesn’t really simplify the code, but for the record, here’s the Groovy version:

import java.time.LocalDate
import java.time.Month
import java.time.Period

import static java.time.temporal.ChronoUnit.DAYS
import static java.time.temporal.ChronoUnit.MONTHS
import static java.time.temporal.ChronoUnit.YEARS

String pluralize(long num) { num == 1 ? '' : 's' }

LocalDate electionDay = LocalDate.of(2020, Month.NOVEMBER, 3)
LocalDate today = LocalDate.now()

// Using "between"
println "${DAYS.between(today, electionDay)} days to go..."

long years  = YEARS.between(today, electionDay)
long months = MONTHS.between(today.plusYears(years), electionDay)
long days   = DAYS.between(today.plusYears(years).plusMonths(months), electionDay)
println "$years year${pluralize(years)}, " +
    "$months month${pluralize(months)}, and " +
    "$days day${pluralize(days)}"

// Using "until"
Period until = today.until(electionDay)
assert today.until(electionDay).toString() == 'P3Y9M8D'

println "$years year${pluralize(until.years)}, " +
    "$months month${pluralize(until.months)}, and " +
    "$days day${pluralize(until.days)}"

So there you have it, and now I have a quick utility I can run whenever I need to. Which is every day right now, if not once an hour. Ugh.

About Ken Kousen
I teach software development training courses. I specialize in all areas of Java and XML, from EJB3 to web services to open source projects like Spring, Hibernate, Groovy, and Grails. Find me on Google+ I am the author of "Making Java Groovy", a Java / Groovy integration book published by Manning in the Fall of 2013, and "Gradle Recipes for Android", published by O'Reilly in 2015.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: