Parsing ISO 8601 dates in JavaScript
So, I had to deal with ISO 8601 formatted dates in JavaScript, and I can’t just use the cool ISO 8601 support in js 1.8.5’s Date.parse because of IE 6. I found a couple of examples out there on blogs but couldn’t get them to work right with my data, and I wasn’t sure I wanted a heavyweight do-it-all solution. I reluctantly reinvented the wheel so I thought I’d share it.
function parseISO8601Date(s){ // parenthese matches: // year month day hours minutes seconds // dotmilliseconds // tzstring plusminus hours minutes var re = /(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)(\.\d+)?(Z|([+-])(\d\d):(\d\d))/; var d = []; d = s.match(re); // "2010-12-07T11:00:00.000-09:00" parses to: // ["2010-12-07T11:00:00.000-09:00", "2010", "12", "07", "11", // "00", "00", ".000", "-09:00", "-", "09", "00"] // "2010-12-07T11:00:00.000Z" parses to: // ["2010-12-07T11:00:00.000Z", "2010", "12", "07", "11", // "00", "00", ".000", "Z", undefined, undefined, undefined] if (! d) { throw "Couldn't parse ISO 8601 date string '" + s + "'"; } // parse strings, leading zeros into proper ints var a = [1,2,3,4,5,6,10,11]; for (var i in a) { d[a[i]] = parseInt(d[a[i]], 10); } d[7] = parseFloat(d[7]); // Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]]) // note that month is 0-11, not 1-12 // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/UTC var ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]); // if there are milliseconds, add them if (d[7] > 0) { ms += Math.round(d[7] * 1000); } // if there's a timezone, calculate it if (d[8] != "Z" && d[10]) { var offset = d[10] * 60 * 60 * 1000; if (d[11]) { offset += d[11] * 60 * 1000; } if (d[9] == "+") { ms -= offset; } else { ms += offset; } } return new Date(ms); }; |
A friendly user named Raynos in the new StackOverflow JavaScript chat room helped me with the 0-based month gotcha. Thanks again, Raynos!
Also, the author of Anentropic tipped me off to this effort to promote good-quality JavaScript documentation, like the Mozilla Javascript Reference and Javascript Guide, which are still definitive after all these years. Googling for javascript stuff usually gets you spam sites with lots of ads and less-than-stellar docs. W3schools, I love you, but seriously.
So listen, Google:
Hi Nathan,
First of all, thanks very much; you’ve solved a problem and saved me solving again myself!
I recently read and made use of your blog post to get round IE 8’s lack of a JavaScript ISO 8601 date string constructor.
I wrote up what I was doing in a post about dealing with DateTime JSON serialization issues in ASP.NET. The blog post I’ve written is very clear that you are the author of the parseISO8601Date code but for the readers ease I’ve put your code in my post in the form of this gist on GitHub: (also contains web.config setting and the JavaScript DateTime serializer I ended up using)
https://gist.github.com/2489976
I haven’t published this blog post yet as I wanted to check with you if you were happy for me to use your code in a gist like this? I’ve contained links back to your original post within my blog and said that it’s your work and not mine but if you preferred I’d be quite happy to remove your JS code from the gist entirely and just link back to your blog so that people would be forced to “go to the source” to get your parseISO8601Date parser rather than to my blog.
I’m planning to publish the post sometime next week so let me know if you’d like me to make that change.
By the way, sorry for not mailing you directly – couldn’t see a mail address anywhere.
Thanks, John
Your + and – to calculate the time based on the timezone goes in the wrong direction!!!!
When the time being described is: 2012-09-27T09:00:00.000+02:00
It means that the 9am is at a place where the time is 2 hours later then in GMT/UTC. So whenever you see +, the UTC time is minus the time being described. So the last part of your code should actually be:
if (d[9] == “+”) { ms -= offset; } else { ms += offset; }
http://en.wikipedia.org/wiki/Time_zone
Thanks man, this is useful for us with VMs that haven’t caught up with the world. 🙂
Also, what Chris said.
I originally had modified your code to boil it down as small as I could:
But then I realized that the date object was smart enough to parse it on its own when I did new Date(s), where s was already formatted as ISO8601. So… I learned something new.
I second Craig Talbert comment in that you have you offset backwards. You are shifting the wrong way when its a plus you should minus to get the correct Date
Thanks Andrewiski (and Craig from earlier this year). It’s been a while but I think my code was working with the Flot graphing lib that required backwards offsets, at least in those days.
I have fixed the example in the main post.
‘Z’ is optional. If ‘z’ is not present it should parse it as local time.
function parseISO8601Date(s){
var d = []; d = s.match(re);
// "2010-12-07T11:00:00.000-09:00" parses to: // ["2010-12-07T11:00:00.000-09:00", "2010", "12", "07", "11", // "00", "00", ".000", "-09:00", "-", "09", "00"] // "2010-12-07T11:00:00.000Z" parses to: // ["2010-12-07T11:00:00.000Z", "2010", "12", "07", "11", // "00", "00", ".000", "Z", undefined, undefined, undefined]
if (! d) { throw "Couldn't parse ISO 8601 date string '" + s + "'"; }
// parse strings, leading zeros into proper ints var a = [1,2,3,4,5,6,10,11]; for (var i in a) { d[a[i]] = parseInt(d[a[i]], 10); } d[7] = parseFloat(d[7]);
// Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]]) // note that month is 0-11, not 1-12 // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/UTC var ms; if(d[8]==null){ ms = new Date(d[1], d[2] - 1, d[3], d[4], d[5], d[6]); } else{ ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]); }
// if there are milliseconds, add them if (d[7] > 0) { ms += Math.round(d[7] * 1000); }
// if there's a timezone, calculate it if (d[8] != "Z" && d[10]) { var offset = d[10] * 60 * 60 * 1000; if (d[11]) { offset += d[11] * 60 * 1000; } if (d[9] == "+") { ms -= offset; } else { ms += offset; } }
return new Date(ms);
};