0

I am trying to set an end date for a JSON object. The end date equals 30 days after the start date. Sometimes this returns the correct date, sometimes it doesn't.

Here is the GetDateSchedulerFormatted function

GetDateSchedulerFormatted(date) {
    function pad(s) { return (s < 10) ? '0' + s : s; }
    var d = new Date(date);
    // yyy-MM-dd
    return [pad(d.getDate()), pad(d.getMonth() + 1), d.getFullYear()].join('/') + " " + pad(d.getHours()) + ":" + pad(d.getMinutes());
}

In this example the code returns the correct date

//activity.startDate = 2017-07-02T00:00:00-08:00
    var startDate = this.GetDateSchedulerFormatted(activity.startDate); //start date 07/02/2017 00:00 d/m/yyy

    var newDate = new Date(startDate); // returns Tue Aug 01 2017 00:00:00 GMT-0700 (Pacific Daylight Time) also 1 day off

    var endDate = this.GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns correct date 01/08/2017 00:00 d/m/yyyy 

In this next example it returns the date 1 year off

    //activity.startDate = 2016-12-12T00:00:00-08:00
    var startDate = this.GetDateSchedulerFormatted(activity.startDate); //returns 12/12/2016 00:00 d/m/yyy

    var newDate = new Date(startDate); // returns Wed Jan 11 2017 00:00:00 GMT-0800 1 month ahead

    var endDate = this.GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns 11/01/2017 00:0 d/m/yyy 

In this example the same exact date is returned

        //activity.startDate = 2017-02-01T00:00:00-08:00
        var startDate = this.GetDateSchedulerFormatted(activity.startDate); //returns 01/02/2017 00:00 d/m/yyy

        var newDate = new Date(startDate); // returns Wed Feb 01 2017 00:00:00 GMT-0800

        var endDate = this.GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns 01/02/2017 00:00 the same date, it's not 30 days ahead

Then in this final example I get NaN/NaN/NaN NaN:NaN

        //activity.startDate = 2017-02-25T00:00:00-08:00
        var startDate = this.GetDateSchedulerFormatted(activity.startDate); //returns 25/02/2017 00:00 d/m/yyy

        var newDate = new Date(startDate); // returns invalid date

        var endDate = this.GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns NaN/NaN/NaN NaN:NaN

I have also tried new Date(Date.parse(startDate));

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Train
  • 3,420
  • 2
  • 29
  • 59

2 Answers2

2

You really don't need a library. To add one month to a date is fairly straight forward, starting with:

date.setMonth(date.getMonth() + 1);

