ChatGPT解决这个技术问题 Extra ChatGPT

Date parsing in javascript is different between safari and chrome

I have the following code

var c = new Date(Date.parse("2011-06-21T14:27:28.593Z"));
console.log(c);

On Chrome it correctly prints out the date on the console. In Safari it fails. Who is correct and more importantly what is the best way to handle this?

They both give me Tue Jun 21 2011 10:27:28 GMT-0400 (Eastern Daylight Time)
You sure. jsfiddle.net/A26Gu run on safari Version 5.0.4 (6533.20.27) gives me an output in the console of "invalid date"
Why do you create a Date object twice? What is your definition of correct? You may use the 'Date.toISOString()' method. But be aware: It is not supported by older browsers.
Javascript Date supports 2 timezones, UTC and the local one from the OS. You can't be sure the local timezone is set correct. And as Javascript is client side, you can't really trust it does anything correct - not even parsing dates. Any application critical calculations should be done server side.
@Erik—good comment, Dates in browsers are very unreliable. The use of Date.parse in new Date(Date.parse(string)) is redundant since if the Date constructor is called with a string, it's passed to Date.parse anyway. Also, Safari has a few bugs with creating dates that are difficult (if not impossible) to work around.

E
Erik

You can't really use Date.parse. I suggest you use: new Date (year, month [, date [, hours [, minutes [, seconds [, ms ] ] ] ] ] )

To split the string you could try

var s = '2011-06-21T14:27:28.593Z';
var a = s.split(/[^0-9]/);
//for (i=0;i<a.length;i++) { alert(a[i]); }
var d=new Date (a[0],a[1]-1,a[2],a[3],a[4],a[5] );
alert(s+ " "+d);

These date strings are coming from the server. If you have a way to parse them into the seperate components I'd be more than happy to hear.
Date.parse just caught me out with old safari, thanks for your answer.
This only works if dates are given in local time. It strips timezone and applies local. Example: you are in -0500 timezone you send it 11am utc, you should get back 6am local, but instead you get back 11am local.
As disso says, this answer ignores the timezone. It takes a string with offset 00:00 ("Z") and parses it as if it was local. Date.UTC should have been used.
O
Olmstov

My similar issue was caused by Safari not knowing how to read the timezone in a RFC 822 time zone format. I was able to fix this by using the ISO 8601 format. If you have control of the date format I got this working with java's SimpleDateFormat "yyyy-MM-dd'T'HH:mm:ss.sssXXX" which produces for me ie. "2018-02-06T20:00:00.000+04:00". For whatever reason Safari can't read "2018-02-06T20:00:00.000+0400", notice the lack of colon in the timezone format.

// Works
var c = new Date("2018-02-06T20:00:00.000+04:00"));
console.log(c);

// Doesn't work
var c = new Date("2018-02-06T20:00:00.000+0400"));
console.log(c);

Found the same behavior; haven't yet been able to find documentation of this being a known bug. However, both formats (with colon, and without colon) do seem to be intended as acceptable formats: The UTC offset is appended to the time in the same way that 'Z' was above, in the form ±[hh]:[mm], ±[hh][mm], or ±[hh]. And indeed, Chrome & Firefox are able to parse the offsets without colons
This blog post explains the problem & one regex solution, for Safari & Internet Explorer, but still doesn't indicate whether Safari or IE have somewhere acknowledged that they don't match the ISO spec
Ah, here it is: bugs.webkit.org/show_bug.cgi?id=160287 Yet unaddressed, and given that a fix definitely wouldn't come for IE, probably simplest to include the colon, if one is able to.
Perfect if you need dates with a timezone. Note that new Date("2020-04-16T20:00+04:00")) works as well across browsers (Chrome, Firefox, Safari, Edge).
j
jabley

I tend to avoid Date.parse, as per the other answers for this question. It doesn't seem to be a portable way to reliably deal with dates.

Instead, I have used something like the function below. This uses jQuery to map the string array into a number array, but that's a pretty easy dependency to remove / change. I also include what I consider sensible defaults, to allow you to parse 2007-01-09 and 2007-01-09T09:42:00 using the same function.

function dateFromString(str) {
  var a = $.map(str.split(/[^0-9]/), function(s) { return parseInt(s, 10) });
  return new Date(a[0], a[1]-1 || 0, a[2] || 1, a[3] || 0, a[4] || 0, a[5] || 0, a[6] || 0);
}

D
Daniel Sokolowski

I've checked it in several browsers, and yes, safari returns invalid date. By the way, you don't have to use Date.parse here, just new Date([datestring]) will work too. Safari evidently requires more formatting of the datestring you supply. If you replace '-' with '/', remove the T and everything after the dot (.593Z), it will give you a valid date. This code is tested and works in Safari

var datestr = '2011-06-21T14:27:28.593Z'.split(/[-T.]/);
var safdat = new Date( datestr.slice(0,3).join('/')+' '+datestr[3] );

Or using String.replace(...):

