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?

92937 次浏览

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

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(/(\..*|\+.*/,""))

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.

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);
}

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

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

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());

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'));

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'));

Use this to both (Safari / Chrome):

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