JavaScript: Which browsers support parsing of ISO-8601 Date String with Date.parse
Asked Answered
B

10

51

I failed to parse an ISO-8601 date "2011-04-26T13:16:50Z" on IE8 and Safari 5, but it worked on Chrome 10, FF4. Support seems to be quite mixed?

Does anyone know the actual status of which browsers can parse this format? I assume IE6, and 7 will fail too.

var d = Date.parse("2011-04-26T13:16:50Z");
Boride answered 27/4, 2011 at 10:13 Comment(1)
Probably we cannot figure this one out, because ECMAScript-262v5 support is not published well. Useful links: Mozilla Docs, W3 datetimeBoride
G
19

I had this issue today. I found momentjs was a good way of parsing ISO 8601 dates in a cross browser manor.

momentjs can also be used to output the date in a different format.

Gabby answered 17/10, 2012 at 13:8 Comment(1)
For anyone who cares about performance, be careful of MomentJSCacus
E
33

I say shim it only if needed via a few tests,

here is one I already written:

(function() {

var d = window.Date,
    regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,3})(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;

if (d.parse('2011-11-29T15:52:30.5') !== 1322581950500 ||
    d.parse('2011-11-29T15:52:30.52') !== 1322581950520 ||
    d.parse('2011-11-29T15:52:18.867') !== 1322581938867 ||
    d.parse('2011-11-29T15:52:18.867Z') !== 1322581938867 ||
    d.parse('2011-11-29T15:52:18.867-03:30') !== 1322594538867 ||
    d.parse('2011-11-29') !== 1322524800000 ||
    d.parse('2011-11') !== 1320105600000 ||
    d.parse('2011') !== 1293840000000) {

    d.__parse = d.parse;

    d.parse = function(v) {

        var m = regexIso8601.exec(v);

        if (m) {
            return Date.UTC(
                m[1],
                (m[2] || 1) - 1,
                m[3] || 1,
                m[4] - (m[8] ? m[8] + m[9] : 0) || 0,
                m[5] - (m[8] ? m[8] + m[10] : 0) || 0,
                m[6] || 0,
                ((m[7] || 0) + '00').substr(0, 3)
            );
        }

        return d.__parse.apply(this, arguments);

    };
}

d.__fromString = d.fromString;

d.fromString = function(v) {

    if (!d.__fromString || regexIso8601.test(v)) {
        return new d(d.parse(v));
    }

    return d.__fromString.apply(this, arguments);
};

})();

and in your code just always use Date.fromString(...) instead of new Date(...)

test a browser to see if the shim will be used:

http://jsbin.com/efivib/1/edit

works in all major browsers, used these references:

http://dev.w3.org/html5/spec/common-microsyntaxes.html

http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.15

http://msdn.microsoft.com/en-us/library/windows/apps/ff743760(v=vs.94).aspx

http://msdn.microsoft.com/en-us/library/windows/apps/wz6stk2z(v=vs.94).aspx

http://msdn.microsoft.com/en-us/library/windows/apps/k4w173wk(v=vs.94).aspx

!- microsoft connect requires a log in to view:

IE9 was failing on milliseconds with digit counts other than 3: (fixed in IE10) https://connect.microsoft.com/IE/feedback/details/723740/date-parse-and-new-date-fail-on-valid-formats

IE10 is still (as of 1/17/2013) failing when the time zone is omitted (according to ECMA, this should defalt to Z or UTC, not local): https://connect.microsoft.com/IE/feedback/details/776783/date-parse-and-new-date-fail-on-valid-formats

-- Read this if you care about where the standard is now / going in the future and why I can't get the IE team to recognize that their IE10 implementation is technically incorrect:

ECMAScript-262 v6.0 is going to move to the slightly more iso8601 compliant version of "if time zone indicator is omitted, assume local time"... so now there is a discrepancy, this implementation, chrome, mobile safari and opera all follow ECMAScript-262 v5.1, whereas IE10, firefox, desktop safari all seem to be following the more iso8601 compliant ECMAScript-262 v6.0 specification... this is confusing to say the least. When chrome or mobile safari pull the trigger and move to the ES6 implementation, I think this implementation should go with it leaving ES5.1 in the minority. I've read that this is listed in the "errata" of version 5.1 though I haven't found it. I'm more of the opinion that it's a little early to be pulling the trigger on ES6 just yet, but I'm also of the opinion that code needs to be practical, not ideal and move to where the browser makers move to. That said, it seems to be a 50/50 decision right now, so below is the "future" version of this code...

I should also mention that either version of the code will normalize "non-compliant" browsers to match the behavior of the other one, since that's what shims do ;)

