Aaron Gadberry

Help – v. helped, help·ing, helps

Was this site helpful?
My Amazon.com Wishlist

Find the Difference Between two Java Dates (Calendars)

17th August 2007 - By Aaron Gadberry

Well the first question to ask is what unit do you want the difference in. This is one of the reasons many people stick to incremental date math instead of math returning an int.

Lets say you want to do a basic age calculation. How old is Joe? His birthday is 7/29/1981. Well you could subtract 1981 from today, then determine if he had a birthday this year, which could mean comparing everything up to the milliseconds. The problem is, such a calculation is not as accurate as it would seem. This style method fails to preserve the validity of the passing of leap years, leap seconds, etc. If you asked how many days old Joe is then your answer would be something like 365 * (current – 1981) + number of days this year – number of days between Jan 1 and July 29. Well you forgot a few leap days in there, at least 5.

I don’t know about you, but inaccurate results bug me. So I went about researching the correct way to solve this problem. Turns out that adding through the Calendar add method preserves these little nuances. The way to solve this is to increment one date until it passes the second date, counting the times it is incremented.

What?!? But it will take forever to count the number of days that passed between Joe’s birthday and today! What if I needed to know the number of hours, seconds, or even worse, milliseconds? Well we can build in some optimization just for that.

The method I wrote has the following signature CalendarUtils.difference(Calendar c1, Calendar c2, int unit);. To figure out Joe’s age we would call it with something like CalendarUtils.difference(birthday, today, Calendar.YEAR);.

Enough with the small talk. Here’s the code. It happens to be part of an open source project I work on, JExel. Here is the most recent, complete CalendarUtils.java.

public static long difference(Calendar c1, Calendar c2, int unit) { differenceCheckUnit(unit); Map<Integer, Long> unitEstimates = differenceGetUnitEstimates(); Calendar first = (Calendar) c1.clone(); Calendar last = (Calendar) c2.clone(); long difference = c2.getTimeInMillis() - c1.getTimeInMillis(); long unitEstimate = unitEstimates.get(unit).longValue(); long increment = (long) Math.floor((double) difference / (double) unitEstimate); increment = Math.max(increment, 1); long total = 0; while (increment > 0) { add(first, unit, increment); if (first.after(last)) { add(first, unit, increment * -1); increment = (long) Math.floor(increment / 2); } else { total += increment; } } return total; } private static Map<Integer, Long> differenceGetUnitEstimates() { Map<Integer, Long> unitEstimates = new HashMap<Integer, Long>(); unitEstimates.put(Calendar.YEAR, 1000l * 60 * 60 * 24 * 365); unitEstimates.put(Calendar.MONTH, 1000l * 60 * 60 * 24 * 30); unitEstimates.put(Calendar.DAY_OF_MONTH, 1000l * 60 * 60 * 24); unitEstimates.put(Calendar.HOUR_OF_DAY, 1000l * 60 * 60); unitEstimates.put(Calendar.MINUTE, 1000l * 60); unitEstimates.put(Calendar.SECOND, 1000l); unitEstimates.put(Calendar.MILLISECOND, 1l); return unitEstimates; } private static void differenceCheckUnit(int unit) { List<Integer> validUnits = new ArrayList<Integer>(); validUnits.add(Calendar.YEAR); validUnits.add(Calendar.MONTH); validUnits.add(Calendar.DAY_OF_MONTH); validUnits.add(Calendar.HOUR_OF_DAY); validUnits.add(Calendar.MINUTE); validUnits.add(Calendar.SECOND); validUnits.add(Calendar.MILLISECOND); if (!validUnits.contains(unit)) { throw new RuntimeException( "CalendarUtils.difference one of these units Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND." ); } } public static void add(Calendar c, int unit, long increment) { while (increment > Integer.MAX_VALUE) { c.add(unit, Integer.MAX_VALUE); increment -= Integer.MAX_VALUE; } c.add(unit, (int) increment); }

You can find the JUnit test cases I run this method through here.

4 Responses to “Find the Difference Between two Java Dates (Calendars)”

  1. naren Says:

    neat idea to get over those leap year issues!

  2. Stucco Says:

    Updated the code 3/5/2008 to fix a bug where the estimated time was greater than the actual time passed, yet the time passed was sufficient to be a single unit. For example Feb 1st to March 1st. Thank you Tim for catching this bug.

  3. Naren Says:

    Hi
    I think you should use

    
    unitEstimates.put(Calendar.MONTH, 1000l * 60 * 60 * 24 * 28); 
    

    instead of
    
    unitEstimates.put(Calendar.MONTH, 1000l * 60 * 60 * 24 * 30); 
    

    in the differenceGetUnitEstimates() method
    For example as the code stands today – it returns the difference in months between Feb 7, 2008 and Mar 7, 2008 as 0(zero)
    This is because Feb has only 29 days and the code snippet in difference(Calendar c1, Calendar c2, int unit)
    
    long increment = (long) Math.floor((double) difference / (double) unitEstimate);
    

    returns zero as the increment as a result
    The while loop skips over an zero increment value.
    
    while (increment > 0) {
    

    The fix I suggested was based on the observation that every month has 28 days (leap year february included) and the basic logic of finding the number of months elapsed based on the increments still is a fundamentally strong idea
    Let me know what you think. I have done some testing on this as well and it works.

  4. Dragan Says:

    Thanks for posting this online. It really helped me. I just can’t believe there is no such util included with standard jdk.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>