UNPKG

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
/** * 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