Link to home
Start Free TrialLog in
Avatar of mas1963
mas1963

asked on

Time difference between two dates considering leap year -Urgent

The following j-script below calculates term in years, months and days. However, it is not working when there are leap years within the term. Here is an example of the behavior in the system with the script enabled. Clearly this is a 6 year lease, not a “5 Years, 11 Months, 28 Days” term:.

How I can I add leap year in following code.
here code I am using

/* Function calculates difference between Commencement and Expiry date and displays in the format 'X Yrs,  Y Mos,  Z Days' */
DateDifference = function(commence, expiry)
{
   if(commence == null || expiry == null)
   {
       return '0 Yrs  0 Mos  0 Days';
   }
   else
   {
       // Difference between 2 dates
       var diffDate =  expiry - commence;

       // Years
       var years = diffDate/31536000000;
       // Months
       var months = (diffDate % 31536000000)/2628000000;
       // Days
       var days = ((diffDate % 31536000000) % 2628000000)/86400000;
       
       // Result string
                   var result =  Math.floor(years) + " Yrs, " + Math.floor(months) + " Mos, " + Math.floor(days) + " Days";
                 
                   return result;
                }
}

Please check the screen shot for more info
Thanks
Masood

Date-difference.docx
Avatar of knightEknight
knightEknight
Flag of United States of America image

