When is the month in a Date-object zero indexed - javascript

I know, because zero indexed month, for the date
Thu Mar 02 2017
I have to write
new Date(2017, 2, 2);
The same if I use a variable
var thismonth = 2; new Date(2017, thismonth, 2);
But, if I say
var thisday = [2017, 2, 2]; new Date(thisday);
I get a result with a one indexed month
Thu Feb 02 2017
This means, I can use the array-vars like the date-vars in the real life
var thisday = [2017, 3, 2]; new Date(thisday);
and get the (un)expected result
Thu Mar 02 2017
Thats nice, but confusing! Why this behaviour? Is there a rule?

That's an interesting find! However there is no documented Date constructor that accepts an array of values such as [2017, 3, 2], so what you are getting in this case isn't something you should rely on.
You can see that among your examples, only the first one is documented:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Date

I don't think the code does what you think it does. The Date constructor does not accept arrays. Quoting the spec
Let v be ? ToPrimitive(value).
If Type(v) is String, then
Let tv be the result of parsing v as a date, in exactly the same manner as for the parse method (20.3.3.2). If the parse resulted in an abrupt completion, tv is the Completion Record.
which means you actually pass 2017,3,2 as a string. Now what happens to that string? Well, check the spec again.
Otherwise, parse interprets the resulting String as a date and time; it returns a Number, the UTC time value corresponding to the date and time. The String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String. The function first attempts to parse the format of the String according to the rules (including extended years) called out in Date Time String Format (20.3.1.16).
Interesting, another link to the spec
Your format doesn't follow the YYYY-MM-DDTHH:mm:ss.sssZ format or date only YYYY-MM-DD which means we're back to the previous spec section which says
If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
So basically you should not rely on it. Depending on the JS engine/browser the date might not be parsed at all. It looks like V8 accepts not only colons/dashes as separators, but also commas, which is why it works.
Edit: found it
https://github.com/v8/v8/blob/68960eeb763f93dcedb37d5b663b1019192b7f36/src/builtins/builtins-date.cc#L115
which calls
https://github.com/v8/v8/blob/68960eeb763f93dcedb37d5b663b1019192b7f36/src/dateparser-inl.h#L16
which has plenty of comments and weird stuff to keep searching if you want.

Related

Understanding JavaScript dates in British Summer Time

I live in the UK. If I create a date object for 1st June, I would have expected .getUTCDate() to return the "correct" day of the month as 1, but instead it returns 31 and .getDate(), which I thought was meant to return the locale date returns 1.
new Date(2020,5,1).getUTCDate() // 31
new Date(2020,5,1).getDate(); // 1
Why is this? Is it because new Date(2020,5,1) is already converted to local time or something? I thought dates were stored universally, and it is only when the date is printed out that the locale rules are applied? I've read all the MDN docs and still don't understand, I would really appreciate if someone can walk through the steps of what happens for the above to return 31 and 1.
A brief explanation of how ECMAScript Date constructor works.
Single string values are parsed and converted to Dates in weird and wonderful ways that are mostly implementation dependent, see below. So all of the following ignores parsing and assumes the Date constructor is called with at least two arguments, year and month. In this case, string values are converted to number as if by Number(value).
All the non–UTC methods work in local time, i.e. based on the host system date, time and timezone offset settings. When creating a Date object using the constructor and values for year, month, day, etc. then any missing values are treated as 0 except for the day, which defaults to 1.
So
new Date(2020, 5, 1)
creates a date based on the host (local) settings for date, time and timezone offset for that date and time, respecting historical changes like daylight saving and other adjustments (common before 1900) as if by:
new Date(2020, 5, 1, 0, 0, 0, 0); // Local 1 Jun 2020 00:00:00.000
So getUTCDate will return 31 (i.e. 31 May) for systems with a timezone offset that is less than zero and 1 (i.e. 1 Jun) for any with an offset of zero or greater. The largest positive offset in common use is +14 and the smallest is -10.
If you want to create a Date based on UTC values, then use the Date.UTC method, whcih returns a number, the time value, so you have to pass it to the Date constructor to create a Date object:
new Date(Date.UTC(2020, 5, 1)).getUTCDate(); // 1 regardless of host settings
Parsing strings: new Date(string) and Date.parse(string)
This where the Date object crashes and burns. While the constructor mostly uses local, it can also use UTC even where UTC is not specified. Parsing of strings is almost entirely implementation dependent except for the 3 formats supported by ECMA-262. Even the supported formats are not parsed consistently across implementations so the general advice is do not use the built–in parser. See Why does Date.parse give incorrect results?

