0

I'm making a business day calculator that takes a date and an offset and returns a date. So if you pass it Monday, Feb 27, 2017 and want to know the date 2 business days prior it returns Thursday, Feb 23, 2017. It also takes into account holidays, so if in the previous example, Feb 23, 2017 was a holiday it would return Feb 22, 2017.

I have a list of holidays in SQL Server in a date field in the format yyyy-mm-dd. The problem is that when they're converted to javascript Date objects it appends a timezone offset and in the case of Eastern Standard Time subtracts 5 hours from the date, rolling it back to the previous day.

new Date('2017-02-20');

becomes

Sun Feb 19 2017 19:00:00 GMT-0500 (Eastern Standard Time)

or

Sun Feb 19 2017 20:00:00 GMT-0400 (Eastern Daylight Time)

It seems to flip back and forth between standard time and daylight time randomly.

How do I prevent javascript from messing up my dates regardless of where the user lives? I only care about dates, not times.

Dexygen
  • 12,287
  • 13
  • 80
  • 147
Legion
  • 3,922
  • 8
  • 51
  • 95
  • Use https://momentjs.com/ and never worry about the lousy date object again. – Waxi Feb 21 '17 at 17:39
  • JavaScript is not "messing" anything up here; it's converting the dates _you_ are passing it. You need to localize your times before storing them. – lux Feb 21 '17 at 17:40
  • @lux Time is meaningless in this context. I want it to treat days as the fundamental unit of time in this instance. So Feb 20, is Feb 20 no matter where in the universe you live. – Legion Feb 21 '17 at 17:42
  • @Waxi I've used momentjs before, but I'm not clear on how it can help me get rid of the time component of a date. – Legion Feb 21 '17 at 17:43
  • You can try this: `var date = new Date("2017-02-20"); var dates = new Date(date.valueOf() + date.getTimezoneOffset() * 60000); console.log(dates);` – Bahadir Tasdemir Feb 21 '17 at 17:43
  • @Legion Because you can format the date in any way you like, for example, `moment(myDate, 'YYYY-MM-DD').format('MM/DD/YY')` which will trim off the time portion, but if you wanted to keep it, you would just include the proper syntax in your format method. Moment.js makes trivial work of all things date/time in JS. – Waxi Feb 21 '17 at 17:45
  • the problem doesn't seem to be with JavaScript, imo. If you truly used the `date` field in SQL and not the `datetime` field, the precision of that field is 1d, and it does not hold the time or offset at all. – Claies Feb 21 '17 at 17:52
  • @Claies That's correct. But as soon as you turn `2017-02-20` into a javascript `Date` object it assumes a timestamp of `00:00:00` and then applies a timezone offset. – Legion Feb 21 '17 at 17:55
  • 1
    If you want them to all use the same time, then just pass in the time yourself and let them all be midnight. `new Date('2017-02-21 00:00:00')` – Waxi Feb 21 '17 at 17:58
  • @Legion: timezones were not created by Brendan Eich. Storing simply a date is just not enough. In your database near your date you must store also the timezone shift of that day, plus the daylight saving shift of that day. Without this two additional information you simply will be not able to find out if today is the day you stored. – deblocker Feb 21 '17 at 18:00
  • Probably a duplicate of [*Why does Date.parse give incorrect results?*](http://stackoverflow.com/questions/2587345/why-does-date-parse-give-incorrect-results) – RobG Feb 21 '17 at 21:02
  • @AmericanSlime—`new Date('2017-02-20 00:00')` returns *Invalid date* in Safari. Parsing strings with the Date constructor (or Date.parse) is not recommended due to browser differences and inconsistent results. – RobG Feb 21 '17 at 21:04

1 Answers1

1

Parsing strings with the Date constructor (or Date.parse) is not recommended as it's largely implementation dependent. In recent browsers, ISO 8601 format dates are parsed as UTC, hence the reason they are offset by the host timezone offset. Older browsers will not parse ISO 8601 format dates at all, and some parse them as local.

Parsing a date requires a 2 line function, validation requires one more:

/* Parse an ISO 8601 format date string as local
** @param {string} s - date informat yyyy-mm-dd
** @returns {Date}
*/
function parseLocal(s) {
  var b = s.split(/\D/);
  var d = new Date(b[0], --b[1], b[2]);
  return d && d.getMonth() == b[1]? d : new Date(NaN);
}

console.log(parseLocal('2017-02-25').toString()); // 25 Feb 2017
console.log(parseLocal('2017-02-30').toString()); // Invalid date
RobG
  • 142,382
  • 31
  • 172
  • 209