What inputs does the function take -- by this I mean, what format are the commence and expiry parameters in?
Would it be worth separating the year (2011) and then going through a for(var i=startyear; i<=endyear; i++) { } loop checking the year? If it is a leap year, add 86400000 (86400 seconds in a day, what are the extra three 0's for?).

Leap years are every 4 years, except years divisible by 100 unless they are also divisible by 400. (2000 is, 2100 isn't, 2200 isn't, 2300 isn't, 2400 is) so 2096 is before 2104 for leap years.

Also, this will never be entirely accurate in its current state. You're assuming that months have 30.416 days. No months have that.

It may be better to do, and this is not code, just logic:

years = endyear - startyear;
months = endmonth - startmonth; If months < 0 { years--; months=12+months; (the negative creates a number lower than 12) }
days = enddays - startdays; If days < 0 { months--; if months < 0 { years--; months=12-months; } days=(days in the month)-days; }

Is this all going to be client-side processing? If not, use whatever server-side language you're using to do this.

There are dozens of these kinds of things already made: http://ditio.net/2010/05/02/javascript-date-difference-calculation/ -- whether or not they take into account leap years is another issue.
>> ...  this will never be entirely accurate in its current state.  You're assuming that months have 30.416 days.

I agree, and if you are going to make this assumption, you may as well also assume that a year has 31557600000ms instead of 31536000000ms.  (Since a year is actually closer to 365.25 days.)  This might improve the accuracy somewhat, but it won't be exact.
Avatar of mas1963
mas1963

ASKER

I will appreicate if you can provide me working code as I need it urgent.
stare date is 1/1/ 2011 and expiry as 12/31/2016 ( dd/mm/yyyy) format


Thanks
Mas
Avatar of mas1963

ASKER

I will be happy if you can change the logic which I have and add the logic of checking he leap year to calculate the date difference.
Very quickly thrown together and tested. This is basing on you inputting as DATE FORMATS as you have said above and not as integers, as your code implies. You can add variables to use the information in other days. It also features a very (VERY) simple date verification, so you can't put the end date as sometime in the past, or enter February 29th UNLESS it is a leap year.

I have not added month checks (1-12) or year checks.

<script type='text/javascript'>
//1/1/ 2011 and expiry as 12/31/2016

	var startDate='01/01/2011';
	var endDate='12/31/2016';
	
	var startExplode=startDate.split('/');
		var startYears=parseInt(startExplode[2],10);
		var startMonths=parseInt(startExplode[0],10);
		var startDays=parseInt(startExplode[1],10);
	var endExplode=endDate.split('/');
		var endYears=parseInt(endExplode[2],10);
		var endMonths=parseInt(endExplode[0],10);
		var endDays=parseInt(endExplode[1],10);
	startDate=new Date(startDate).getTime();
	endDate=new Date(endDate).getTime();

	if((startYears < endYears || startMonths < endMonths || startDays < endDays) && startDate < endDate) {
		
		
		var dateDifference=endDate-startDate;
		
		var datesPerMonth=new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
		
		if(endYears%4==0) { datesPerMonth[2]=29; }
		if(endYears%100==0 && endYears%400>0) { datesPerMonth[2]=28; }
		
		if(startDays<=datesPerMonth[startMonths]) {
			if(endDays<=datesPerMonth[endMonths]) {
		
				var diffYears=endYears-startYears;
				var diffMonths=endMonths-startMonths; if(diffMonths < 0) { diffYears--; diffMonths=(12-diffMonths); }
				var diffDays=endDays-startDays; if(diffDays < 0) { diffMonths--; if(diffMonths<0) { diffYears--; diffMonths=(12-diffMonths); } diffDays=datesPerMonth[(endMonths==1?12:(endMonths-1))]-diffDays; }
			
				document.write("Difference: "+diffYears+" years; "+diffMonths+" months; "+diffDays+" days");
			}
			else { document.write('Bad end'); }
		}
		else { document.write('Bad start'); }
	}
</script>

Open in new window

Ignore that, it doesn't work with start months higer than end months. I'll make some changes.
My mistake. I had - instead of + when dealing with negatives, which I said above wouldn't work. Rushing is bad, kids.

<script type='text/javascript'>
//1/1/ 2011 and expiry as 12/31/2016

	var startDate='12/01/2011';
	var endDate='12/31/2016';
	endDate='02/29/2016';
	
	var startExplode=startDate.split('/');
		var startYears=parseInt(startExplode[2],10);
		var startMonths=parseInt(startExplode[0],10);
		var startDays=parseInt(startExplode[1],10);
	var endExplode=endDate.split('/');
		var endYears=parseInt(endExplode[2],10);
		var endMonths=parseInt(endExplode[0],10);
		var endDays=parseInt(endExplode[1],10);
	startDate=new Date(startDate).getTime();
	endDate=new Date(endDate).getTime();

	if((startYears < endYears || startMonths < endMonths || startDays < endDays) && startDate < endDate) {
		
		
		var dateDifference=endDate-startDate;
		
		var datesPerMonth=new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
		
		if(endYears%4==0) { datesPerMonth[2]=29; }
		if(endYears%100==0 && endYears%400>0) { datesPerMonth[2]=28; }
		
		if(startDays<=datesPerMonth[startMonths]) {
			if(endDays<=datesPerMonth[endMonths]) {
		
				var diffYears=endYears-startYears; // if(endMonths < startMonths || (endMonths == startMonths && endDays < startDays)) { diffYears--; }
				var diffMonths=endMonths-startMonths; if(diffMonths < 0) { diffYears--; diffMonths=(12+diffMonths); }
				var diffDays=endDays-startDays; if(diffDays < 0) { diffMonths--; if(diffMonths<0) { diffYears--; diffMonths=(12+diffMonths); } diffDays=datesPerMonth[(endMonths==1?12:(endMonths-1))]+diffDays; }
			
				document.write("Difference: "+diffYears+" years; "+diffMonths+" months; "+diffDays+" days");
			}
			else { document.write('Bad end'); }
		}
		else { document.write('Bad start'); }
	}
</script>

Open in new window

Avatar of mas1963

ASKER

Hi Vampireofdarkness,
Thanks for code.
I have question, time difference between  1/1/ 2011 and 12/31/2016 should be 6 year, but I ran your code I am getting Difference: 5 years; 0 months; 30 days ??

could you please look into it.

Thanks
Mas
NEVER use document.write after a page has loaded. I know you example is inline, but the asker had a function.  

Anyway - have a read here http://www.merlyn.demon.co.uk/js-date1.htm#DYMD

Also takes me back to http://www.irt.org/script/29.htm
Avatar of mas1963

ASKER

Should we check each year between start and end date (including start date and end date) as leap year ??
@mas1963 the difference between 1/1/2011 and 12/31/2016 is not 6 years. At greatest it is 5 years, 11 months, 30 days, 23 hours, 59 minutes and 59 seconds.

Also, in my code the start date is 12/01/2011, not 01/01/2011, which is why you are showing 0 months.

The code checks for leap years.

@mplungjan: the code was used as a complete HTML file, not as a separate script, as implied by the fact it is self executing.
Avatar of mas1963

ASKER

Hi Vampireofdarkness,
code also checking each year between start and end date (including start date and end date) as leap year ??

Thanks
Mas
No, it doesn't need to because you're not doing a total day count. It does check for leap years IF REQUIRED when counting days between months (see lines 26, 27 and 34).
Avatar of mas1963

ASKER


Hi Hi Vampireofdarkness,
If I check the date difference between 1/1/2013 and 12/31/2015 , I am getting Difference: 2 years; 11 months; 30 days . Should it be 3 year, 0 month, 0 days

Thanks
Mas


You mean 0 minutes into 1/1/2013 until but not including 1 minute past midnight on the second of Jan 2015
The difference isn't showing as 3 years because it ISN'T 3 years. 01/01/2011 00:00 to 12/31/2011 00:00 is NOT a year. 01/01/2011 00:00 to 12/31/2011 23:59 is still NOT a year. It is 11 months, 30 days, 23 hours and 59 minutes.

If you want it to show 3 years (ie- one day more than it should) I would suggest changing the code so that you either

1. Subtract one day from the startDate variable (and make sure if you go into negatives to subtract from the months and year accordingly)
2. Add to the endDate variable (also noting the months and years)
3. Add to the diffDays, remembering months and years
also normalise the hour to 0,0,0,0 to not deal with DST :)
>>Clearly this is a 6 year lease, not a “5 Years, 11 Months, 28 Days” term
No it is NOT.
The year starts at 1/1/2011 00:00:00, but on 12/31/2011 23:59:59 there are ZERO years elapsed still.

You have to wait until January 1 to be able to say "A year has elapsed!".

Also note, when you say that the expiration date is:
12/31/2016

Javascript assuming you mean 12/31/2016 00:00:00, but based on your (wrong) assumptions of elapsed times, what you really mean is 12/31/2016 23:59:59.

But like I said, at 12/31/2016 23:59:59 the sixth year has NOT completed yet. So for your intended purposes, add a day to whatever your end date is and you will get the time frame you want/expect.
Here is my take on this.

1: calculate number of days till the first of next month
2: calculate the number of months till end of year
3: calculate number of years from the 1st of Jan to the 1st of Jan of the end year
4: add months from 1st of Jan of the end year to end month start
5: add days from 1st of end month to end date

TODO: Test if days within same month and months within same year, but it seems a good start :)
<script>
/* Attempt on an accurate year month day difference by
   Michel Plungjan, javascripts(a)plungjan.name
   It will be appreciated if this comment stays */
