16

I am trying to create a simple script that gives me the next recycling date based on a biweekly schedule starting on Wed Jul 6, 2011. So I've created this simple function...

    function getNextDate(startDate) {
        if (today <= startDate) {
            return startDate;
        }
        // calculate the day since the start date.
        var totalDays = Math.ceil((today.getTime()-startDate.getTime())/(one_day));
        // check to see if this day falls on a recycle day 
        var bumpDays = totalDays%14;  // mod 14 -- pickup up every 14 days...
        // pickup is today
        if (bumpDays == 0) {
            return today;
        }
        // return the closest day which is in 14 days, less the # of days since the last
        // pick up..
        var ms =  today.getTime() + ((14- bumpDays) * one_day);
        return new Date(ms);
    }

and can call it like...

 var today=new Date();
 var one_day=1000*60*60*24;  // one day in milliseconds
 var nextDate = getNextDate(new Date(2011,06,06));

so far so good... but when I project "today" to 10/27/2011, I get Tuesday 11/8/2011 as the next date instead of Wednesday 11/9/2011... In fact every day from now thru 10/26/2011 projects the correct pick-up... and every date from 10/27/2011 thru 2/28/2012 projects the Tuesday and not the Wednesday. And then every date from 2/29/2012 (leap year) thru 10/24/2012 (hmmm October again) projects the Wednesday correctly. What am I missing? Any help would be greatly appreciated..

V

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
user799301
  • 453
  • 2
  • 5
  • 8
  • 1
    Shouldn't ceil be floor? Because is you use 2011-06-06 1:00u totalDays will be 1, and the next pickup will be in 13 not 14 days, so a tuesday. – Gerben Jun 15 '11 at 10:33
  • Looks strange. I am getting the correct date with `one_day=1000*60*60*24; today = new Date(2011,8,27); nextDate = getNextDate(new Date(2011,6,6));`, and nextDate is `Wed Sep 28 2011 00:00:00 GMT+`, which is correct right? Tested on FF4. – Raze Jun 15 '11 at 11:00
  • startDate is always new Date(2011,06,06) -- so it shouldn't move – user799301 Jun 15 '11 at 11:12
  • Raze, the date you used works fine, try a date within 10/27/2011 thru 2/28/2012 range.. – user799301 Jun 15 '11 at 11:17
  • If you are in the US you are crossing from daylight savings to standard time in your Oct to Nov span. – kennebec Jun 15 '11 at 14:25
  • A question with similar answers (but possibly more depth): http://stackoverflow.com/questions/563406/add-days-to-datetime-using-javascript – Brent Bradburn Feb 04 '13 at 02:17

3 Answers3

36

The easiest way to do this is update the Date object using setDate. As the comments for this answer indicate this isn't officially part of the spec, but it is supported on all major browsers.

You should NEVER update a different Date object than the one you did the original getDate call on.

Sample implementation:

var incrementDate = function (date, amount) {
    var tmpDate = new Date(date);
    tmpDate.setDate(tmpDate.getDate() + amount)
    return tmpDate;
};

If you're trying to increment a date, please use this function. It will accept both positive and negative values. It also guarantees that the used date objects isn't changed. This should prevent any error which can occur if you don't expect the update to change the value of the object.


Incorrect usage:

var startDate = new Date('2013-11-01T11:00:00');
var a = new Date();
a.setDate(startDate.getDate() + 14)

This will update the "date" value for startDate with 14 days based on the value of a. Because the value of a is not the same is the previously defined startDate it's possible to get a wrong value.

