Saturday, July 4, 2009

ECMAScript ISO Date For Every Browser

One most than welcome new entry of ECMAScript 5 is the Date.prototype.toISOString function and ability for Date constructor to accept an ISO String and to generate a Date instance.
This feature will make JSON Date instances import/export extremely fast and simple, but why should we wait ES5 release when we can have both features now?

Full Specs ISO 8601 Parser Plus Unobtrusive toISOString Method


We all do not like that much to extend native JavaScript constructors, so here I am with a proposal that will not touch the Date prototype:

if(!Date.ISO)(function(){"use strict";
/** ES5 ISO Date Parser Plus toISOString Method
* @author Andrea Giammarchi
* @blog WebReflection
* @version 2009-07-04T11:36:25.123Z
* @compatibility Chrome, Firefox, IE 5+, Opera, Safari, WebKit, Others
*/
function ISO(s){
var m = /^(\d{4})(-(\d{2})(-(\d{2})(T(\d{2}):(\d{2})(:(\d{2})(\.(\d+))?)?(Z|((\+|-)(\d{2}):(\d{2}))))?)?)?$/.exec(s);
if(m === null)
throw new Error("Invalid ISO String");
var d = new Date;
d.setUTCFullYear(+m[1]);
d.setUTCMonth(m[3] ? (m[3] >> 0) - 1 : 0);
d.setUTCDate(m[5] >> 0);
d.setUTCHours(m[7] >> 0);
d.setUTCMinutes(m[8] >> 0);
d.setUTCSeconds(m[10] >> 0);
d.setUTCMilliseconds(m[12] >> 0);
if(m[13] && m[13] !== "Z"){
var h = m[16] >> 0,
i = m[17] >> 0,
s = m[15] === "+"
;
d.setUTCHours((m[7] >> 0) + s ? -h : h);
d.setUTCMinutes((m[8] >> 0) + s ? -i : i);
};
return toISOString(d);
};
var toISOString = Date.prototype.toISOString ?
function(d){return d}:
(function(){
function t(i){return i<10?"0"+i:i};
function h(i){return i.length<2?"00"+i:i.length<3?"0"+i:3<i.length?Math.round(i/Math.pow(10,i.length-3)):i};
function toISOString(){
return "".concat(
this.getUTCFullYear(), "-",
t(this.getUTCMonth() + 1), "-",
t(this.getUTCDate()), "T",
t(this.getUTCHours()), ":",
t(this.getUTCMinutes()), ":",
t(this.getUTCSeconds()), ".",
h("" + this.getUTCMilliseconds()), "Z"
);
};
return function(d){
d.toISOString = toISOString;
return d;
}
})()
;
Date.ISO = ISO;
})();

The public static ISO Date method accepts a valid ISO 8601 String compatible with W3 Draft:

Year:
YYYY (eg 1997)
Year and month:
YYYY-MM (eg 1997-07)
Complete date:
YYYY-MM-DD (eg 1997-07-16)
Complete date plus hours and minutes:
YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
Complete date plus hours, minutes and seconds:
YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
Complete date plus hours, minutes, seconds and a decimal fraction of a second
YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)

where:

YYYY = four-digit year
MM = two-digit month (01=January, etc.)
DD = two-digit day of month (01 through 31)
hh = two digits of hour (00 through 23) (am/pm NOT allowed)
mm = two digits of minute (00 through 59)
ss = two digits of second (00 through 59)
s = one or more digits representing a decimal fraction of a second
TZD = time zone designator (Z or +hh:mm or -hh:mm)

The only limit is about milliseconds and JavaScript Date itself, the latest parameter will be rounded if there are more than 3 digits (JS precision is up to a millisecond, no microsecond yet).
The toISOString method is created for every browser that does not support this prototype yet but it is simply assigned and compiled once to avoid Date prototype problems and to optimize memory and performances.
I interpreted ISO specs as Zulu default:

var normalDate = new Date(2009, 06, 04);
var ISODate = Date.ISO("2009-07-04");

normalDate.toISOString = ISODate.toISOString;

alert([ // being in UK ...

// 2009-07-03T23:00:00.000Z
normalDate.toISOString(),

// 2009-07-04T00:00:00.000Z
ISODate.toISOString()
].join("\n"));

Since solar time is +01 hour, if I create a Date with midnight time, the respective Zulu one (UTC) will be the day before at 23:00:00 while since ISO consider an unspecified Time Zone the Zulu one, +00:00, the created Date will be exactly the specified one but obviously if we try to get ISODate.getHours() it will be 01 and not 00, accordingly with my local settings.

This is a little function/proposal that has zero dependencies, is widely compatible, and could be a standard or a must for every library/framework. Waiting for your thoughts, have a nice we 8)

No comments:

Post a Comment