var aDay = 24*60*60*1000;
var DateDifference = function(commence, expiry) {
  if (!commence || !expiry) return '0 Yrs  0 Mos  0 Days';
  var months = 0;
  var years  = 0;
  var days   = 0;
  var start  = new Date(Date.parse(commence))
  start.setHours(0,0,0,0); // normalise
  var end = new Date(Date.parse(expiry));
  end.setHours(0,0,0,0); // at midnight
  
  var firstOfNextMonth = new Date(start.getFullYear(),start.getMonth()+1,1,0,0,0,0); // 1st of the next month
  var numberOfDaysInStartMonth = new Date(start.getFullYear(),start.getMonth()+1,0,0,0,0,0).getDate();
  
  var daysToNextMonth=0;
  for (var i=start.getTime(), n=firstOfNextMonth.getTime();i<n;i+=aDay) {
  	  daysToNextMonth++;
  }
  if (daysToNextMonth===numberOfDaysInStartMonth) {
  	days=0;
  	months++;
  }
  var monthsToNextYear = 12-(start.getMonth()+1);
  months += monthsToNextYear;
  if (months === 12) {
  	months = 0;
  	years++;
  }
  if (end.getFullYear() > start.getFullYear()+1) {
    for (var i=start.getFullYear()+1, n=end.getFullYear();i<n;i++) {
      years++;
    }
  }  
  
  firstOfNextMonth = new Date(end.getFullYear(),end.getMonth()+1,1,0,0,0,0); // 1st of the next month
  var numberOfDaysInEndMonth = new Date(end.getFullYear(),end.getMonth()+1,0,0,0,0,0).getDate();

  if (end.getDate()===numberOfDaysInEndMonth) {
  	days=0;
  	months++;
  }
  else days += end.getDate()+1; // 24 hours
  months += end.getMonth();
  if (months === 12) {
  	months = 0;
  	years++;
  }

  return years+" year"+((years==1)?" ":"s ")+months+" month"+((months==1)?" ":"s ")+days+" day"+((days==1)?" ":"s ")
}
alert(DateDifference("02/29/2004","02/28/2007"));
alert(DateDifference("1/1/2013","12/31/2015"));
</script>

