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).