Exelian
  • 5,749
  • 1
  • 30
  • 49
  • 1
    It isn't easy to find documentation that guarantees that this will work in the general case (adding an arbitrary number of days). – Brent Bradburn Feb 04 '13 at 02:52
  • 9
    This is a very dangerous thought process. Imagine 'startDate' is on the 1st of the month, and today is the 31st. You want a date 7 days back from startdate. a.setDate(startDate.getDate() - 7) would suffice, right? Not so fast. 'a' is now 7 days before the beginning of the *current* month, not 7 days before 'startDate'. Considering where this page comes up in Google results, this is absolutely important to understand. I'm correcting such an issue right now and strongly suspect our contractor found this page and followed that exact logic. – 10gistic Oct 31 '13 at 17:03
  • Hmm, I never intended this to be used in the manner which you described. Only for __updating__ a date value. I will change my answer to reflect the issue. – Exelian Nov 01 '13 at 13:51
  • @10gistic *`a` is now 7 days before the beginning of the current month, not 7 days before `startDate`.* Why is `a` now 7 days before the beginning of the current month, and not 7 days before `startDate`? Does `getDate` not *get the date* of the *date* object? – Ian Boyd Feb 23 '14 at 16:15
  • @IanBoyd, It's a bit of an idiosyncrasy of Javascript's Date weirdness. If `startDate` happens to be some day in another month than `a`, which is pretty common towards the end of the month, then there's a problem. `startDate.getDate() -7)` returns `-6`. `setDate()` expects a day of the month *in its month*. Thus, if `a` exists in a different month than `startDate`, `a.setDate(-6)` sets the date of a to 6 days before the beginning of `a`'s month, not `startDate`'s month. – 10gistic Feb 25 '14 at 19:20
  • 1
    @10gistic A careful review of the documentation explained it. My confusion was i thought that `getDate` returned the date and `getTime` returned the time. `getDate` doesn't return the date, and `getTime` doesn't return the time. `getTime()` returns the date (a value in milliseconds since 1970-ish). `getDate()` returns the day of the month (`1..31`). If you approach it thinking that `getDate` returns the date portion of a datetime, you can see how the algorithm would make sense. – Ian Boyd Feb 25 '14 at 20:13
  • I don't mind the discussion, but is it my sample implementation that isn't correctly working? Because I can't reproduce the issue you are talking about with the `incrementDate` function I supplied. Please provide an minimal testcase so I can update my answer. – Exelian Feb 26 '14 at 11:08
  • @Exelian, I thought your update provided an adequate warning to not try it on a separate Date object. :-) – 10gistic Feb 26 '14 at 21:09
4

Expanding on Exellian's answer, if you want to calculate any period in the future (in my case, for the next pay date), you can do a simple loop:

var today = new Date();
var basePayDate = new Date(2012, 9, 23, 0, 0, 0, 0);

while (basePayDate < today) {
    basePayDate.setDate(basePayDate.getDate()+14);
}

var nextPayDate = new Date(basePayDate.getTime());
basePayDate.setDate(nextPayDate.getDate()-14);

document.writeln("<p>Previous pay Date: " + basePayDate.toString());
document.writeln("<p>Current Date: " + today.toString());
document.writeln("<p>Next pay Date: " + nextPayDate.toString());

This won't hit odd problems, assuming the core date services work as expected. I have to admit, I didn't test it out to many years into the future...

yalarad
  • 41
  • 1
2

Note: I had a similar issue; I wanted to create an array of dates on a weekly basis, ie., start date 10/23/2011 and go for 12 weeks. My code was more or less this:

var myDate = new Date(Date.parse(document.eventForm.startDate.value));
var toDate = new Date(myDate);

var week = 60 * 60 * 24 * 7 * 1000;
var milliseconds = toDate.getTime();

dateArray[0] = myDate.format('m/d/Y');

for (var count = 1; count < numberOccurrences; count++) {
    milliseconds += week;
toDate.setTime(milliseconds);
    dateArray[count] = toDate.format('m/d/Y');
}

Because I didn't specify the time and I live in the US, my default time was midnight, so when I crossed the daylight savings time border, I moved into the previous day. Yuck. I resolved it by setting my time of day to noon before I did my week calculation.

Debby
  • 21
  • 1