Open in new window

Avatar of mas1963

ASKER

Hi Vampireofdarkness,
Sorry for late reply, I was testing it.
I used you code and I am almost there but
It’s not working on months. For example. 1/1/2011 – 1/31/2011 should  be 1 month not 30 days

Or 1/1/2011 – 6/30/2011 should be 6 months not 5 months, 29 days


Hi mplungjan,
in your code how we can check leap year ?


Thanks
Masood
No need to check leap year - never was
@mplungjan - There is a need to check leap year if you're calculating the days between Jan 15th and March 14th. February may or may not have 29 days.

@mas1963 - please read above. 1st Jan to 31st Jan is 30 days, not a month (31 days, in this case). If you want to change the output, you need to add one day to the output and change the months and years accordingly. I have said this at least twice.

Modified code, NOTE THAT THIS IS NOT CONTAINED WITHIN A FUNCTION: datesPerMonth moved to the top, added the endDays++; line (Line 17?)
<script type='text/javascript'>

	var datesPerMonth=new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);

	var startDate='1/01/2011';
	var endDate='12/31/2016';
	
	var startExplode=startDate.split('/');
		var startYears=parseInt(startExplode[2],10);
		var startMonths=parseInt(startExplode[0],10);
		var startDays=parseInt(startExplode[1],10);
	var endExplode=endDate.split('/');
		var endYears=parseInt(endExplode[2],10);
		var endMonths=parseInt(endExplode[0],10);
		var endDays=parseInt(endExplode[1],10);
		
		endDays++; if(endDays>datesPerMonth[endMonths]) { endDays=1; endMonths++; if(endMonths==13) { endMonths=1; endYears++; } }

	startDate=new Date(startDate).getTime();
	endDate=new Date(endDate).getTime();

	if((startYears < endYears || startMonths < endMonths || startDays < endDays) && startDate < endDate) {
		
		
		var dateDifference=endDate-startDate;
		
		
		if(endYears%4==0) { datesPerMonth[2]=29; }
		if(endYears%100==0 && endYears%400>0) { datesPerMonth[2]=28; }
		
		if(startDays<=datesPerMonth[startMonths]) {
			if(endDays<=datesPerMonth[endMonths]) {
		
				var diffYears=endYears-startYears; // if(endMonths < startMonths || (endMonths == startMonths && endDays < startDays)) { diffYears--; }
				var diffMonths=endMonths-startMonths; if(diffMonths < 0) { diffYears--; diffMonths=(12+diffMonths); }
				var diffDays=endDays-startDays; if(diffDays < 0) { diffMonths--; if(diffMonths<0) { diffYears--; diffMonths=(12+diffMonths); } diffDays=datesPerMonth[(endMonths==1?12:(endMonths-1))]+diffDays; }
			
				document.write("Difference: "+diffYears+" years; "+diffMonths+" months; "+diffDays+" days");
			}
			else { document.write('Bad end'); }
		}
		else { document.write('Bad start'); }
	}
</script>