HERE IS AN ADAPTED VERSION COMPATIBLE WITH ECMAScript-262 v6.0 (JavaScript Future)

see relevant sections here: (this is the only online html version of the spec I could find) http://people.mozilla.org/~jorendorff/es6-draft.html#sec-15.9.1.15

(function() {

    var d = window.Date,
        regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})\.(\d{1,})(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/,
        lOff, lHrs, lMin;

    if (d.parse('2011-11-29T15:52:30.5') !== 1322599950500 ||
        d.parse('2011-11-29T15:52:30.52') !== 1322599950520 ||
        d.parse('2011-11-29T15:52:18.867') !== 1322599938867 ||
        d.parse('2011-11-29T15:52:18.867Z') !== 1322581938867 ||
        d.parse('2011-11-29T15:52:18.867-03:30') !== 1322594538867 ||
        d.parse('2011-11-29') !== 1322524800000 ||
        d.parse('2011-11') !== 1320105600000 ||
        d.parse('2011') !== 1293840000000) {

        d.__parse = d.parse;

        lOff = -(new Date().getTimezoneOffset());
        lHrs = Math.floor(lOff / 60);
        lMin = lOff % 60;

        d.parse = function(v) {

            var m = regexIso8601.exec(v);

            if (m) {
                return Date.UTC(
                    m[1],
                    (m[2] || 1) - 1,
                    m[3] || 1,
                    m[4] - (m[8] ? m[9] ? m[9] + m[10] : 0 : lHrs) || 0,
                    m[5] - (m[8] ? m[9] ? m[9] + m[11] : 0 : lMin) || 0,
                    m[6] || 0,
                    ((m[7] || 0) + '00').substr(0, 3)
                );
            }

            return d.__parse.apply(this, arguments);

        };
    }

    d.__fromString = d.fromString;

    d.fromString = function(v) {

        if (!d.__fromString || regexIso8601.test(v)) {
            return new d(d.parse(v));
        }

        return d.__fromString.apply(this, arguments);
    };

})();

hope this helps -ck

