Going the 'milliseconds way' will lead to rounding off errors and they become most evident once you have a little thing like "Daylight Savings Time" come into the picture.
The Correct Way:
/** Using Calendar - THE CORRECT WAY**/
//assert: startDate must be before endDate
public static long daysBetween(Calendar startDate, Calendar endDate) {
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
}
or more efficiently, (thanks Mauro), if you're using the Gregorian Calendar:
/** Using Calendar - THE CORRECT (& Faster) WAY**/
/****Needs testing ...... Anyone?****/
//assert: startDate must be before endDate
public static long daysBetween(final Calendar startDate, final Calendar endDate) {
int MILLIS_IN_DAY = 1000 * 60 * 60 * 24;
long endInstant = endDate.getTimeInMillis();
int presumedDays = (int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);
Calendar cursor = (Calendar) startDate.clone();
cursor.add(Calendar.DAY_OF_YEAR, presumedDays);
long instant = cursor.getTimeInMillis();
if (instant == endInstant)
return presumedDays;
final int step = instant < endInstant ? 1 : -1;
do {
cursor.add(Calendar.DAY_OF_MONTH, step);
presumedDays += step;
} while (cursor.getTimeInMillis() != endInstant);
return presumedDays;
}
The Nuances:
Lets take, for example, the difference between the two dates 03/24/2007 and 03/25/2007 should be 1 day;
However, using the millisecond route, you'll get 0 days, if you run this in the UK!
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class DateTest {
public class DateTest {
static SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy");
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/London"));
//diff between these 2 dates should be 1
Date d1 = new Date("01/01/2007 12:00:00");
Date d2 = new Date("01/02/2007 12:00:00");
//diff between these 2 dates should be 1
Date d3 = new Date("03/24/2007 12:00:00");
Date d4 = new Date("03/25/2007 12:00:00");
Calendar cal1 = Calendar.getInstance();cal1.setTime(d1);
Calendar cal2 = Calendar.getInstance();cal2.setTime(d2);
Calendar cal3 = Calendar.getInstance();cal3.setTime(d3);
Calendar cal4 = Calendar.getInstance();cal4.setTime(d4);
printOutput("Manual ", d1, d2, calculateDays(d1, d2));
printOutput("Calendar ", d1, d2, daysBetween(cal1, cal2));
System.out.println("---");
printOutput("Manual ", d3, d4, calculateDays(d3, d4));
printOutput("Calendar ", d3, d4, daysBetween(cal3, cal4));
}
private static void printOutput(String type, Date d1, Date d2, long result) {
System.out.println(type+ "- Days between: " + sdf.format(d1)
+ " and " + sdf.format(d2) + " is: " + result);
}
/** Manual Method - YIELDS INCORRECT RESULTS - DO NOT USE**/
/* This method is used to find the no of days between the given dates */
public static long calculateDays(Date dateEarly, Date dateLater) {
return (dateLater.getTime() - dateEarly.getTime()) / (24 * 60 * 60 * 1000);
}
/** Using Calendar - THE CORRECT WAY**/
public static long daysBetween(Calendar startDate, Calendar endDate) {
Calendar date = (Calendar) startDate.clone();
long daysBetween = 0;
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
daysBetween++;
}
return daysBetween;
}
}
OUTPUT:
Manual - Days between: 01-Jan-2007 and 02-Jan-2007 is: 1
Calendar - Days between: 01-Jan-2007 and 02-Jan-2007 is: 1
---
Manual - Days between: 24-Mar-2007 and 25-Mar-2007 is: 0
Calendar - Days between: 24-Mar-2007 and 25-Mar-2007 is: 1
36 comments:
What if end is before start (negative days diff)?
assertion added. Thanks!
You can use jodatime to solve your problem http://joda-time.sourceforge.net/
Good one. Thanks for sharing.
using joda time:
DateTime start = new DateTime(2007, 1, 1, 0, 0, 0, 0);
DateTime end = new DateTime(2009, 1, 1, 0, 0, 0 ,0);
Days days = Days.daysBetween(start, end);
System.out.println(days.getDays());
You should be aware that the algorithm given may give 'one more day' than you expect.
It gives 1 as the number of days between 2009-02-28 19:00:00 and 2009-02-28 19:00:01.
This may be what you want (it is what I want) - but do check.
Very usefull stuff.
Thanks very much.
You just save my life brother, i was crazy about it
thanks for sharing
What if the Start date is feb sometime say(20 feb 2011) and end date is march say (3 march 2011) ?
Check the day difference between these two dates.
Thank you very much.
This also works. Simply get an instance of a Calendar for GMT timezone, it has no offset, daylight saving flag is set to false (sun.util.calendar.ZoneInfo[id="GMT",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null])
Use the calendar to set the date.
final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.setTimeInMillis(0);
System.out.println(cal.getTimeZone());
cal.set(Calendar.YEAR, 2011);
cal.set(Calendar.MONTH, 9);
cal.set(Calendar.DAY_OF_MONTH, 29);
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
final Date startDate = cal.getTime();
cal.set(Calendar.YEAR, 2011);
cal.set(Calendar.MONTH, 12);
cal.set(Calendar.DAY_OF_MONTH, 21);
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
final Date endDate = cal.getTime();
System.out.println((endDate.getTime() - startDate.getTime()) % (1000l * 60l * 60l * 24l));
Thanks
Why not java provides standard way to calculate difference between two dates instead of implementing own solution. I like the joda time way more clean.
Thanks
How to convert String to Date in Java
This is way too complicated for what it is - here's a far more efficient way of doing this...
private Integer daysBetween(Calendar startDate, Calendar endDate) {
long start = startDate.getTimeInMillis();
long end = endDate.getTimeInMillis();
long diff = end - start;
Long daysBetween = diff / 86400000;
return daysBetween.intValue();
}
@bigcalm :
Did you read the topic?
Sure your way is more efficient, but it's also sometimes wrong...
Your daysBetween function optimized for GregorianCalendar is wrong. For instance:
From: Thu Jan 31 00:00:00 CET 2019
To: Fri Mar 29 00:00:00 CET 2019
Correct result: 57
Result calculated by your function: 60
Using Europe/Berlin timezone.
This is just an example, if you do a long test with random dates you can check yourself. By the way, a still correct approach for GregorianCalendar but even more simple and fast is to do an heuristic (based on the naive "wrong" method), followed by an adjustment. That is:
public static int heuristic(Calendar startDate, Calendar endDate)
{
long endInstant = endDate.getTimeInMillis();
int presumedDays =
(int) ((endInstant - startDate.getTimeInMillis()) / MILLIS_IN_DAY);
Calendar cursor = (Calendar) startDate.clone();
cursor.add(Calendar.DAY_OF_YEAR, presumedDays);
long instant = cursor.getTimeInMillis();
if(instant == endInstant)
return presumedDays;
final int step = instant < endInstant? 1: -1;
do
{
cursor.add(Calendar.DAY_OF_MONTH, step);
presumedDays += step;
}
while(cursor.getTimeInMillis() != endInstant);
return presumedDays;
}
Thanks Mauro. Have incorporated your code into the post.
I'm interested in why the startDate parameter is cloned? And why isn't the endDate cloned as well?
Because the startDate is manipulated (used as a counter), the endDate is not. It would be impolite at least to modify your input parameter as side effect.
Hey Ryan Fernandes, thanks for the code. Is there any licensing for this?
This is great. thank you
Doesn't work if file is only an hour older, in fact it runs in a continuous loop due to while (cursor.getTimeInMillis() != endTimeMil) never equalling it should be a <= sign
I had to add this to make it work
long temp = 0;
do {
cursor.add(Calendar.DAY_OF_MONTH, step);
numberDays += step;
temp = cursor.getTimeInMillis();
} while (temp <= endTimeMil);
As John has noted equality cursor.getTimeInMillis() != endTimeMil) terminates loop only when there is even number of days b/n start and end dates. I propose the following fix:
int endDay = endDate.get(Calendar.DAY_OF_YEAR);
if (computedDay == endDay) {
return presumedDays;
}
final int step = computedDay < endDay ? 1 : -1;
do {
cursor.add(Calendar.DAY_OF_YEAR, step);
presumedDays += step;
} while (cursor.get(Calendar.DAY_OF_YEAR) != endDay);
How to calculate the difference between two date? Before. I am running into a problem.
How to calculate the difference between two date? using Before method?. I am running into a problem.
I would suggest something less bad performing as this, any flaws?:
public static void main(String[] args) {
TimeZone ltz = TimeZone.getTimeZone("Europe/London");
Date dateTo = new Date("03/25/2007 12:00:00");
Date dateFrom = new Date("03/24/2007 12:00:00");
System.out.println("days: "+ durationInDays(ltz, dateFrom, dateTo));
}
private static long durationInDays(TimeZone tz, Date dateFrom, Date dateTo) {
long timeTo = dateTo.getTime();
long timeFrom = dateFrom.getTime();
long duration = timeTo - timeFrom;
if (tz.inDaylightTime(dateFrom)) {
if (!tz.inDaylightTime(dateTo)) {
System.out.println("in daylight saving from: "+dateFrom);
System.out.println("not in daylight saving to: "+dateTo);
duration -= 60 * 60 * 1000;
}
} else {
if (tz.inDaylightTime(dateTo)) {
System.out.println("not in daylight saving from: "+dateFrom);
System.out.println("in daylight saving to: "+dateTo);
duration += 60 * 60 * 1000;
}
}
return duration / ( 24 * 60 * 60 * 1000 );
}
Interesting but quite a slow algorithm. This code suggestion base it's assertion on the educated guess based on the 1000 * 60 * 60 * 24 value then correct the answer with one day increments.
public static long daysBetween(Calendar startDate, Calendar endDate) {
if (startDate.after(endDate))
return -daysBetween(endDate, startDate);
Calendar date = (Calendar) startDate.clone();
long bigLeap = (endDate.getTimeInMillis() - startDate.getTimeInMillis())
/ 1000 / 60 / 60 / 24;
date.add(Calendar.DAY_OF_YEAR, (int) bigLeap);
long smallStep = 0;
if (date.before(endDate)) {
while (date.before(endDate)) {
date.add(Calendar.DAY_OF_MONTH, 1);
smallStep++;
}
} else if (endDate.before(date)) {
while (endDate.before(date)) {
date.add(Calendar.DAY_OF_MONTH, -1);
smallStep--;
}
}
return smallStep + bigLeap;
}
@jpgravel: I tested your code and if you compute the difference between (day/month/year hour:minute:second):
28/7/2009 22:0:0
and
29/7/2009 23:0:0
you get 2 as a result. If we want to compare only the dates, not the time, it is not 2 days, but 1 day of difference.
Is there any reason why one would not use the Universal Time Calendar for all date difference calculations?
As UTC does not have daylight savings the simple
days=(UTC_cal1.getTimeInMillis() - UTC_cal1.getTimeInMillis())/(millisecs_in_day)
will work just fine.
Am I missing something?
I need to change this to get the total number of hours between two dates.
Any help?
how to get the date difference in form of following:
input:date1=12/12/2009;
date2=12/5/2013;
output=date Difference:
dd/mm/yyyy
Very usefull.
Thanks so much.
Will this piece of code work if start date and end date are in different timezones .
For Eg. If start_date = 9 Aug 2012 12:00:01 am CEST
and end -date = 9 Nov 2012 12:00:00 am CET?
Post a Comment