Open in new window


1/1/2011 - 12/31/2016 shows as 6 years, but now 1/1/2011 - 1/1/2016 shows as 5 years and 1 day (which by your method is correct)
Avatar of mas1963

ASKER

Thanks Vampireofdarkness,
Its now working perfectly.
I need final help.
In result variable if years, month or days, any one is zero then in result i don't to show them.
var result =  Math.floor(diffYears) + " Yrs, " + Math.floor(diffMonths) + " Mos, " + Math.floor(diffDays) + " Days";
i

Thanks
Masood
ASKER CERTIFIED SOLUTION
Avatar of Vampireofdarkness
Vampireofdarkness
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
My code does not check specifically for leap year. It gets the number of actual days in the month. I have not had time to make sure it works for dates in the same year, same month and same week for which I apologise. I will try to get to it these days where I am off.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of mas1963

ASKER

I am really thankfull to Vampireofdarkness for his easy to understand solution and solution worked perfectly. I really appreciated other contributors too.
Avatar of mas1963

ASKER

Thanks Vampireofdarkness for your live saver answer. It worked perfectly.
 I also like to thanks mplungjan for your input.

Thanks all.
masod: Would you like to answer my last question so I can implement it in my script?

Thanks

Right now I cheat by adding this to the script

if (start.getFullYear() === end.getFullYear() && (end.getDate()-start.getDate())<28 ) { // this is cheating!
    for (var i=start.getDate();i<=end.getDate();i+=aDay) days++;
    return formatDiff(0,0,days);
}
function formatDiff(years,months,days) {
var res = [];
  if (years)  res.push(years +" year" +((years ==1)?" ":"s "));
  if (months) res.push(months+" month"+((months==1)?" ":"s "));
  if (days)   res.push(days  +" day"  +((days  ==1)?" ":"s "));
  return res.join(" ");
}
Usign Vampire's script

If I have
      var startDate='2/25/2004';
      var endDate='3/4/2004';

I get 9 days

if I have
      var startDate='2/25/2003';
      var endDate='3/4/2003';

I get 8 days

But 25 @00:00 +26+27+28+29+1+2+3 @23:59
is 8 days, no?
@mplungjan - I said the same thing many times. The user wanted both the start and end date to be included so that 1/1/x to 12/31/x is a year and not 11 months, 30 days. This would put 25, 26, 27, 28, 29th Feb, 1, 2, 3, 4th Mar. You're not including the 4th March, which the asker wanted.

You're using a leap year in your first example and a non-leap year in the second.
Yes I was using that on purpose and there should be one more day in the leap year but it does not make sense to add ANOTHER day to a period that include 24 hours on the 25th and on the 3rd

Also in my opinion the 1st to the 29th of Feb in a leap year is a month and the 1st to the 28th of Feb is a month in a non-leap year
If 2/1/x to 2/28/x is a month (ie- 28 days as 1 and 28 are included) in a non leap year, then by your same logic 25th Feb to 4th Mar in a leap year is 25, 26, 27, 28, 29, 1, 2, 3, 4 (ie- 9 days as 25 and 4 are included).

You can't selectively include the last day when calculating, otherwise when booking a holiday you could end up losing a day of your holiday depending on whether you leave on the last day of the month or not. Either you include the last day as a full day, or you don't.
I am only getting more confused

25 - 1  < including
26 - 2
27 - 3
28 - 4
29 - 5
1  - 6
2 - 7
3 - 8 < including

unless of course you talk nights
then you arrive on the 25th and leave on the 4th
You put 3/4/2004 (That's 4th March, not 3rd). You are not including the LAST day.

25 = 1
26 = 2
27 = 3
28 = 4
29 = 5
01 = 6
02 = 7
03 = 8
04 = 9

The asker wanted BOTH Start and End date included in the duration. Ending at 03/03/2004 is ending too early, according to the asker.
Avatar of mas1963

ASKER

Vampireofdarkness is right.
As most of properties lease are day you sign the lease and end of day when ever lease is ending, so point is that we will include the day lease start and end of the days lease is ending.

I