Welton122 Welton122 - 1 month ago 13
Javascript Question

JavaScript Date object building wrong date

I have an issue whereby a JavaScript Date object is being built incorrectly. I'm not sure how best to resolve this issue. This is what I'm doing:

var date = new Date('2016-11-31');


Now. I understand that November does not have 31 days. This is an intended accident. The problem is rather than this date simply failing to construct, it actually builds as December 1st??

Now I think this maybe a locale issue, as when attempting the same in JSFiddle (or StackOverflow snippets as below), I get October 31st?



var date = new Date('2016-11-31');
console.log(date);





Does anyone know how I can get around this issue?

Answer

The problem is rather than this date simply failing to construct, it actually builds as December 1st??

Now I think this maybe a locale issue...

No, it's a design feature. Date is expressly designed to handle rollover between dates (in this case, I think it's probably down to the abstract DateFromTime operation), which is why things like myDate.setDate(myDate.getDate() + 1) can reliably be used to increment the day, even when going from one month to the next.

In a comment you've asked if there's a way that will fail if the date string is invalid in the way you identify in your question; the only way I know is to check the result afterward:

// Note: This example only handles YYYY-MM-DD, not times
function strictCreateDate(str) {
  var date = new Date(str);
  if (isNaN(date.getTime())) {
      throw new Error("Invalid date: '" + str + "'");
  }
  var parts = str.split("-");
  if (+parts[0] != date.getFullYear() ||
      +parts[1] != date.getMonth() + 1 ||
      +parts[2] != date.getDate()) {
      throw new Error("Invalid date: '" + str + "'");
  }
  return date;
}

(If you prefer to return an invalid Date instance rather than throwing an error, change the first throw above to return date; and the second one to return new Date(NaN);.)

Example:

// Note: This example only handles YYYY-MM-DD, not times
function strictCreateDate(str) {
  var date = new Date(str);
  if (isNaN(date.getTime())) {
      throw new Error("Invalid date: '" + str + "'");
  }
  var parts = str.split("-");
  if (+parts[0] != date.getFullYear() ||
      +parts[1] != date.getMonth() + 1 ||
      +parts[2] != date.getDate()) {
      throw new Error("Invalid date: '" + str + "'");
  }
  return date;
}
function test(str) {
  try {
    var dt = strictCreateDate(str);
    console.log(str + " => " + dt.toISOString());
  }
  catch (e) {
    console.log(str + " => " + e.message);
  }
}
test('2016-11-30');
test('2016-11-31');
test('2016-08-01');

Note: There was an error in the ES5 specification which defined the behavior of new Date("2016-08-01") to interpret the string as UTC, in contravention of the ISO-8601 standard saying it should be interpreted as "local time" because there is no time zone indicator. This specification error was fixed in ES2015, but unfortunately for some time there were browsers in the wild (Firefox in particular) that continued the ES5 version. I just tested both recent Chrome and Firefox, and they both now follow the ES2015 standard, but it's important to beware. If there's any chance your code may run on browsers implementing incorrect behavior, add a timezone indicator in the standard +/-HH:MM format to the end of your string (or add a "Z" to it and use the UTC functions [getUTCFullYear, etc.] instead of the local time functions [getFullYear, etc.] above).