Why do I get 2 different dates with new Date() when I pass a date vs. when I pass a date and time?
example:
Date.parse('2015-03-14')
// 1426291200000
new Date(1426291200000)
// Fri Mar 13 2015 17:00:00 GMT-0700 (PDT)
Date.parse('2015-03-14 00:00:00')
// 1426316400000
new Date(1426291400000)
// Fri Mar 13 2015 17:03:20 GMT-0700 (PDT)
Sorry but I cannot comment yet. The string you pass in Date.parse() must follow the
ECMAScript 5 ISO-8601 format support
.Reference link here
format specified in ECMAScript 2015
According to that, you the format of Date with time must be: '2015-03-14T00:00:00' instead of '2015-03-14 00:00:00'.
And since the time of Date.parse('2015-03-14') is at GMT+00 but Date.parse('2015-03-14T00:00:00') is at your time zone so if you add timezone GMT+00 to the second two time will be equal:
Date.parse('2015-03-14');
Date.parse('2015-03-14T00:00:00+00:00');
//1426291200000
Why do I get 2 different dates with new Date() when I pass a date vs. when I pass a date and time?
Because parsing strings with the Date constructor (and Date.parse, they are equivalent) is largely implementation dependent and therefore strongly recommended against.
Date.parse('2015-03-14')
// 1426291200000
new Date(1426291200000)
// Fri Mar 13 2015 17:00:00 GMT-0700 (PDT)
ISO 8601 format dates are treated as UTC in browsers compliant with ES5 and later (and UTC, local or invalid by earlier implementations), however not all browsers are compliant. Also, the specified behaviour is inconsistent with ISO 8601, which specifies that dates without a time zone are to be treated as local.
Date.parse('2015-03-14 00:00:00')
// 1426316400000
new Date(1426291400000)
// Fri Mar 13 2015 17:03:20 GMT-0700 (PDT)
The string '2015-03-14 00:00:00' is not compliant with ISO 8601, therefore parsing is entirely implementation dependent and may be treated as UTC, local or invalid. If made compliant with the addition of a "T":
"2015-03-14T00:00:00"
it should be treated as local (noting the previously mentioned caveat about non–compliant implementations).
It is very strongly recommended that you always manually parse strings. Use a library, one of the many good parsers that are around or just write your own function, 2 or 3 lines should suffice.
Related
This question already has answers here:
Why does Date.parse give incorrect results?
(11 answers)
Closed 2 years ago.
Came across this one tonight and thought I would throw it out there to see if anybody had a rational and clear explanation on what's going on.
I've declared two JavaScript date objects and passed in date strings to the constructor (same date).
In the first variable I have been explicit with the time and added 00:00.
In the second variable I have omitted the time (assuming that it would be midnight).
I've wrapped everything in a Self-Invoking Function but this has no bearing on the outcome.
I'm in GMT so I've used a date that is in BST (GMT+1).
(function() {
'use strict';
let a = new Date("2020-06-05 00:00");
let b = new Date("2020-06-05");
console.log(a);
console.log(b);
console.log(JSON.stringify(a));
console.log(JSON.stringify(b));
})();
I would have expected Fri Jun 05 2020 00:00:00 GMT+0100 (British Summer Time) on both cases but the second variable without the explicit 00:00 has been set to Fri Jun 05 2020 01:00:00 GMT+0100.
Any explanations?
It appears that a date inside BST (+1) without a time assumes it's +1.
Scenario doesn't happen with new Date("2020-01-05"); for example. This is outside BST and therefore +0.
Fri Jun 05 2020 00:00:00 GMT+0100 (British Summer Time)
Fri Jun 05 2020 01:00:00 GMT+0100 (British Summer Time)
"2020-06-04T23:00:00.000Z"
"2020-06-05T00:00:00.000Z"
Due to the ECMA specs:
ECMA-262 Date(value) constructor specs
Date.parse
If the String conforms to the Date Time String Format, substitute values take the place of absent format elements. When the MM or DD elements are absent, "01" is used. When the HH, mm, or ss elements are absent, "00" is used. When the sss element is absent, "000" is used. When the UTC offset representation is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time.
According to ECMA specs:
Date only forms without timezone are interpreted as UTC.
Date-Time without timezone are interpreted as local time.
Generally you are better off just using the full ISO-like form with timezone attached. But since these are spec-compliant, if you always keep it under control it's safe to depend on this behavior, albeit it might get confusing sometimes for both you and others, especially when it comes to server-side code and DB storage.
Output from Chrome 66 debug console with Hongkong timezone:
Valid date:
new Date('2018-06-30')
Sat Jun 30 2018 08:00:00 GMT+0800 (China Standard Time)
Invalid date gives T+1 value!
new Date('2018-06-31')
Sun Jul 01 2018 08:00:00 GMT+0800 (China Standard Time)
Finally... and invalid date error.
new Date('2018-06-32')
Invalid Date
Why does June 31 give T+1?
The JavaScript Date object is happy to handle unit rollover for you, that's all that's going on with the 2018-06-31 example — it's handling the rollover from June 30th to July 1st.
It doesn't do that for the 2018-06-32 example because 32 is an invalid value for the days field (whereas 31 isn't, it's just that June only has 30 days). The spec defines the valid ranges for the parts of the date/time string here, where we can see it says the valid values for the day of the month are 01 to 31 inclusive.
It's probably worth noting that the parsing of that ISO-8601-derived format (it isn't ISO-8601, quite) if you don't include a timezone indicator has a checkered history, sadly. ES5 specified ISO-8601 but got the meaning of the absense of a timezone indicator wrong (it said it should mean UTC, but ISO-8601 says it means local time); then ES2015 tried to fix that, but conforming to ES2016's rules would have broken a substantial amount of real-world code; and so it wasn't stabilized until ES2016, which says: Date-only forms (like yours) without a timezone indicator are UTC, date/time forms without one are local time. (It's been fine for years if you do include a timezone indicator.)
I get dates from the database in this format:
yyyy-mm-dd
When I create a javascript Date object using this string, it builds a day before the date.
You can test this in your console:
var d = new Date("2015-02-01");
d
You will get January 31st! I've tested many theories, but none answer the question.
The day is not zero-based, otherwise it would give Feb 00, not Jan 31
It's not performing a math equation, subtracting the day from the month and/or year
Date(2015-02-01) = Wed Dec 31 1969
Date("2015-01") = Wed Dec 31 2014
It is not confusing the day for the month
Date("2015-08-02") = Sat Aug 01 2015
If this were true the date would be Feb 08 2015
If you create a Date using a different format, it works fine
Date("02/01/2015") = Feb 1st, 2015
My conclusion is that js does this purposefully. I have tried researching 'why' but can't find an explanation. Why does js build dates this way, but only with this format? Is there a way around it, or do I have to build the Date, then set it to the next day?
PS: "How to change the format of the date from the db" is not what I'm asking, and that is why I'm not putting any db info here.
Some browsers parse a partial date string as UTC and some as a local time,
so when you read it the localized time may differ from one browser to another
by the time zone offset.
You can force the Date to be UTC and add the local offset if you
want the time to be guaranteed local:
1. set UTC time:
var D= new Date("2015-02-01"+'T00:00:00Z');
2. adjust for local:
D.setMinutes(D.getMinutes()+D.getTimezoneOffset());
value of D: (local Date)
Sun Feb 01 2015 00:00:00 GMT-0500 (Eastern Standard Time)
Offset will be whatever is local time.
Some differences between browsers when time zone is not specified in a parsed string:
(tested on Eastern Standard Time location)
(new Date("2015-02-01T00:00:00")).toUTCString();
Firefox 35: Sun, 01 Feb 2015 05:00:00 GMT
Chrome 40: Sun, 01 Feb 2015 00:00:00 GMT
Opera 27: Sun, 01 Feb 2015 00:00:00 GMT
IE 11: Sun, 01 Feb 2015 05:00:00 GMT
IE and Firefox set the Date as if it was local, Chrome and Opera as if it was UTC.
In javascript, Date objects are internally represented as the number of milliseconds since Jan 1st 1970 00:00:00 UTC. So instead of thinking of it as a "date" in the normal sense, try thinking of a Date object as a "point in time" represented by an integer number (without timezone).
When constructing your Date object using a string, you are actually just calling the parse function. Most date time formats (including ISO 8601) allow you to reduce the precision of a date string.
For reduced precision, any number of values may be dropped from any
of the date and time representations, but in the order from the least
to the most significant.
e.g. 2015-02-01 would represent the day February 1st 2015.
This causes a dilemma for javascript because a Date object is always accurate to the millisecond. Javascript cannot store a reduced accuracy date since it is just an integer of milliseconds since 1st Jan 1970. So it does the next best thing which is to assume a time of midnight (00:00:00) if not specified, and a timezone of UTC if not specified.
All valid javascript implementations should give the same result for this:
var d = new Date("2015-02-01");
alert(d.getTime());
1422748800000
The out-by-1-day issue comes when outputting the date either to some (often unclear) debugger or using the getter methods because the local timezone is used. In a browser, that will be your operating systems timezone. Anyone "west" of Greenwich Mean Time may see this problem because they have a negative UTC offset. Please note there are UTC equivalent functions too which use the UTC timezone, if you are really just interested in representing a date rather than a point in time.
I'm trying to convert a date string into a date object without changing the timezone. Here is the standard behavior:
new Date ("2014-10-24T00:00:00")
result
Thu Oct 23 2014 19:00:00 GMT-0500 (Central Daylight Time)
I am able to reverse the timezone by getting the offset in minutes, multiplying it by 60,000, and then adding that to the new string date.
new Date(new Date("2014-10-24T00:00:00").getTime() + new Date().getTimezoneOffset()*60000)
This works, but it seems like there must be a better way that doesn't require created three date objects.
Do not parse strings using the Date constructor. It calls Date.parse which, despite being standardised for one version of ISO 8601 strings in ES5, is still almost entirely implementation dependent.
I'm trying to convert a date string into a date object without changing the timezone.
> new Date ("2014-10-24T00:00:00")
That string will be treated differently in different browsers. If you want it to be treated as UTC, then it is simple to parse yourself:
function parseISOAsUTC(s) {
var b = s.split(/\D/);
return new Date(Date.UTC(b[0],--b[1],b[2],b[3],b[4],b[5],(b[6]||0)));
}
console.log(parseISOAsUTC('2014-10-24T00:00:00').toISOString()); // 2014-10-24T00:00:00.000Z
Now you can be certain that the string will be treated as UTC in all browsers in use (including the 20% or so still using IE 8 and lower).
If, on the other hand, you want the string to be treated as a local time, then just remove the Date.UTC part:
function parseISOAsLocal(s) {
var b = s.split(/\D/);
return new Date(b[0],--b[1],b[2],b[3],b[4],b[5],(b[6]||0));
}
console.log(parseISOAsLocal('2014-10-24T00:00:00')); // Fri 24 Oct 2014 00:00:00 <local timezone>
Here is an implementation of zerkms's solution.
new Date("2014-10-24T00:00:00".replace('T', ' '))
result
Fri Oct 24 2014 00:00:00 GMT-0500 (Central Daylight Time)
What's going on here:
> new Date('Apr 15 2013');
Mon Apr 15 2013 00:00:00 GMT+0100 (GMT Daylight Time)
> new Date('04/15/2013');
Mon Apr 15 2013 00:00:00 GMT+0100 (GMT Daylight Time)
> new Date('2013-04-15');
Mon Apr 15 2013 01:00:00 GMT+0100 (GMT Daylight Time)
Obviously, one is being interpreted as UTC time, while the other two are being interpreted as local time. What causes the difference in parsing?
From the specification:
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 called out in Date Time String Format (15.9.1.15). If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
Of all the formats you provided, only '2013-04-15' is officially supported, so the others seem to fall back to implementation-dependent behavior.
Your third example is the only one that is actually explained by the spec. When you call the Date constructor with a single argument, this is what happens (where v is the string passed to the constructor):
Parse v as a date, in exactly the same manner as for the parse method (15.9.4.2); let V be the time value for this date.
The parse method will attempt to parse the string (emphasis added):
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 called out in Date Time String Format (15.9.1.15).
If the String does not conform to that format the function may fall back to any implementation-specific heuristics or implementation-specific date formats.
And the "Date Time String Format" is YYYY-MM-DDTHH:mm:ss.sssZ, and also defines a shorter version of the form YYYY-MM-DD, which is what you use in your third example.
For the other examples, the parse will fail and the behaviour is implementation-defined.
The Date constructor delegates to Date.parse, and it appears that Date.parse has two variants:
Given a string representing a time, parse returns the time value. It
accepts the RFC2822 / IETF date syntax (RFC2822 Section 3.3), e.g.
"Mon, 25 Dec 1995 13:30:00 GMT". It understands the continental US
time-zone abbreviations, but for general use, use a time-zone offset,
for example, "Mon, 25 Dec 1995 13:30:00 GMT+0430" (4 hours, 30 minutes
east of the Greenwich meridian). If you do not specify a time zone,
the local time zone is assumed. GMT and UTC are considered equivalent.
Alternatively, the date/time string may be in
ISO 8601 format. Starting with JavaScript 1.8.5 (Firefox 4), a subset
of ISO 8601 is supported. For example, "2011-10-10" (just date) or
"2011-10-10T14:48:00 (date and time) can be passed and parsed.
Clearly, these behave differently with respect to local timezones
Edit: Looks like this is implementation defined