This maintains the time associated with the date, even over daylight saving boundaries, but can push the date beyond the end of the following month, e.g. adding 1 month to Jan 31 gives 31 Feb which goes to 2 or 3 March (depending on if it's a leap year or not).

So there needs to be a check that if the date isn't the same, it's rolled over a month so set it to the last day of the previous month. Written as a function to add an arbitrary number of months:

function addMonths(date, months) {
  var d = date.getDate();
  date.setMonth(date.getMonth() + +months);
  if (date.getDate() != d) {
    date.setDate(0);
  }
  return date;
}

// Add 12 months to 29 Feb, 2016
var d = new Date(2016,1,29)
console.log(addMonths(d, 12).toString()); // 28 Feb 2017

Adding is even easier, see Add +1 to current date which is easily adapted to add any number of days (which means this question is really a duplicate).

So, back to your code.

Here is the GetDateSchedulerFormatted function

function GetDateSchedulerFormatted(date) {
  function pad(s) {
    return (s < 10) ? '0' + s : s;
  }
  var d = new Date(date);
  // yyy-MM-dd
  return [pad(d.getDate()),
      pad(d.getMonth() + 1),
      d.getFullYear()
    ].join('/') + " " +
    pad(d.getHours()) + ":" +
    pad(d.getMinutes());
}

// In this example the code returns the correct date
var activity = {};
activity.startDate = '2017-07-02T00:00:00-08:00';
var startDate = GetDateSchedulerFormatted(activity.startDate); //start date 07/02/2017 00:00 d/m/yyy

var newDate = new Date(startDate); // returns Tue Aug 01 2017 00:00:00 GMT-0700 (Pacific Daylight Time) also 1 day off

var endDate = GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns

correct date 01/08/2017

Your issue is that you start with a valid ISO 8601 formatted string, '2017-07-02T00:00:00-08:00' but then reformat it into your own format in your own timezone (something like '02/07/2017 00:00' if your timezone is -0800), then parse that with the Date constructor, which is a very bad idea. It's likely treated as 7 February so I don't know how you can say it returns the correct date when you started with 2 July. And adding 1 month should be 2 August, not 1 August (though you did add 30 days rather than 1 month). Lastly, if you cross a daylight saving boundary, you may lose or pick up an hour so the date may be 23:00 the day before or go from 00:00 to 01:00.

Note that you have:

07/02/2017 00:00 d/m/yyy
                 ^^^^^^^

which is not 2 July, it's 7 February.

The rest of your issues are similar.

Anyhow, if you're happy with using a library, fine. Just thought I'd point out where you'd gone wrong.

Here's your code adapted so it runs here, showing the errors:

function GetDateSchedulerFormatted(date) {
  function pad(s) {
    return (s < 10) ? '0' + s : s;
  }
  var d = new Date(date);
  // yyy-MM-dd
  return [pad(d.getDate()),
      pad(d.getMonth() + 1),
      d.getFullYear()
    ].join('/') + " " +
    pad(d.getHours()) + ":" +
    pad(d.getMinutes());
}

// In this example the code returns the correct date
var activity = {};
activity.startDate = '2017-07-02T00:00:00-08:00';
var startDate = GetDateSchedulerFormatted(activity.startDate); //start date 07/02/2017 00:00 d/m/yyy

var newDate = new Date(startDate); // returns Tue Aug 01 2017 00:00:00 GMT-0700 (Pacific Daylight Time) also 1 day off

var endDate = GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns correct date 01/08/2017 00: 00 d / m / yyyy

console.log('activity.startDate: ' + activity.startDate +
  '\nstartDate : ' + startDate +
  '\nnewDate   : ' + GetDateSchedulerFormatted(newDate) +
  '\nendDate   : ' + endDate);

// In this next example it returns the date 1 year off

activity.startDate = '2016-12-12T00:00:00-08:00';
var startDate = GetDateSchedulerFormatted(activity.startDate); //returns 12/12/2016 00:00 d/m/yyy

var newDate = new Date(startDate); // returns Wed Jan 11 2017 00:00:00 GMT-0800 1 month ahead

var endDate = GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns 11/01/2017 00:0 d/m/yyy 
console.log('activity.startDate: ' + activity.startDate +
  '\nstartDate : ' + startDate +
  '\nnewDate   : ' + GetDateSchedulerFormatted(newDate) +
  '\nendDate   : ' + endDate);


// In this example the same exact date is returned


activity.startDate = '2017-02-01T00:00:00-08:00';
var startDate = GetDateSchedulerFormatted(activity.startDate); //returns 01/02/2017 00:00 d/m/yyy

var newDate = new Date(startDate); // returns Wed Feb 01 2017 00:00:00 GMT-0800

var endDate = GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns 01/02/2017 00:00 the same date, it 's not 30 days ahead
console.log('activity.startDate: ' + activity.startDate +
  '\nstartDate : ' + startDate +
  '\nnewDate   : ' + GetDateSchedulerFormatted(newDate) +
  '\nendDate   : ' + endDate);

// Then in this final example I get NaN / NaN / NaN NaN: NaN

activity.startDate = '2017-02-25T00:00:00-08:00';
var startDate = GetDateSchedulerFormatted(activity.startDate); //returns 25/02/2017 00:00 d/m/yyy

var newDate = new Date(startDate); // returns invalid date

var endDate = GetDateSchedulerFormatted(new Date(newDate.setTime(newDate.getTime() + 30 * 86400000))); //returns NaN/NaN/NaN NaN:NaN
console.log('activity.startDate: ' + activity.startDate +
  '\nstartDate : ' + startDate +
  '\nnewDate   : ' + GetDateSchedulerFormatted(newDate) +
  '\nendDate   : ' + endDate);
Community
  • 1
  • 1
RobG
  • 142,382
  • 31
  • 172
  • 209
0

Thanks To Jordan S and zzzzBov for the help. I decided to go with moment.js

a code snippet of how I returned the correct dates

var startDate = moment(activity.startDate);
var endDate = moment(activity.startDate);
endDate = endDate.clone().add(1, 'months').calendar();
endDate = moment(endDate);

startDate = startDate._d;
endDate = endDate._d;

The moment() function returned the moment date object.

You have to .clone() the object to add to it correctly, also .calendar() returned a different format DD/MM/YYYY.

And finally the variable _d refered to the date object generated from the library.

Train
  • 3,420
  • 2
  • 29
  • 59
  • 1
    Don't use `_d`. That's an internal field. Use a function like `.format()` or `.toDate()`. Using `_d` can lead to errors. – Matt Johnson-Pint Mar 27 '17 at 22:00
  • Also, you shouldn't need to produce a string with `.calendar()` and then parse it again. Keep your operations to a minimum. – Matt Johnson-Pint Mar 27 '17 at 22:02
  • @MattJohnson Thanks but `.format()` was returning wrong results. I had the same problem as above when using `.format()` – Train Mar 27 '17 at 23:02
  • You can pass parameters to format to control the output. For example, `.format('MM/DD/YYYY')`. Please read the moment docs, and https://maggiepint.com/2016/05/14/moment-js-shows-the-wrong-date/ – Matt Johnson-Pint Mar 27 '17 at 23:36