Prevent JavaScript from changing the time to GMT - javascript

The following outputs a time that is 4 hours less than what was input:
X = new Date('2015-07-09T14:18:12.430')
$('body').append(X)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

When you do no specify the timezone:
ECMAScript-5 compliant browsers will assume UTC timezone:
The value of an absent time zone offset is "Z".
ECMAScript-6 compliant browsers will assume local timezone:
If the time zone offset is absent, the date-time is interpreted as a
local time.
Use the long Date constructor which assumes local timezone:
var X = new Date(2015, 7 - 1, 9, 14, 18, 12, 430);
alert(X);

When you omit the timezone information on the end of an ISO 8601 formatted date-time the majority of computer implementations default to +0000, this means Date interprets 2015-07-09T14:18:12.430 the same as 2015-07-09T14:18:12.430+0000 or 2015-07-09T14:18:12.430Z
It looks like you were expecting it to be interpreted as your local time rather than in UTC, you have 3 options
Append your local timezone offset to the end
Write it differently so it will parse as you expect
Create a second Date using the details from the UTC fields of the first Date
function myParser(iso_style) {
var d = new Date(iso_style);
return new Date(
d.getUTCFullYear(),
d.getUTCMonth(),
d.getUTCDate(),
d.getUTCHours(),
d.getUTCMinutes(),
d.getUTCSeconds(),
d.getUTCMilliseconds()
);
}
You can't simply adjust by your local offset as you would experience unexpected behaviour if the time crosses a date that would change that offset e.g. a daylight savings boundry
As Salman A's answer points out the current ES 6 Draft defines that an omitted timezone should be interpreted as meaning the client's local timezone. This will make the behaviour inconsistent as different JavaScript implementations change over so I strongly recommend you always use a timezone when writing ISO 8601.

A quick and dirty workaround:
X = new Date('2015-07-09T14:18:12.430');
$('body').append( X.toString().split('GMT')[0] );
It works by turning the date object into a string, then splitting it into an array around the pattern GMT. So you would have 2 elements in the array: Thu Jul 09 2015 10:18:12 and 0400 (EDT). Since you want to drop everything after the GMT, you just use the element in the 0 index of the array
I misunderstood the question. If you use X.toUTCString(), it may fix your issue.

Related

What is the difference between new Date("2017-01-01") and new Date("2017-1-1")? [duplicate]

