timezonecomplete
Version:
DateTime, TimeZone, Duration and Period library aimed at providing a consistent and complete date-time interface, away from the original JavaScript Date class.
520 lines • 21.1 kB
JavaScript
/**
* Copyright(c) 2014 ABB Switzerland Ltd.
*
* Time zone representation and offset calculation
*/
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isTimeZone = exports.TimeZone = exports.TimeZoneKind = exports.zone = exports.utc = exports.local = void 0;
var assert_1 = require("./assert");
var basics_1 = require("./basics");
var error_1 = require("./error");
var strings = require("./strings");
var tz_database_1 = require("./tz-database");
/**
* The local time zone for a given date as per OS settings. Note that time zones are cached
* so you don't necessarily get a new object each time.
* @throws nothing
*/
function local() {
return TimeZone.local();
}
exports.local = local;
/**
* Coordinated Universal Time zone. Note that time zones are cached
* so you don't necessarily get a new object each time.
* @throws timezonecomplete.NotFound.Zone if the UTC zone is not present in the time zone database
*/
function utc() {
return TimeZone.utc();
}
exports.utc = utc;
/**
* zone() implementation
*/
function zone(a, dst) {
return TimeZone.zone(a, dst);
}
exports.zone = zone;
/**
* The type of time zone
*/
var TimeZoneKind;
(function (TimeZoneKind) {
/**
* Local time offset as determined by JavaScript Date class.
*/
TimeZoneKind[TimeZoneKind["Local"] = 0] = "Local";
/**
* Fixed offset from UTC, without DST.
*/
TimeZoneKind[TimeZoneKind["Offset"] = 1] = "Offset";
/**
* IANA timezone managed through Olsen TZ database. Includes
* DST if applicable.
*/
TimeZoneKind[TimeZoneKind["Proper"] = 2] = "Proper";
})(TimeZoneKind || (exports.TimeZoneKind = TimeZoneKind = {}));
/**
* Time zone. The object is immutable because it is cached:
* requesting a time zone twice yields the very same object.
* Note that we use time zone offsets inverted w.r.t. JavaScript Date.getTimezoneOffset(),
* i.e. offset 90 means +01:30.
*
* Time zones come in three flavors: the local time zone, as calculated by JavaScript Date,
* a fixed offset ("+01:30") without DST, or a IANA timezone ("Europe/Amsterdam") with DST
* applied depending on the time zone rules.
*/
var TimeZone = /** @class */ (function () {
/**
* Do not use this constructor, use the static
* TimeZone.zone() method instead.
* @param name NORMALIZED name, assumed to be correct
* @param dst Adhere to Daylight Saving Time if applicable, ignored for local time and fixed offsets
* @throws timezonecomplete.NotFound.Zone if the given zone name doesn't exist
* @throws timezonecomplete.InvalidTimeZoneData if the time zone database is invalid
*/
function TimeZone(name, dst) {
if (dst === void 0) { dst = true; }
/**
* Allow not using instanceof
*/
this.classKind = "TimeZone";
this._name = name;
this._dst = dst;
if (name === "localtime") {
this._kind = TimeZoneKind.Local;
}
else if (name.charAt(0) === "+" || name.charAt(0) === "-" || name.charAt(0).match(/\d/) || name === "Z") {
this._kind = TimeZoneKind.Offset;
this._offset = TimeZone.stringToOffset(name);
}
else {
this._kind = TimeZoneKind.Proper;
(0, assert_1.default)(tz_database_1.TzDatabase.instance().exists(name), "NotFound.Zone", "non-existing time zone name '%s'", name);
}
}
/**
* The local time zone for a given date. Note that
* the time zone varies with the date: amsterdam time for
* 2014-01-01 is +01:00 and amsterdam time for 2014-07-01 is +02:00
* @throws nothing
*/
TimeZone.local = function () {
return TimeZone._findOrCreate("localtime", true);
};
/**
* The UTC time zone.
* @throws timezonecomplete.NotFound.Zone if the UTC time zone doesn't exist in the time zone database
*/
TimeZone.utc = function () {
return TimeZone._findOrCreate("UTC", true); // use 'true' for DST because we want it to display as "UTC", not "UTC without DST"
};
/**
* zone() implementations
*/
TimeZone.zone = function (a, dst) {
if (dst === void 0) { dst = true; }
var name = "";
switch (typeof (a)) {
case "string":
{
var s = a;
if (s.indexOf("without DST") >= 0) {
dst = false;
s = s.slice(0, s.indexOf("without DST") - 1);
}
name = TimeZone._normalizeString(s);
}
break;
case "number":
{
var offset = a;
(0, assert_1.default)(offset > -24 * 60 && offset < 24 * 60, "Argument.Offset", "TimeZone.zone(): offset out of range");
name = TimeZone.offsetToString(offset);
}
break;
/* istanbul ignore next */
default:
(0, error_1.throwError)("Argument.A", "unexpected type for first argument: %s", typeof a);
}
return TimeZone._findOrCreate(name, dst);
};
/**
* Makes this class appear clonable. NOTE as time zone objects are immutable you will NOT
* actually get a clone but the same object.
* @throws nothing
*/
TimeZone.prototype.clone = function () {
return this;
};
/**
* The time zone identifier. Can be an offset "-01:30" or an
* IANA time zone name "Europe/Amsterdam", or "localtime" for
* the local time zone.
* @throws nothing
*/
TimeZone.prototype.name = function () {
return this._name;
};
/**
* Whether DST is enabled
* @throws nothing
*/
TimeZone.prototype.dst = function () {
return this._dst;
};
/**
* The kind of time zone (Local/Offset/Proper)
* @throws nothing
*/
TimeZone.prototype.kind = function () {
return this._kind;
};
/**
* Equality operator. Maps zero offsets and different names for UTC onto
* each other. Other time zones are not mapped onto each other.
* @throws timezonecomplete.InvalidTimeZoneData if the global time zone data is invalid
*/
TimeZone.prototype.equals = function (other) {
if (this.isUtc() && other.isUtc()) {
return true;
}
switch (this._kind) {
case TimeZoneKind.Local: return (other.kind() === TimeZoneKind.Local);
case TimeZoneKind.Offset: return (other.kind() === TimeZoneKind.Offset && this._offset === other._offset);
case TimeZoneKind.Proper: return (other.kind() === TimeZoneKind.Proper
&& this._name === other._name
&& (this._dst === other._dst || !this.hasDst()));
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
/**
* Returns true iff the constructor arguments were identical, so UTC !== GMT
* @throws nothing
*/
TimeZone.prototype.identical = function (other) {
switch (this._kind) {
case TimeZoneKind.Local: return (other.kind() === TimeZoneKind.Local);
case TimeZoneKind.Offset: return (other.kind() === TimeZoneKind.Offset && this._offset === other._offset);
case TimeZoneKind.Proper: return (other.kind() === TimeZoneKind.Proper && this._name === other._name && this._dst === other._dst);
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
/**
* Is this zone equivalent to UTC?
* @throws timezonecomplete.InvalidTimeZoneData if the global time zone data is invalid
*/
TimeZone.prototype.isUtc = function () {
switch (this._kind) {
case TimeZoneKind.Local: return false;
case TimeZoneKind.Offset: return (this._offset === 0);
case TimeZoneKind.Proper: return (tz_database_1.TzDatabase.instance().zoneIsUtc(this._name));
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
/**
* Does this zone have Daylight Saving Time at all?
* @throws timezonecomplete.InvalidTimeZoneData if the global time zone data is invalid
*/
TimeZone.prototype.hasDst = function () {
switch (this._kind) {
case TimeZoneKind.Local: return false;
case TimeZoneKind.Offset: return false;
case TimeZoneKind.Proper: return (tz_database_1.TzDatabase.instance().hasDst(this._name));
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
TimeZone.prototype.offsetForUtc = function (a, month, day, hour, minute, second, milli) {
var utcTime = (typeof a === "number" ? new basics_1.TimeStruct({ year: a, month: month, day: day, hour: hour, minute: minute, second: second, milli: milli }) :
typeof a === "undefined" ? new basics_1.TimeStruct({}) :
a);
switch (this._kind) {
case TimeZoneKind.Local: {
var date = new Date(Date.UTC(utcTime.components.year, utcTime.components.month - 1, utcTime.components.day, utcTime.components.hour, utcTime.components.minute, utcTime.components.second, utcTime.components.milli));
return -1 * date.getTimezoneOffset();
}
case TimeZoneKind.Offset: {
return this._offset;
}
case TimeZoneKind.Proper: {
if (this._dst) {
return tz_database_1.TzDatabase.instance().totalOffset(this._name, utcTime).minutes();
}
else {
return tz_database_1.TzDatabase.instance().standardOffset(this._name, utcTime).minutes();
}
}
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
TimeZone.prototype.standardOffsetForUtc = function (a, month, day, hour, minute, second, milli) {
var utcTime = (typeof a === "number" ? new basics_1.TimeStruct({ year: a, month: month, day: day, hour: hour, minute: minute, second: second, milli: milli }) :
typeof a === "undefined" ? new basics_1.TimeStruct({}) :
a);
switch (this._kind) {
case TimeZoneKind.Local: {
var date = new Date(Date.UTC(utcTime.components.year, 0, 1, 0));
return -1 * date.getTimezoneOffset();
}
case TimeZoneKind.Offset: {
return this._offset;
}
case TimeZoneKind.Proper: {
return tz_database_1.TzDatabase.instance().standardOffset(this._name, utcTime).minutes();
}
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
TimeZone.prototype.offsetForZone = function (a, month, day, hour, minute, second, milli) {
var localTime = (typeof a === "number" ? new basics_1.TimeStruct({ year: a, month: month, day: day, hour: hour, minute: minute, second: second, milli: milli }) :
typeof a === "undefined" ? new basics_1.TimeStruct({}) :
a);
switch (this._kind) {
case TimeZoneKind.Local: {
var date = new Date(localTime.components.year, localTime.components.month - 1, localTime.components.day, localTime.components.hour, localTime.components.minute, localTime.components.second, localTime.components.milli);
return -1 * date.getTimezoneOffset();
}
case TimeZoneKind.Offset: {
return this._offset;
}
case TimeZoneKind.Proper: {
// note that TzDatabase normalizes the given date so we don't have to do it
if (this._dst) {
return tz_database_1.TzDatabase.instance().totalOffsetLocal(this._name, localTime).minutes();
}
else {
return tz_database_1.TzDatabase.instance().standardOffset(this._name, localTime).minutes();
}
}
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
/**
* Note: will be removed in version 2.0.0
*
* Convenience function, takes values from a Javascript Date
* Calls offsetForUtc() with the contents of the date
*
* @param date: the date
* @param funcs: the set of functions to use: get() or getUTC()
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TimeZone.prototype.offsetForUtcDate = function (date, funcs) {
return this.offsetForUtc(basics_1.TimeStruct.fromDate(date, funcs));
};
/**
* Note: will be removed in version 2.0.0
*
* Convenience function, takes values from a Javascript Date
* Calls offsetForUtc() with the contents of the date
*
* @param date: the date
* @param funcs: the set of functions to use: get() or getUTC()
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TimeZone.prototype.offsetForZoneDate = function (date, funcs) {
return this.offsetForZone(basics_1.TimeStruct.fromDate(date, funcs));
};
TimeZone.prototype.abbreviationForUtc = function (a, b, day, hour, minute, second, milli, c) {
var utcTime;
var dstDependent = true;
if (typeof a !== "number" && !!a) {
utcTime = a;
dstDependent = (b === false ? false : true);
}
else {
utcTime = new basics_1.TimeStruct({ year: a, month: b, day: day, hour: hour, minute: minute, second: second, milli: milli });
dstDependent = (c === false ? false : true);
}
switch (this._kind) {
case TimeZoneKind.Local: {
return "local";
}
case TimeZoneKind.Offset: {
return this.toString();
}
case TimeZoneKind.Proper: {
return tz_database_1.TzDatabase.instance().abbreviation(this._name, utcTime, dstDependent);
}
/* istanbul ignore next */
default:
// istanbul ignore next
return (0, error_1.throwError)("Assertion", "unknown time zone kind");
}
};
TimeZone.prototype.normalizeZoneTime = function (localTime, opt) {
if (opt === void 0) { opt = tz_database_1.NormalizeOption.Up; }
var tzopt = (opt === tz_database_1.NormalizeOption.Down ? tz_database_1.NormalizeOption.Down : tz_database_1.NormalizeOption.Up);
if (this.kind() === TimeZoneKind.Proper) {
if (typeof localTime === "number") {
return tz_database_1.TzDatabase.instance().normalizeLocal(this._name, new basics_1.TimeStruct(localTime), tzopt).unixMillis;
}
else {
return tz_database_1.TzDatabase.instance().normalizeLocal(this._name, localTime, tzopt);
}
}
else {
return localTime;
}
};
/**
* The time zone identifier (normalized).
* Either "localtime", IANA name, or "+hh:mm" offset.
* @throws nothing
*/
TimeZone.prototype.toString = function () {
var result = this.name();
if (this.kind() === TimeZoneKind.Proper) {
if (this.hasDst() && !this.dst()) {
result += " without DST";
}
}
return result;
};
/**
* Convert an offset number into an offset string
* @param offset The offset in minutes from UTC e.g. 90 minutes
* @return the offset in ISO notation "+01:30" for +90 minutes
* @throws Argument.Offset if offset is not a finite number or not within -24 * 60 ... +24 * 60 minutes
*/
TimeZone.offsetToString = function (offset) {
(0, assert_1.default)(Number.isFinite(offset) && offset >= -24 * 60 && offset <= 24 * 60, "Argument.Offset", "invalid offset %d", offset);
var sign = (offset < 0 ? "-" : "+");
var hours = Math.floor(Math.abs(offset) / 60);
var minutes = Math.floor(Math.abs(offset) % 60);
return sign + strings.padLeft(hours.toString(10), 2, "0") + ":" + strings.padLeft(minutes.toString(10), 2, "0");
};
/**
* String to offset conversion.
* @param s Formats: "-01:00", "-0100", "-01", "Z"
* @return offset w.r.t. UTC in minutes
* @throws timezonecomplete.Argument.S if s cannot be parsed
*/
TimeZone.stringToOffset = function (s) {
var t = s.trim();
// easy case
if (t === "Z") {
return 0;
}
// check that the remainder conforms to ISO time zone spec
(0, assert_1.default)(t.match(/^[+-]\d$/) || t.match(/^[+-]\d\d$/) || t.match(/^[+-]\d\d(:?)\d\d$/), "Argument.S", "Wrong time zone format: \"" + t + "\"");
var sign = (t.charAt(0) === "+" ? 1 : -1);
var hours = 0;
var minutes = 0;
switch (t.length) {
case 2:
hours = parseInt(t.slice(1, 2), 10);
break;
case 3:
hours = parseInt(t.slice(1, 3), 10);
break;
case 5:
hours = parseInt(t.slice(1, 3), 10);
minutes = parseInt(t.slice(3, 5), 10);
break;
case 6:
hours = parseInt(t.slice(1, 3), 10);
minutes = parseInt(t.slice(4, 6), 10);
break;
}
(0, assert_1.default)(hours >= 0 && hours < 24, "Argument.S", "Invalid time zone (hours out of range): '".concat(t, "'"));
(0, assert_1.default)(minutes >= 0 && minutes < 60, "Argument.S", "Invalid time zone (minutes out of range): '".concat(t, "'"));
return sign * (hours * 60 + minutes);
};
/**
* Find in cache or create zone
* @param name Time zone name
* @param dst Adhere to Daylight Saving Time?
* @throws timezonecomplete.NotFound.Zone if the zone doesn't exist in the time zone database
*/
TimeZone._findOrCreate = function (name, dst) {
var key = name + (dst ? "_DST" : "_NO-DST");
if (key in TimeZone._cache) {
return TimeZone._cache[key];
}
else {
var t = new TimeZone(name, dst);
TimeZone._cache[key] = t;
return t;
}
};
/**
* Normalize a string so it can be used as a key for a cache lookup
* @throws Argument.S if s is empty
*/
TimeZone._normalizeString = function (s) {
var t = s.trim();
(0, assert_1.default)(t.length > 0, "Argument.S", "Empty time zone string given");
if (t === "localtime") {
return t;
}
else if (t === "Z") {
return "+00:00";
}
else if (TimeZone._isOffsetString(t)) {
// offset string
// normalize by converting back and forth
try {
return TimeZone.offsetToString(TimeZone.stringToOffset(t));
}
catch (e) {
if ((0, error_1.errorIs)(e, "Argument.Offset")) {
e = (0, error_1.error)("Argument.S", e.message);
}
throw e;
}
}
else {
// Olsen TZ database name
return t;
}
};
/**
* Returns true iff the first non-whitespace character of s is +, -, or Z
* @param s
* @throws nothing
*/
TimeZone._isOffsetString = function (s) {
var t = s.trim();
return (t.charAt(0) === "+" || t.charAt(0) === "-" || t === "Z");
};
/**
* Time zone cache.
*/
TimeZone._cache = {};
return TimeZone;
}());
exports.TimeZone = TimeZone;
/**
* Checks if a given object is of type TimeZone. Note that it does not work for sub classes. However, use this to be robust
* against different versions of the library in one process instead of instanceof
* @param value Value to check
* @throws nothing
*/
function isTimeZone(value) {
return typeof value === "object" && value !== null && value.classKind === "TimeZone";
}
exports.isTimeZone = isTimeZone;
//# sourceMappingURL=timezone.js.map