Stop converting date only string in javascript [duplicate]

I have a string representing the current time: 2015-11-24T19:40:00. How do I parse this string in Javascript to get a Date represented by this string as the LOCAL TIME? Due to some restriction, I cannot use the library moment, but jquery is allowed. I know that someone has asked this question before, but the answer used moment
For example, if I run the script in California, then this string would represent 7PM pacific time, but if I run the script in NY then this string would represent Eastern Time?
I tried the following but Chrome and Firefox give me different results:
var str = "2015-11-24T19:40:00";
var date = new Date(str);
Chrome consumes it as UTC time (Tue Nov 24 2015 11:40:00 GMT-0800 (Pacific Standard Time)),
but Firefox consumes it as my local PACIFIC time (Tue Nov 24 2015 19:40:00 GMT-0800 (Pacific Standard Time))
I tried adding "Z" to str, like this var date = new Date(str+"Z");, then both browsers give me UTC time. Is there any similar letter to "Z" which tells all browsers (at least chrome, Firefox and Safari) to parse the string as local time zone?
Parsing of date strings using the Date constructor or Date.parse (which are essentially the same thing) is strongly recommended against.
If Date is called as a function and passed an ISO 8601 format date string without a timezone (such as 2015-11-24T19:40:00), you may get one of the following results:
Pre ES5 implementaitons may treat it as anything, even NaN (such as IE 8)
ES5 compliant implementations will treat it as UTC timezone
ECMAScript 2015 compliant implementations will treat it as local (which is consistent with ISO 8601)
A Date object has a time value which is UTC, and an offset based on system settings. When you send a Date to output, what you see is usually the result of Date.prototype.toString, which is an implementation dependent, human readable string representing the date and time, usually in a timezone based on system settings.
The best way to parse date strings is to do it manually. If you are assured that the format is consistent and valid, then parsing an ISO format string as a local date is as simple as:
/* #param {string} s - an ISO 8001 format date and time string
** with all components, e.g. 2015-11-24T19:40:00
** #returns {Date} - Date instance from parsing the string. May be NaN.
*/
function parseISOLocal(s) {
var b = s.split(/\D/);
return new Date(b[0], b[1]-1, b[2], b[3], b[4], b[5]);
}
document.write(parseISOLocal('2015-11-24T19:40:00'));
Note that parsing of ISO strings using Date.parse only accepts UTC, it does not accept any other timezone designation (noting the above behaviour if it's missing).
A variation on RobG's terrific answer.
Note that this will require that you run bleeding edge JavaScript as
it relies on the arrow notation and spread operator.
function parseDateISOString(s) {
let ds = s.split(/\D/).map(s => parseInt(s));
ds[1] = ds[1] - 1; // adjust month
return new Date(...ds);
}
Note that this doesn't take into account if the date/time given is in any timezone. It will assume local time. You can change new Date for Date.UTC to assume UTC.
There are technical reasons for why you would write you code like this. For example, here we apply the correct number of arguments, with their corresponding expected type. It's true that the Date constructor will turn strings into numbers but what could be happening is that there's a deoptimization taking place where the optimized code is expecting a number but sees a string and takes a slower path. Not a big deal but I try to write my JavaScript to avoid such things. We also won't be indexing outside the bounds of the array if less than 6 components can be found in the string which is also one of those things you can do in JavaScript but it has subtle deoptimization caveats.
Where Date is called as a constructor with more than one argument, the specified arguments represent local time.
I also have a much faster way than using the string.split() because we already know where the numbers are:
return new Date(Number(date.substring(0, 4)), Number(date.substring(5, 7))-1,
Number(date.substring(8, 10)), Number(date.substring(11, 13)),
Number(date.substring(14, 16)), Number(date.substring(17, 19)));
This will work with and/or without the 'T' and 'Z' strings and still has decent performance. I added the explicit Number conversion (faster and better than parseInt) so this also compiles in TypeScript.
Number

How to find the timeZoneOffset added in Date object [duplicate]

I have a string representing the current time: 2015-11-24T19:40:00. How do I parse this string in Javascript to get a Date represented by this string as the LOCAL TIME? Due to some restriction, I cannot use the library moment, but jquery is allowed. I know that someone has asked this question before, but the answer used moment
For example, if I run the script in California, then this string would represent 7PM pacific time, but if I run the script in NY then this string would represent Eastern Time?
I tried the following but Chrome and Firefox give me different results:
var str = "2015-11-24T19:40:00";
var date = new Date(str);
Chrome consumes it as UTC time (Tue Nov 24 2015 11:40:00 GMT-0800 (Pacific Standard Time)),
but Firefox consumes it as my local PACIFIC time (Tue Nov 24 2015 19:40:00 GMT-0800 (Pacific Standard Time))
I tried adding "Z" to str, like this var date = new Date(str+"Z");, then both browsers give me UTC time. Is there any similar letter to "Z" which tells all browsers (at least chrome, Firefox and Safari) to parse the string as local time zone?
Parsing of date strings using the Date constructor or Date.parse (which are essentially the same thing) is strongly recommended against.
If Date is called as a function and passed an ISO 8601 format date string without a timezone (such as 2015-11-24T19:40:00), you may get one of the following results:
Pre ES5 implementaitons may treat it as anything, even NaN (such as IE 8)
ES5 compliant implementations will treat it as UTC timezone
ECMAScript 2015 compliant implementations will treat it as local (which is consistent with ISO 8601)
A Date object has a time value which is UTC, and an offset based on system settings. When you send a Date to output, what you see is usually the result of Date.prototype.toString, which is an implementation dependent, human readable string representing the date and time, usually in a timezone based on system settings.
The best way to parse date strings is to do it manually. If you are assured that the format is consistent and valid, then parsing an ISO format string as a local date is as simple as:
/* #param {string} s - an ISO 8001 format date and time string
** with all components, e.g. 2015-11-24T19:40:00
** #returns {Date} - Date instance from parsing the string. May be NaN.
*/
function parseISOLocal(s) {
var b = s.split(/\D/);
return new Date(b[0], b[1]-1, b[2], b[3], b[4], b[5]);
}
document.write(parseISOLocal('2015-11-24T19:40:00'));
Note that parsing of ISO strings using Date.parse only accepts UTC, it does not accept any other timezone designation (noting the above behaviour if it's missing).
A variation on RobG's terrific answer.
Note that this will require that you run bleeding edge JavaScript as
it relies on the arrow notation and spread operator.
function parseDateISOString(s) {
let ds = s.split(/\D/).map(s => parseInt(s));
ds[1] = ds[1] - 1; // adjust month
return new Date(...ds);
}
Note that this doesn't take into account if the date/time given is in any timezone. It will assume local time. You can change new Date for Date.UTC to assume UTC.
There are technical reasons for why you would write you code like this. For example, here we apply the correct number of arguments, with their corresponding expected type. It's true that the Date constructor will turn strings into numbers but what could be happening is that there's a deoptimization taking place where the optimized code is expecting a number but sees a string and takes a slower path. Not a big deal but I try to write my JavaScript to avoid such things. We also won't be indexing outside the bounds of the array if less than 6 components can be found in the string which is also one of those things you can do in JavaScript but it has subtle deoptimization caveats.
Where Date is called as a constructor with more than one argument, the specified arguments represent local time.
I also have a much faster way than using the string.split() because we already know where the numbers are:
return new Date(Number(date.substring(0, 4)), Number(date.substring(5, 7))-1,
Number(date.substring(8, 10)), Number(date.substring(11, 13)),
Number(date.substring(14, 16)), Number(date.substring(17, 19)));
This will work with and/or without the 'T' and 'Z' strings and still has decent performance. I added the explicit Number conversion (faster and better than parseInt) so this also compiles in TypeScript.
Number

Javascript: parse a string to Date as LOCAL time zone

I have a string representing the current time: 2015-11-24T19:40:00. How do I parse this string in Javascript to get a Date represented by this string as the LOCAL TIME? Due to some restriction, I cannot use the library moment, but jquery is allowed. I know that someone has asked this question before, but the answer used moment
For example, if I run the script in California, then this string would represent 7PM pacific time, but if I run the script in NY then this string would represent Eastern Time?
I tried the following but Chrome and Firefox give me different results:
var str = "2015-11-24T19:40:00";
var date = new Date(str);
Chrome consumes it as UTC time (Tue Nov 24 2015 11:40:00 GMT-0800 (Pacific Standard Time)),
but Firefox consumes it as my local PACIFIC time (Tue Nov 24 2015 19:40:00 GMT-0800 (Pacific Standard Time))
I tried adding "Z" to str, like this var date = new Date(str+"Z");, then both browsers give me UTC time. Is there any similar letter to "Z" which tells all browsers (at least chrome, Firefox and Safari) to parse the string as local time zone?
Parsing of date strings using the Date constructor or Date.parse (which are essentially the same thing) is strongly recommended against.
If Date is called as a function and passed an ISO 8601 format date string without a timezone (such as 2015-11-24T19:40:00), you may get one of the following results:
Pre ES5 implementaitons may treat it as anything, even NaN (such as IE 8)
ES5 compliant implementations will treat it as UTC timezone
ECMAScript 2015 compliant implementations will treat it as local (which is consistent with ISO 8601)
A Date object has a time value which is UTC, and an offset based on system settings. When you send a Date to output, what you see is usually the result of Date.prototype.toString, which is an implementation dependent, human readable string representing the date and time, usually in a timezone based on system settings.
The best way to parse date strings is to do it manually. If you are assured that the format is consistent and valid, then parsing an ISO format string as a local date is as simple as:
/* #param {string} s - an ISO 8001 format date and time string
** with all components, e.g. 2015-11-24T19:40:00
** #returns {Date} - Date instance from parsing the string. May be NaN.
*/
function parseISOLocal(s) {
var b = s.split(/\D/);
return new Date(b[0], b[1]-1, b[2], b[3], b[4], b[5]);
}
document.write(parseISOLocal('2015-11-24T19:40:00'));
Note that parsing of ISO strings using Date.parse only accepts UTC, it does not accept any other timezone designation (noting the above behaviour if it's missing).
A variation on RobG's terrific answer.
Note that this will require that you run bleeding edge JavaScript as
it relies on the arrow notation and spread operator.
function parseDateISOString(s) {
let ds = s.split(/\D/).map(s => parseInt(s));
ds[1] = ds[1] - 1; // adjust month
return new Date(...ds);
}
Note that this doesn't take into account if the date/time given is in any timezone. It will assume local time. You can change new Date for Date.UTC to assume UTC.
There are technical reasons for why you would write you code like this. For example, here we apply the correct number of arguments, with their corresponding expected type. It's true that the Date constructor will turn strings into numbers but what could be happening is that there's a deoptimization taking place where the optimized code is expecting a number but sees a string and takes a slower path. Not a big deal but I try to write my JavaScript to avoid such things. We also won't be indexing outside the bounds of the array if less than 6 components can be found in the string which is also one of those things you can do in JavaScript but it has subtle deoptimization caveats.
Where Date is called as a constructor with more than one argument, the specified arguments represent local time.
I also have a much faster way than using the string.split() because we already know where the numbers are:
return new Date(Number(date.substring(0, 4)), Number(date.substring(5, 7))-1,
Number(date.substring(8, 10)), Number(date.substring(11, 13)),
Number(date.substring(14, 16)), Number(date.substring(17, 19)));
This will work with and/or without the 'T' and 'Z' strings and still has decent performance. I added the explicit Number conversion (faster and better than parseInt) so this also compiles in TypeScript.
Number

Date constructor: numeric arguments vs. string argument giving different dates in some cases

First of all, I think timezone probably has something to do with this. I'm in EST/EDT. Also, I'm testing this on chromium 17 / linux.
Now, let's say I create two dates like this:
// December 5
dateFromNumbers = new Date(2020, 11, 5);
dateFromString = new Date("2020-12-5");
It seems these dates should have identical timestamps, and they do:
+dateFromNumbers == +dateFromString; // true
...at least in this case. But in some cases, they don't:
// December 15
dateFromNumbers = new Date(2020, 11, 15);
dateFromString = new Date("2020-12-15");
+dateFromNumbers == +dateFromString; // false
What's going on here?
dateFromNumbers; // Tue Dec 15 2020 00:00:00 GMT-0500 (EST)
dateFromString; // Mon Dec 14 2020 19:00:00 GMT-0500 (EST)
Looks like dateFromString is 5 hours earlier than dateFromNumbers in this case (EST is GMT - 5, I'm sure it's related somehow).
It seems to affect the ends of months October through December. Here's a fiddle that makes it easy to see which days differ (unless you are red-green colorblind, in that case it may be difficult to see, my apologies).
http://jsfiddle.net/9gBfX/
What gives?
Notes:
You can set your system timezone to EST/EDT to see the jsfiddle example as I'm seeing it.
Date's month numbers are zero-based; the 11 is not a typo.
This issue appears in every year that I checked.
After looking in V8's source code:
// Specification:
// Accept ES5 ISO 8601 date-time-strings or legacy dates compatible
// with Safari.
<...>
// A string that matches both formats (e.g. 1970-01-01) will be
// parsed as an ES5 date-time string - which means it will default
// to UTC time-zone. That's unavoidable if following the ES5
// specification.
Reading the surrounding code, it seems that a date-time string with both month and day 2 symbols long is considered a valid ES5 date-time string. Further down in the ES5 parser, after it parses the dates and times, there's a comment:
// Successfully parsed ES5 Date Time String. Default to UTC if no TZ given.
In case of "YYYY-MM-DD", once the code gets that far, the ES5 parser has successfully parsed the entire string, so it returns before the legacy parser gets a chance to localize the timezone. Otherwise (month/day are one symbol long) it's treated as a "legacy" datetime and the legacy parser gets to deal with it and localize it.
Here's a simplified answer drawing from the other answers.
Date recognizes different string formats
Nonstandard dates
RFC 2282 dates
ES 5 dates
Most formats are interpreted as local dates
On page 14 of RFC 2282, we see:
The date and time-of-day SHOULD express local time.
Nonstandard dates are treated in similar fashion.
ES 5 format is interpreted as UTC
In section 15.9.1.15 of the ES 5 spec we see:
The value of an absent time zone offset is “Z”.
"Z" represents UTC time.
The tenth of October
ES 5 formatted dates require double-digit months and days. The months and days in the original post are not zero-padded. "2020-9-9" is not a valid ES 5 date representation; it's a non-standard format, so it's interpreted in local time. "2020-10-10" is a valid ES 5 date representation, so it must be interpreted in UTC.
Possible workarounds
Don't use the string constructor / Date.parse!
Change the separator character so the format never matches the ES 5 format.
Specify a timezone.
Adjust dates to local time. If they have hours or minutes: date.setMinutes(date.getTimezoneOffset()); (this seems to work, anyway).
relaying on this post, it seems that the Date's string argument constructor is implementation sensitive, due to various Date.parse() implementations by browsers.
your measurements are correct, and you probably should avoid using this constructor altogether, if you wish your browser to parse EST correctly.
Using "-" as a date separator for USA dates confuses some browsers, some will perform date arithmetic others will return NaN, so use the "/" date separator. A culture aware solution is to use date.js, which is an outstanding JavaScript date handler that resolves issues like the one you have pointed out (http://www.datejs.com/). Using the parse method removes all confusion:
Date.parse("2020-12-15").toString() // yields the correct date ("Tue Dec 15 00:00:00 PST 2020").
It looks like the date constructor requires spaces rather than '-'. Its the recommended way. Check out this link:
3.3. Date and Time Specification
Though foldingwhite space is permitted throughout the date-time
specification, it is RECOMMENDED that a single space be used in
each place that FWS appears (whether it is required or optional)
Also check out this link:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date
dateString: String value representing a date. The string should be in a
format recognized by the parse method (IETF-compliant RFC 2822
timestamps).
I tried following code and it returns true
dateFromNumbers = new Date(2020, 11, 15);
dateFromString = new Date("2020 12 15");
alert(+dateFromNumbers == +dateFromString);​
Also its not a problem start with the month of October, it has do with the double digit months. If I try the same method with September then:
dateFromNumbers = new Date(2020, 8, 15);
dateFromString = new Date("2020-09-15");
alert(+dateFromNumbers == +dateFromString);​ // This returns false
But if I use single digit for September then it returns true
dateFromNumbers = new Date(2020, 8, 15);
dateFromString = new Date("2020-9-15");
alert(+dateFromNumbers == +dateFromString);​ // This returns true
And if use space with double digit September, then it returns true
dateFromNumbers = new Date(2020, 8, 15);
dateFromString = new Date("2020 09 15");
alert(+dateFromNumbers == +dateFromString);​//This returns true

Categories