This question already has answers here:
Why isn't "2016-02-16" equal to "2016-02-16 00:00"?
(5 answers)
Closed 5 years ago.
I input new Date("2017-01-01") in chrome console, the output shows its hour is 8, but new Date("2017-01-1") and new Date("2017-1-01") shows their hour are both 0, so how does new Date(dateString) parse?
new Date("2017-01-01")
// Sun Jan 01 2017 08:00:00 GMT+0800 (中国标准时间)*
new Date("2017-01-1")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
new Date("2017-1-1")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
new Date("2017-1-01")
// Sun Jan 01 2017 00:00:00 GMT+0800 (中国标准时间)*
"2017-01-01" follows the ISO standard ES5 Date Time String format (simplification of ISO 8601 Extended Format), and therefore it is in UTC time, which is 8am in China. All other strings are parsed as local time in Chrome1.
1 Relevant source code in Chromium: https://cs.chromium.org/chromium/src/v8/src/dateparser-inl.h?type=cs&l=16
Date parsing in Chromium follows the standard ES5 rules as well as these extra rules:
Any unrecognized word before the first number is ignored.
Parenthesized text is ignored.
An unsigned number followed by : is a time value, and is added to the TimeComposer. A number followed by :: adds a second zero as well. A number followed by . is also a time and must be followed by milliseconds. Any other number is a date component and is added to DayComposer.
A month name (or really: any word having the same first three letters as a month name) is recorded as a named month in the Day composer.
A word recognizable as a time-zone is recorded as such, as is (+|-)(hhmm|hh:).
Legacy dates don't allow extra signs (+ or -) or unmatched ) after a number has been read (before the first number, any garbage is allowed).
Any strings that satisfy the ES5 rules and the rules above will be parsed using ES5 rules. This means "1970-01-01" will be in UTC time-zone not in local time-zone.
What does that mean?
First note that "2017-01-01" is parsed in UTC time because it is a "date" string instead of a "date-time" string, and it matches the ES5 definition of a "date" string. If time is attached, then it will follow the ISO standard and parse it in local time.
Examples:
2017-01-01 - Jan 1, 2017 in UTC time
2017-01-01T00:00 - Jan 1, 2017 in local time
2017-1-1 - Jan 1, 2017 in local time
2017-(hello)01-01 - Jan 1, 2017 in local time
may 2017-01-01 - May 1, 2017 in local time
mayoooo 2017-01-01 - May 1, 2017 in local time
"jan2017feb-mar01apr-may01jun" - Jun 1, 2017 in local time
difference between new Date("2017-01-01") and new Date("2017-1-1")
new Date("2017-01-01") is in-spec (more below). new Date("2017-1-1") is not, and so falls back on any "...implementation-specific heuristics or implementation-specific date formats" the JavaScript engine wants to apply. E.g., you have no guarantee how (or whether) it will successfully parse and if so, whether it will be parsed as UTC or local time.
Although new Date("2017-01-01") is in-spec, sadly what browsers are supposed to do with it has been a moving target, because it doesn't have a timezone indicator on it:
In ES5 (December 2009), strings without a timezone indicator were supposed to be parsed as UTC. But that's at variance with the ISO-8601 standard the date/time format is based on, which says strings without a timezone indicator are meant to be local time, not UTC. So in ES5, new Date("2017-01-01") is parsed in UTC.
In ES2015 (aka ES6, June 2015), strings without a timezone indicator were supposed to be local time, not UTC, like ISO-8601. So in ES2015, new Date("2017-01-01") is parsed as local time.
But, that was changed again in ES2016 (June 2016) because browsers had been parsing date-only forms with - in them as UTC for years. So as of ES2016, date-only forms (like "2017-01-01") are parsed in UTC, but date/time forms (like "2017-01-01T00:00:00") are parsed in local time.
Sadly, not all JavaScript engines currently implement the spec. Chrome (as of this writing, v56) parses date/time forms in UTC even though they should be local time (so does IE9). But Chrome, Firefox, and IE11 (I don't have IE10 or Edge handy) all handle date-only forms correctly (as UTC). IE8 doesn't implement the ISO-8601 form at all (having been released before the ES5 spec was released).
When parsing dates, JavaScript interprets ISO dates as UTC time and other formats as local time.
As MDN article suggests,
Where the string is ISO 8601 date only, the UTC time zone is used to interpret arguments.
Given a date string of "March 7, 2014", parse() assumes a local time zone, but given an ISO format such as "2014-03-07" it will assume a time zone of UTC (ES5 and ECMAScript 2015). Therefore Date objects produced using those strings may represent different moments in time depending on the version of ECMAScript supported unless the system is set with a local time zone of UTC. This means that two date strings that appear equivalent may result in two different values depending on the format of the string that is being converted.
// 2017-03-28 is interpreted as UTC time,
// shown as 2017-03-28 00:00:00 in UTC timezone,
// shown as 2017-03-28 06:00:00 in my timezone:
console.log("ISO dates:");
var isoDates = [new Date("2017-03-28")];
for (var dt of isoDates)
{
console.log(dt.toUTCString() + " / " + dt.toLocaleString());
}
// Other formats are interpreted as local time,
// shown as 2017-03-27 18:00:00 in my timezone,
// shown 2017-03-28 00:00:00 in my timezone:
console.log("Other formats:");
var otherDates = [new Date("2017-3-28"), new Date("March 28, 2017"), new Date("2017/03/28")];
for (var dt of otherDates)
{
console.log(dt.toUTCString() + " / " + dt.toLocaleString());
}
This format is International Standard (ISO format)
new Date("2017-01-01")
This guarantees same output across all browsers.
However, the other formats may change according to browser as they are not so well defined.
As you can see that this format
new Date("2017-1-1")
is parsed successfully in chrome, but gives error in IE 11

Why is JavaScript guessing two different timezones based on strings that are identical in structure?

I am providing dates two to my script in this format: 2016-05-25 12:30:02. However, JavaScript is setting the each date to a different GMT offset. I formatted the dates to match the suggested answer here and added the function provided, but I got the same results as shown below so I reverted back to my original script shown below.
var response = JSON.parse(jsonResponse);
var lastExportedOrderData = response.lastExportedOrder;
/* currentDateTime = '2016-05-25 12:30:02'; */
var currentDateTime = new Date(response.scriptExecutionTime);
/* lastOrderExportedAt = '2016-01-12 16:53:56'; */
var lastOrderExportedAt = new Date(lastExportedOrderData.exported_at);
currentDateTime Results: 2016-05-25 12:30:02 -> Wed May 25 2016 12:30:02 GMT-0400 (EDT)
lastOrderExportedAt Results: 2016-01-12 16:53:56 > Tue Jan 12 2016 16:53:56 GMT-0500 (EST)
I don't really care about the timezone so long as the dates are in the same timezone.
Update
My dates are now being input using the ISO-8601 Standard format but my the problem remains. 2016-05-25T14:04:00-05:00 results as GMT-0400 where 2016-01-12T16:53:56-05:00 results as GMT -0500
They're in the same timezone (U.S. Eastern). The difference is that the one in May is during Daylight Savings Time and thus is Eastern Daylight Time (EDT), and the one in January isn't, and thus is in Eastern Standard Time.
It's important to note that their underlying time values (e.g., as from dt.getTime()) are unaffected by timezone, and as both dates have been parsed (in your example) as local time, they can reliably be compared. You'd only run into an issue comparing them if one were parsed as local time and the other as UTC.
Side note: You're relying on new Date to parse those strings, which means you're relying on unspecified behavior that may vary from JavaScript engine to JavaScript engine. The only string format engines are required to support is a simplified version of ISO-8601, which those strings are not in. In particular, it's entirely possible that one browser's JavaScript engine will decide to parse them as though they were in UTC and another engine's browser will decide (as yours seems to have done) to parse them as local time.
You haven't said what timezone the strings are meant to be interpreted in, but if (for instance) they were meant to be UTC, you can fairly easily change them to be in ISO-8601 format by replacing the space between the date and time with a T, and adding a Z to the end:
str = str.replace(" ", "T") + "Z";
The resulting string, for instance "2016-05-25T12:30:02Z", is valid for new Date on any modern JavaScript engine (so, not IE8).
Alternately, if you know what timezone offset they should be interpreted in (say, GMT-05:00), you can replace the space with T and add a timezone offset to the end:
str = str.replace(" ", "T") + "-05:00";
That string (for instance, "2016-05-25T12:30:02-05:00") is also valid for new Date.
Alternately, get the parts of them:
var parts = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/.exec(str);
...and build the date/time yourself
// As local time
var dt = new Date(
+parts[1], // Year
+parts[2] - 1, // Month (0 = January)
+parts[3], // Day
+parts[4], // Hour
+parts[5], // Minute
+parts[6] // Second
);
or
// As UTC
var dt = new Date(Date.UTC(
+parts[1], // Year
+parts[2] - 1, // Month (0 = January)
+parts[3], // Day
+parts[4], // Hour
+parts[5], // Minute
+parts[6] // Second
));
The unary + on the above coerces from string to number. (It isn't strictly necessary, though, both new Date and Date.UTC do it for you.)
Just to add to TJ's answer, in implementations consistent with ES5 and later, the string 2016-05-25T14:04:00 will be parsed as a "local" time as it doesn't have a time zone offset. (In an ES2015-compliant browser. Sadly in the previous specification, they got that wrong and made it default to UTC, and currently Chrome implements the ES5 spec [UTC] but Firefox implements the ES2015 spec [local time].) But don't rely on that, parse the string per TJ's answer.
When creating a local time value, the host time zone settings are taking in to consideration. It seems that the host system is set for EST/EDT.
EDT starts at 02:00 on the second Sunday in March, so on 2016-01-12 US Eastern Standard Time (EST) applies which has an offset of -0500. For 2016-05-25, US Eastern Daylight Time (EDT) is used which has an offset of -0400.
Note that the actual time value of the Date created for 2016-05-25T12:30:02-0400 is for 2016-05-25T16:30:02Z (i.e. the equivalent UTC time). When displayed, the date's toString method is called which applies the host system offset for that time and date to adjust the displayed value to "local" time.
Date's use UTC internally so that they can be displayed in any time zone and still represent the same instant in time.

Date in PHP and Javascript are not match

As title, although I set with only value.
In Javascript:
var n = (new Date("2015 Oct 17")).getTime()/1000;
console.log(n);
// result: 1445014800
And PHP:
$unix = date('d-m-Y', 1445014800);
echo $unix;
// result: 16-10-2015
Please leave some explanations.
Thanks a lot!
In your JavaScript:
var n = (new Date("2015 Oct 17")).getTime()/1000;
console.log(n);
// result: 1445014800
The operation /1000 is coercing your value to a numeric type, so the answer is correct!
In javascript, parsing of a string like "2015 Oct 17" is entirely implementation dependant. If it works at all, it is likely to be converted to a Date object representing the date at 00:00:00 at the start of the day in the time zone of the host system.
For a system whose time zone offset is, say, UTC+1000, and that parses the string as a local time, the time value in seconds will be 1445004000.
However, such a system might decide that the string is a bit like an ISO 8601 string, and it might decide that since such strings without a time zone were treated as UTC in ES5, that it will treat it as a UTC time. In that case, the time value in seconds will be 1445040000 (i.e. equivalent to 2015-10-17T00:00:00Z).
To reliably transfer dates between systems, it is often considered best to transfer time values in either seconds or milliseconds since the UNIX (and ECMAScript) epoch of 1070-01-01T00:00:00Z.
To create such a time value for 2015-Oct-17 you can use:
var timeValue = Date.UTC(2015, 9, 17);
To convert the UNIX time value 1445014800 to an ECMAScript date, you can do (noting that UNIX time values are in seconds and ECMAScript in milliseconds):
console.log(new Date(1445014800*1000).toISOString()); // 2015-10-16T17:00:00.000Z
So I'll assume that the PHP host is in a timezone that is UTC-05:00 and that 2015-Oct-17 has been treated as UTC.
I have checked it, and this JS function returns
1445036400
which in 'human time' is
Fri, 16 Oct 2015 23:00:00 GMT
https://jsbin.com/wuvawupede/edit?js,console,output
http://www.epochconverter.com/

JavaScript UTC Date

I try to get UTC Date. Why
(new Date("2015-04-22")).getUTCDate() = 22
And
(new Date("Apr 22, 2015")).getUTCDate() = 21
?
To quote MDN on the workings of Date.parse (called by new Date("string")):
Differences in assumed time zone
Given a date string of "March 7, 2014", parse() assumes a local time zone, but given an ISO format such as "2014-03-07" it will assume
a time zone of UTC. Therefore Date objects produced using those
strings will represent different moments in time unless the system is
set with a local time zone of UTC. This means that two date strings
that appear equivalent may result in two different values depending on
the format of the string that is being converted (this behavior is
changed in ECMAScript ed 6 so that both will be treated as local).
Watch out for the warning I've highlighted on the last line

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