Equip answered 20/2, 2012 at 15:16 Comment(10)
Your regex doesn't work correctly for dates that have no seconds and milliseconds. Perhaps use the regex from this blogpost instead.Dare
@Dare thanks for your feedback and you are correct, i did miss the variants of Thh:mm and Thh:mm:ss, however your assertion that i should "use from this blogpost" is very misinformed, he is doing something different than what i am, he is trying to match ISO8601 the full standard, I am trying to create a shim that emulates the proper behavior outlined by ECMA and demonstrated in other browsers, which is a subset of ISO8601.. confusing i know, that regex doesn't serve my purposes, but i will correct my own to include some of the fringe cases i missedEquip
I think this shim might be over-eager to apply itself - if you're not in GMT TZ, then parsing a date with no TZ indication to localtime won't produce a single epoch value - whereas this script assumes it will e.g. the line d.parse('2011-11-29T15:52:18.867') !== 1322599938867Silverware
@MalcolmBox you should probably read the whole post post where parsing dates with no TZ is talked about... basically there was an argument over default behavior and it was changed, and like all standards that change it is currently only semi-consistent across browsers... this code isn't meant to be a "fix-all" and might need to be customized to suite your specific application since there is some "spec interpretation issues"Equip
@Equip Sure, I understand that. But if you run that shim on a v6 compliant browser, it will apply since it will parse the non-TZ ones as local time which won't match the constants. On a v6 compliant implementation, I'd suggest this shim should not activateSilverware
There's a Y2K bug here as well -- this won't work for the years 0000-0099, which Date.UTC will helpfully convert to 1900-1999.Jonathonjonati
@AlexDixon thanks, missed that one.. and your right Date.UTC interpretation of two digit dates is super helpfulEquip
it's just not working, sorry. not for this ""2014-04-30T00:00:00+0200" doesn't seem to be an outrageous version of a 8601 date.Ornie
@PizzaiolaGorgonzola that +0200 at the end MUST be formatted as 02:00 the colon isn't optional nor are any digits... however, this current regex doesn't treat the ":ss" and ".sss.." as optional and it should to be honest, a better version would be something like: /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{1,}))?)?(Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/ let me test and see if that worksEquip
There is no need for a regular expression at all. Simply split on non–digit characters and test the parts. indexOf will provide the timezone offset sign and UTC indicator.Catercorner
G
19

I had this issue today. I found momentjs was a good way of parsing ISO 8601 dates in a cross browser manor.

momentjs can also be used to output the date in a different format.

Gabby answered 17/10, 2012 at 13:8 Comment(1)
For anyone who cares about performance, be careful of MomentJSCacus
H
18

Simple function to parse ISO8601 date format in any browser:

function dateFromISO8601(isoDateString) {
  var parts = isoDateString.match(/\d+/g);
  var isoTime = Date.UTC(parts[0], parts[1] - 1, parts[2], parts[3], parts[4], parts[5]);
  var isoDate = new Date(isoTime);

  return isoDate;
}
Huff answered 26/11, 2013 at 16:50 Comment(0)
O
6

Yes, Date.parse is not consistent for different browsers. You could:

Ofilia answered 27/4, 2011 at 11:6 Comment(2)
Just to note, it's jQuery UI datepicker's parseDate. Native jQuery does not have a date parsing library. Though I think it sorely needs one. Javascript's date implementation sucks!Metrical
Another note: jQuery UI's parseDate method really only parses dates--it doesn't handle times at all.Eam
M
4

Some older browsers return the wrong date (and not NaN)if you parse an ISO date string.

You can use your own method across all browsers, or use Date.parse if it is implemented correctly- check a known timestamp.

Date.fromISO= (function(){
    var diso= Date.parse('2011-04-26T13:16:50Z');
    if(diso=== 1303823810000) return function(s){
        return new Date(Date.parse(s));
    }
    else return function(s){
        var day, tz, 
        rx= /^(\d{4}\-\d\d\-\d\d([tT][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/, 
        p= rx.exec(s) || [];
        if(p[1]){
            day= p[1].split(/\D/).map(function(itm){
                return parseInt(itm, 10) || 0;
            });
            day[1]-= 1;
            day= new Date(Date.UTC.apply(Date, day));
            if(!day.getDate()) return NaN;
            if(p[5]){
                tz= parseInt(p[5], 10)*60;
                if(p[6]) tz += parseInt(p[6], 10);
                if(p[4]== "+") tz*= -1;
                if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
            }
            return day;
        }
        return NaN;
    }
})()
Maddi answered 27/4, 2011 at 14:17 Comment(2)
Here is one that handles more versions rx = /^(\d{4}\-\d\d\-\d\d([tT][\d:\.]*)?)([zZ]|([+\-])(\d{3}))?$/, as seen here: jsfiddle.net/mplungjan/QkasDFormate
The following codevar diso = Date.parse('2012-06-25T22:19:43.4859618Z'); if (diso === 1340662783485) return function (s) {code better detects the check for IE 9Squashy
M
4

The ES5 spec deviates from the ISO8601 spec, especially when it comes to the treatment of dates without a timezone indicator / offset. There is a bug ticket at https://bugs.ecmascript.org/show_bug.cgi?id=112 describing the problem and it looks like it will be fixed in ES6.

For now, I recommend looking at https://github.com/csnover/js-iso8601 for a cross-browser implementation. I use https://github.com/csnover/js-iso8601/tree/lax which doesn't conform to the ES5 spec but has better interoperability with other JSON serialisation libraries such as JSON.NET.

Mayamayakovski answered 25/5, 2012 at 9:32 Comment(0)
A
2

As mentioned previously ISO 8601 style dates were added in ECMAScript version 5, where implementation is not consistent, and not available in all browsers. There are a number of script stubs available, but you may wish to simply add your own Date.parse* method.

(function() {
  //ISO-8601 Date Matching
  var reIsoDate = /^(\d{4})-(\d{2})-(\d{2})((T)(\d{2}):(\d{2})(:(\d{2})(\.\d*)?)?)?(Z|[+-]00(\:00)?)?$/;
  Date.parseISO = function(val) {
    var m;

    m = typeof val === 'string' && val.match(reIsoDate);
    if (m) return new Date(Date.UTC(+m[1], +m[2] - 1, +m[3], +m[6] || 0, +m[7] || 0, +m[9] || 0, parseInt((+m[10]) * 1000) || 0));

    return null;
  }

  //MS-Ajax Date Matching
  var reMsAjaxDate = /^\\?\/Date\((\-?\d+)\)\\?\/$/;
  Date.parseAjax = function(val) {
    var m;

    m = typeof val === 'string' && val.match(reMsAjaxDate);
    if (m) return new Date(+m[1]);

    return null;
  }
}();

I use the above method for JSON.parse hydration of dates...

JSON.parse(text, function(key, val) {
  return Date.parseISO(val) || Date.parseAjax(val) || val;
});
Ainsley answered 14/2, 2012 at 19:7 Comment(5)
Your regex fails for: "2016-10-24T20:50:06+00:00". Source:en.wikipedia.org/wiki/ISO_8601Harmonic
@TheDembinski yeah, it only supports UTC/Z datetime parsing... The ES5 shim will support it... at this point, it's not worth shimming unless you need to support IE < 8, which is very insecure and doesn't even work in a lot of sites via https now... same goes for IE8 actually, but IE8 will parse ISO dates, as will any other browser made in the past 6+ years.Ainsley
I guess I don't understand - for some reason I feel like iso8601 is that format. Like, they're synonymous in my mind.Harmonic
Updated to allow for [+-]00:00, in any case, you could probably just remove the tail check, and it should work... these days I'd just do new Date(input) if the yyyy-mm-dd part was there... since all current browsers support the format directly, and have for a long time now.Ainsley
You're the most handsomest boy/girl at the ball :] Thank you for doing that.Harmonic
T
2

I found ckozl answer really useful and interesting, but the regexp is not perfect and it didn't work in my case.

Apart from the fact that dates without minutes, secs or milisecs are not parsed, ISO 8501 spec says that the '-' and ':' separators are optional, so "2013-12-27" and "20131227" are both valid. In my case, this is important because I'm setting the server date and time in a JavaScript variable from PHP:

var serverDate = new Date(Date.parse("<?php date(DateTime::ISO8601); ?>"));

This code generates something like this:

<script>
var serverDate = new Date(Date.parse("2013-12-27T15:27:34+0100"));
</script>

The important part is the time zone designator "+0100" where the ':' is missing. Although Firefox parses that string correctly, IE (11) fails (if the ':' is added, then IE also works). The headache about the zonetime and the ECMAScript specifications described by ckozl is unimportant in my case, because PHP always add the time zone designator.

The RegExp I'm using, instead of that one from ckozl is:

var regexIso8601 = /^(\d{4}|\+\d{6})(?:-?(\d{2})(?:-?(\d{2})(?:T(\d{2})(?::?(\d{2})(?::?(\d{2})(?:(?:\.|,)(\d{1,}))?)?)?(Z|([\-+])(\d{2})(?::?(\d{2}))?)?)?)?)?$/;

Keep in mind that this regexp is not perfect either. ISO 8501 allows week specification (2007-W01-1 for monday, 1 Jan 2007), or decimal fractions in hours and minutes (18.50 for 18:30:00, or 18:30.25 for 18:30:15). But they are quite unusual.

P.D. This answer should be, I imagine, a comment to the original chozl answer, but I do not have reputation enough :(

Telescopic answered 27/12, 2013 at 16:19 Comment(1)
Same issue with PHP not outputting the column. The following transformation seems to work. Not sure if it works in anycase PHP can output string.substr(0, string.length- 2)+':'+string.substr(string.length- 2, 10);Whipping
R
1

ISO 8601 date formats were added with ECMAScript-262 v5. So if a browser is not v5 compatible, you simply cannot expect to be capable of handling ISO 8601 formats.

Browsers not being v5 compatible may use any implementation specific date formats they want. Most of them do at least support RFC822/RFC1123 date formats, though. Example:

var d = Date.parse("Wed, 26 Apr 2011 13:16:50 GMT+0200");
Rotman answered 27/4, 2011 at 11:37 Comment(3)
Accepting T24:00 as a valid value is also a concern. FF, Opera, and IE9 do follow the ECMAScript 2011 standard (and ISO8601 5.3.2) in this regard and accept T24:00; Chrome and Safari are non-compliant and report invalid date.Botello
@Tim—ES5 requires support for date–only forms, but not time–only forms. Section 15.9.1.15 says: It also includes “date-time” forms that consist of one of the above date-only forms immediately followed by one of the following time forms with an optional time zone offset appendedCatercorner
@Rob: Sorry for any lack of clarity on my part, but I'm not suggesting a time-only form. I'm referring to a date-time value whose time-element is T24:00. Section15.9.1.15 says: As every day both starts and ends with midnight, the two notations 00:00 and 24:00 are available to distinguish the two midnights that can be associated with one date. This means that the following two notations refer to exactly the same point in time: 1995-02-04T24:00 and 1995-02-05T00:00Botello
A
0

Microsoft Sharepoint 2013 is ALSO using a different notation eg "2013-04-30T22:00:00Z"

If you want to use the REST services from sharepoint 2013 in combination with Internet Explorer 8 ( IE8 ) , then the solution from ckozl is NOT working. Youll get the NaN

change the regex line TO:

regexIso8601 = /^(\d{4}|\+\d{6})(?:-(\d{2})(?:-(\d{2})(?:T(\d{2}):(\d{2}):(\d{2})(\.(\d{1,3}))?(?:Z|([\-+])(\d{2}):(\d{2}))?)?)?)?$/;

this will make the microseconds bit optional !

cheerio, Leo

Abundant answered 3/6, 2013 at 19:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.