new Date("2016-02-17T00:05:01+0000".replace(/-/g,'/').replace('T',' ').replace(/(\..*|\+.*/,""))

This causes you to lose the timezone and the fractions of a second the resulting Date object. I guess you could add some additional code at the end to re-add those components.
Parenthesis in 3rd regex not needed: new Date("2016-02-17T00:05:01+0000".replace(/-/g,'/').replace('T',' ').replace(/\..*|\+.*/,""))
just using .replace(/-/g,'/') works. my datestring is like this "2019-06-26 23:59:59"
L
Londeren

I use the following function for parsing dates with timezone. Works fine both Chrome and Safari:

function parseDate(date) { const parsed = Date.parse(date); if (!isNaN(parsed)) { return parsed; } return Date.parse(date.replace(/-/g, '/').replace(/[a-z]+/gi, ' ')); } console.log(parseDate('2017-02-09T13:22:18+0300')); // 1486635738000 time in ms


This is the safest way to do it. I guess you should change the last replace to something like /[a-z]+/gi, to match a wider audience, like dates with UTC instead of only T.
@Narayon thanks for your remark, I've changed the answer.
A
AaronSieb

I ended up using a library to offset this:

http://zetafleet.com/blog/javascript-dateparse-for-iso-8601

Once that library was included, you use this code to create the new date:

var date = new Date(Date.parse(datestring));

Our project wasn't using millisecond specifiers, but I don't believe that will cause an issue for you.


a
adjwilli

Instead of using 'Z' at the end of the date string, you can add the local client timezone offset. You'll probably want a method to generate that for you:

let timezoneOffset = () => {
    let date = new Date(),
        timezoneOffset = date.getTimezoneOffset(),
        hours = ('00' + Math.floor(Math.abs(timezoneOffset/60))).slice(-2),
        minutes = ('00' + Math.abs(timezoneOffset%60)).slice(-2),
        string = (timezoneOffset >= 0 ? '-' : '+') + hours + ':' + minutes;
    return string;
}

So the end result would be:

var c = new Date("2011-06-21T14:27:28.593" + timezoneOffset());


The Z is the timezone offset, it stands for "zero hour offset" also known as "Zulu time" (UTC), or +00:00
A
Adam Leggett

Here is a more robust ISO 8601 parser than what others have posted. It does not handle week format, but it should handle all other valid ISO 8601 dates consistently across all browsers.

function newDate(value) { var field = value.match(/^([+-]?\d{4}(?!\d\d\b))(?:-?(?:(0[1-9]|1[0-2])(?:-?([12]\d|0[1-9]|3[01]))?)(?:[T\s](?:(?:([01]\d|2[0-3])(?::?([0-5]\d))?|24\:?00)([.,]\d+(?!:))?)?(?::?([0-5]\d)(?:[.,](\d+))?)?([zZ]|([+-](?:[01]\d|2[0-3])):?([0-5]\d)?)?)?)?$/) || []; var result = new Date(field[1], field[2] - 1 | 0, field[3] || 1, field[4] | 0, field[5] | 0, field[7] | 0, field[8] | 0) if (field[9]) { result.setUTCMinutes(result.getUTCMinutes() - result.getTimezoneOffset() - ((field[10] * 60 + +field[11]) || 0)); } return result; } console.log(newDate('2011-06-21T14:27:28.593Z')); console.log(newDate('1970-12-31T06:00Z')); console.log(newDate('1970-12-31T06:00-1200'));


A
Anky

i tried converted date by truncating it and parsing it like that , its working fine with safari and ios .

var dateString = "2016-01-22T08:18:10.000+0000";
 var hours = parseInt(dateString.split("+")[1].substr("0","2"));
 var mins = parseInt(dateString.split("+")[1].substr("2"));
 var date = new Date(dateString.split("+")[0]);
 date.setHours(date.getHours()-hours);
 date.setMinutes(date.getMinutes()-mins);

I
Ivan Ferrer

Use this to both (Safari / Chrome):

Date.parse("2018-02-06T20:00:00.000-03:00")

This strong format recognized in both browsers, Safari and Chrome.
s
schnere

Instead of using a 3rd party library, this is my - relatively simple - solution for this:

function parseDateTime(datetime, timezone) { base = new Date(datetime.replace(/\s+/g, 'T') + 'Z'); hoursUTC = base.toLocaleTimeString('de-AT',{ timeZone: 'UTC' }).split(':')[0]; hoursLocal = base.toLocaleTimeString('de-AT',{ timeZone: 'Europe/Vienna' }).split(':')[0]; timeZoneOffsetSign = (hoursLocal-hoursUTC) < 0 ? '-':'+'; timeZoneOffset = Math.abs(hoursLocal-hoursUTC); timeZoneOffset = timeZoneOffsetSign + timeZoneOffset.toString().padStart(2, '0') + ':00'; return new Date(datetime.replace(/\s+/g, 'T') + timeZoneOffset); } localDate = parseDateTime('2020-02-25 16:00:00','Europe/Vienna'); console.log(localDate); console.log(localDate.toLocaleString('de-AT','Europe/Vienna'));