zoned-date-time
Version:
A tiny JavaScript Date with full IANA timezone support
110 lines (94 loc) • 3.65 kB
JavaScript
function definePrivateProperty(object, property, value) {
Object.defineProperty(object, property, {
value: value
});
}
function getUntilsIndex(original, untils) {
var index = 0;
var originalTime = original.getTime();
// TODO Should we do binary search for improved performance?
while (index < untils.length - 1 && originalTime >= untils[index]) {
index++;
}
return index;
}
function setWrap(fn) {
var offset1 = this.getTimezoneOffset();
var ret = fn();
this.original.setTime(new Date(this.getTime()));
var offset2 = this.getTimezoneOffset();
if (offset2 - offset1) {
this.original.setMinutes(this.original.getMinutes() + offset2 - offset1);
}
return ret;
}
var ZonedDateTime = function(date, timeZoneData) {
definePrivateProperty(this, "original", new Date(date.getTime()));
definePrivateProperty(this, "local", new Date(date.getTime()));
definePrivateProperty(this, "timeZoneData", timeZoneData);
definePrivateProperty(this, "setWrap", setWrap);
if (!(timeZoneData.untils && timeZoneData.offsets && timeZoneData.isdsts)) {
throw new Error("Invalid IANA data");
}
this.setTime(this.local.getTime() - this.getTimezoneOffset() * 60 * 1000);
};
ZonedDateTime.prototype.clone = function() {
return new ZonedDateTime(this.original, this.timeZoneData);
};
// Date field getters.
["getFullYear", "getMonth", "getDate", "getDay", "getHours", "getMinutes",
"getSeconds", "getMilliseconds"].forEach(function(method) {
// Corresponding UTC method, e.g., "getUTCFullYear" if method === "getFullYear".
var utcMethod = "getUTC" + method.substr(3);
ZonedDateTime.prototype[method] = function() {
return this.local[utcMethod]();
};
});
// Note: Define .valueOf = .getTime for arithmetic operations like date1 - date2.
ZonedDateTime.prototype.valueOf =
ZonedDateTime.prototype.getTime = function() {
return this.local.getTime() + this.getTimezoneOffset() * 60 * 1000;
};
ZonedDateTime.prototype.getTimezoneOffset = function() {
var index = getUntilsIndex(this.original, this.timeZoneData.untils);
return this.timeZoneData.offsets[index];
};
// Date field setters.
["setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", "setMilliseconds"].forEach(function(method) {
// Corresponding UTC method, e.g., "setUTCFullYear" if method === "setFullYear".
var utcMethod = "setUTC" + method.substr(3);
ZonedDateTime.prototype[method] = function(value) {
var local = this.local;
// Note setWrap is needed for seconds and milliseconds just because
// abs(value) could be >= a minute.
return this.setWrap(function() {
return local[utcMethod](value);
});
};
});
ZonedDateTime.prototype.setTime = function(time) {
return this.local.setTime(time);
};
ZonedDateTime.prototype.isDST = function() {
var index = getUntilsIndex(this.original, this.timeZoneData.untils);
return Boolean(this.timeZoneData.isdsts[index]);
};
ZonedDateTime.prototype.inspect = function() {
var index = getUntilsIndex(this.original, this.timeZoneData.untils);
var abbrs = this.timeZoneData.abbrs;
return this.local.toISOString().replace(/Z$/, "") + " " +
(abbrs && abbrs[index] + " " || (this.getTimezoneOffset() * -1) + " ") +
(this.isDST() ? "(daylight savings)" : "");
};
ZonedDateTime.prototype.toDate = function() {
return new Date(this.getTime());
};
// Type cast getters.
["toISOString", "toJSON", "toUTCString"].forEach(function(method) {
ZonedDateTime.prototype[method] = function() {
return this.toDate()[method]();
};
});
if (typeof module !== "undefined" && module.exports) {
module.exports = ZonedDateTime;
}