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)
Nice, except there seems to be a problem with Time... when I try iso date "2010-08-18T16:30:00.000+10:00"
ReplyDeleteit returns 12:00AM when it should be 04:30PM...?
I appreciate the code on the one hand, but on the other hand... are you trying to win the unreadable code contest?
ReplyDeleteIf you are, I hear you get better rankings if you can make a picture out of it.
If I wanted unreadable code, I'd just run packer.js on it.
uhm ... "d" for date, "m" for match, "his" for php like gmdate format ... which part exactly is not clear? the "t" for ten? Glad you appreciated
ReplyDeleteWhat's the license? Can I post it as MIT?
ReplyDeleteMit, yes :)
ReplyDeleteThere's an error with these lines:
ReplyDeleted.setUTCHours((m[7] >> 0) + s ? -h : h);
d.setUTCMinutes((m[8] >> 0) + s ? -i : i);
This evaluates to something like this
if ((m[7] >> 0) + s) {
-h;
} else {
h;
}
When you mean something like this
(m[7] >> 0) +
if (s) {
-h;
} else {
h;
}
Should be
d.setUTCHours((m[7] >> 0) + (s ? -h : h));
d.setUTCMinutes((m[8] >> 0) + (s ? -i : i));
Or better
if (s) {
h = -h;
i = -i;
}
d.setUTCHours((m[7] >> 0) + h);
d.setUTCMinutes((m[8] >> 0) + i);
There was also a bug when changing the timezone meant crossing from one date to the next.
ReplyDeleteHere's a snippet from my version
// The bit-shifting is shorthand for `Number(m) || 0`
date.setUTCFullYear(match[1] >> 0);
date.setUTCMonth(match[3] ? (match[3] >> 0) - 1 : 0);
date.setUTCDate(match[5] >> 0);
date.setUTCHours(match[7] >> 0);
date.setUTCMinutes(match[8] >> 0);
date.setUTCSeconds(match[10] >> 0);
date.setUTCMilliseconds(match[12] >> 0);
// Adjust to UTC offset
if (match[13] && match[13] !== "Z") {
hour = match[16] >> 0;
minute = match[17] >> 0;
aheadOfUtc = (match[15] === "+");
hour = hour * 60 * 60 * 1000;
minute = minute * 60 * 1000;
if (aheadOfUtc) {
hour = -hour;
minute = -minute;
}
// easy dateline wrapping
date = new Date(date.valueOf() + hour + minute);
}
return date;