1

I'm not looking for an answer or code snippet to the question in the title, but rather I'm looking for help understanding why a snippet of code isn't working like I expect it to:

var getMonthsBetween = function(date1, date2)
{
    'use strict';

    // Months will be calculated between start and end dates.
    // Make sure start date is less than end date.
    // But remember if the difference should be negative.
    var start_date = date1;
    var end_date = date2;
    var inverse = false;

    if (date1 > date2)
    {
        start_date = date2;
        end_date = date1;
        inverse = true;
    }

    // Calculate the differences between the start and end dates
    var yearsDifference = end_date.getFullYear() - start_date.getFullYear();
    var monthsDifference = end_date.getMonth() - start_date.getMonth();
    var daysDifference = (end_date.getDate()+1) - start_date.getDate(); // This +1 might be a mistake  

    return (inverse ? -1 : 1) * (yearsDifference * 12 + monthsDifference + daysDifference/30); // Add fractional month
}

The above code is intended to return the difference (in months) between 2 given dates, accurate to the first decimal point. I didn't write the code myself and don't have contact with whoever did write it, unfortunately, and I can't tell why the author decided to add a day to the end date when working out the difference in days (there are instances where it is slightly off).

Could someone please help me with this problem? I'm not sure what the reason is to add that extra day here ((end_date.getDate()+1)) and I'm not sure it's actually correct (I suspect it's not correct) but I would like another opinion.

EDIT I was unclear about how the function is not working as expected - it is called as soon as the user selects 2 dates from 2 date pickers. The calculated numbers are not sent to the backend, instead the original 2 dates are sent back and then we use (PHP) datetime.diff to work out how many months there are between the dates. The backend has already been confirmed to be correct, but the frontend does not always match it, e.g.

start date = Monday June 30, 2014, 00:00:00
end date = Saturday February 28, 2015, 00:00:00
result (JS) = 7.933333333333334
result (PHP datetime.diff) = 8.0333333333333

or

start date = Tuesday June 30, 2015, 00:00:00
end date = Monday February 29, 2016, 00:00:00
result (JS) = 7.966666666666667
result (PHP datetime.diff) = 8.0333333333333

Then there are other times when the numbers do match e.g.

start date = Monday June 30, 2014, 00:00:00
end date = Wednesday April 15, 2015, 00:00:00
result (JS) = 9.5
result (PHP datetime.diff) = 9.5
Skytiger
  • 1,745
  • 4
  • 26
  • 53
  • It's because the initial writer of the code possibly thought about the start date to be at the start of the day and the end date to be at the end of the day. So when using the same date for both start and end date he used the convention that that is a one day difference – Dbuggy Sep 22 '15 at 08:49
  • possible duplicate of [Difference in Months between two dates in JavaScript](http://stackoverflow.com/questions/2536379/difference-in-months-between-two-dates-in-javascript) – Aditya Singh Sep 22 '15 at 08:52
  • 1
    what do you mean by "code isn't working like I expect it to:" could include the output and your expected output? you might want to trace each value so that you'll find out why he added that 1. also check the `date1` and `date2` values if it reflects the actual date selected by the user – Dyrandz Famador Sep 22 '15 at 08:54
  • @DyrandzFamador Added some examples :) – Skytiger Sep 22 '15 at 09:04
  • @wizkid I don't see how this is a duplicate, since I'm not actually asking for a way to find the difference, I'm asking for help understanding a (possible) flaw in code I'm already using? – Skytiger Sep 22 '15 at 09:05
  • Shouldn't the difference between June 30th, 2015 and April 15th, 2015 be 2.5 and not 9.5? – aecend Sep 22 '15 at 09:30
  • @aecend I updated the example dates to the correct values – Skytiger Sep 22 '15 at 09:35

2 Answers2

2

You think this is wrong because you assumed that both dates are 00:00:00. The original author thought differently: the first date is assumed to be 00:00:00, the last date is assumed to be at 24:00:00. (Of course JavaScript has no notion of 24:00:00).

So, basically both of you are right, the original author should have made a comment about this though.


Other angle of looking at this comes from a bit of number theory: you are thinking that the function calculates as a left closed - right open interval, the original author thought about a closed interval.

meskobalazs
  • 15,741
  • 2
  • 40
  • 63
  • Apologies - I've added examples of the data being used. You can see that both the start and end dates are 00:00:00 before any calculations are done. – Skytiger Sep 22 '15 at 09:07
  • Of course they are. Maybe I was not clear. JavaScript has no notion of `24:00:00`, but that does not stop a developer to think like there is. The calculation reflects that: `24:00:00` is the same as the next day at `00:00:00`. – meskobalazs Sep 22 '15 at 09:11
2

Apparently PHP's datetime.diff includes the ending day in its calculation. When I actually incremented the end date on the JavaScript side, the numbers all match up. Incrementing the date object itself before comparing them accounts for changing to the next month. The original developer had noticed that that was happening, but he didn't increment the end date correctly. He added another day to the end date's day value, but that wasn't changing to the following month when adding a day to February 28, 2015

This code works as expected:

var getMonthsBetween = function(date1, date2)
{
    'use strict';

    // Months will be calculated between start and end dates.
    // Make sure start date is less than end date.
    // But remember if the difference should be negative.
    var start_date = date1;
    var end_date = date2;
    var inverse = false;

    if (date1 > date2)
    {
        start_date = date2;
        end_date = date1;
        inverse = true;
    }

    end_date = new Date(end_date); //If you don't do this, the original date passed will be changed. Dates are mutable objects.
    end_date.setDate(end_date.getDate() + 1);

    // Calculate the differences between the start and end dates
    var yearsDifference = end_date.getFullYear() - start_date.getFullYear();
    var monthsDifference = end_date.getMonth() - start_date.getMonth();
    var daysDifference = end_date.getDate() - start_date.getDate();

    return (inverse ? -1 : 1) * (yearsDifference * 12 + monthsDifference + daysDifference/30); // Add fractional month
}
aecend
  • 2,432
  • 14
  • 16
  • Thank you, I'm going to try this out and will mark this answer as correct if it works! – Skytiger Sep 22 '15 at 09:18
  • Thank you for the help, but the snippet you provided didn't have any effect on the output of the function for the examples I provided in OP. What is the intended purpose of `!yearsDifference` and `!monthsDifference` in that if statement? – Skytiger Sep 22 '15 at 09:39
  • The `!yearsDifference` and `!monthsDifference` are there to make sure that the date being compared is in the same month and year. That way June 22 and April 22 don't get detected as the same day. – aecend Sep 22 '15 at 09:41
  • @Skytiger If you don't mind me asking, what browser are you working with? – aecend Sep 22 '15 at 09:41
  • I'm currently testing on Chrome (Version 45.0.2454.93 m) – Skytiger Sep 22 '15 at 09:45
  • Give me a minute. I think I found something, I'll update my answer in just a sec. – aecend Sep 22 '15 at 09:54
  • @Skytiger My updated answer should solve the discrepancy. Let me know if it works for you. – aecend Sep 22 '15 at 10:26