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.
1,207 lines • 95 kB
JavaScript
/**
* Copyright(c) 2014 ABB Switzerland Ltd.
*
* Olsen Timezone Database container
*
* DO NOT USE THIS CLASS DIRECTLY, USE TimeZone
*/
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TzDatabase = exports.NormalizeOption = exports.Transition = exports.isValidOffsetString = exports.ZoneInfo = exports.RuleType = exports.RuleInfo = exports.AtType = exports.OnType = exports.ToType = void 0;
var assert_1 = require("./assert");
var basics_1 = require("./basics");
var basics = require("./basics");
var duration_1 = require("./duration");
var error_1 = require("./error");
var math = require("./math");
/**
* Type of rule TO column value
*/
var ToType;
(function (ToType) {
/**
* Either a year number or "only"
*/
ToType[ToType["Year"] = 0] = "Year";
/**
* "max"
*/
ToType[ToType["Max"] = 1] = "Max";
})(ToType || (exports.ToType = ToType = {}));
/**
* Type of rule ON column value
*/
var OnType;
(function (OnType) {
/**
* Day-of-month number
*/
OnType[OnType["DayNum"] = 0] = "DayNum";
/**
* "lastSun" or "lastWed" etc
*/
OnType[OnType["LastX"] = 1] = "LastX";
/**
* e.g. "Sun>=8"
*/
OnType[OnType["GreqX"] = 2] = "GreqX";
/**
* e.g. "Sun<=8"
*/
OnType[OnType["LeqX"] = 3] = "LeqX";
})(OnType || (exports.OnType = OnType = {}));
var AtType;
(function (AtType) {
/**
* Local time (no DST)
*/
AtType[AtType["Standard"] = 0] = "Standard";
/**
* Wall clock time (local time with DST)
*/
AtType[AtType["Wall"] = 1] = "Wall";
/**
* Utc time
*/
AtType[AtType["Utc"] = 2] = "Utc";
})(AtType || (exports.AtType = AtType = {}));
/**
* DO NOT USE THIS CLASS DIRECTLY, USE TimeZone
*
* See http://www.cstdbill.com/tzdb/tz-how-to.html
*/
var RuleInfo = /** @class */ (function () {
/**
* Constructor
* @param from
* @param toType
* @param toYear
* @param type
* @param inMonth
* @param onType
* @param onDay
* @param onWeekDay
* @param atHour
* @param atMinute
* @param atSecond
* @param atType
* @param save
* @param letter
* @throws nothing
*/
function RuleInfo(
/**
* FROM column year number.
*/
from,
/**
* TO column type: Year for year numbers and "only" values, Max for "max" value.
*/
toType,
/**
* If TO column is a year, the year number. If TO column is "only", the FROM year.
*/
toYear,
/**
* TYPE column, not used so far
*/
type,
/**
* IN column month number 1-12
*/
inMonth,
/**
* ON column type
*/
onType,
/**
* If onType is DayNum, the day number
*/
onDay,
/**
* If onType is not DayNum, the weekday
*/
onWeekDay,
/**
* AT column hour
*/
atHour,
/**
* AT column minute
*/
atMinute,
/**
* AT column second
*/
atSecond,
/**
* AT column type
*/
atType,
/**
* DST offset from local standard time (NOT from UTC!)
*/
save,
/**
* Character to insert in %s for time zone abbreviation
* Note if TZ database indicates "-" this is the empty string
*/
letter) {
this.from = from;
this.toType = toType;
this.toYear = toYear;
this.type = type;
this.inMonth = inMonth;
this.onType = onType;
this.onDay = onDay;
this.onWeekDay = onWeekDay;
this.atHour = atHour;
this.atMinute = atMinute;
this.atSecond = atSecond;
this.atType = atType;
this.save = save;
this.letter = letter;
if (this.save) {
this.save = this.save.convert(basics_1.TimeUnit.Hour);
}
}
/**
* Returns true iff this rule is applicable in the year
* @throws nothing
*/
RuleInfo.prototype.applicable = function (year) {
if (year < this.from) {
return false;
}
switch (this.toType) {
case ToType.Max: return true;
case ToType.Year: return (year <= this.toYear);
}
};
/**
* Sort comparison
* @return (first effective date is less than other's first effective date)
* @throws timezonecomplete.InvalidTimeZoneData if this rule depends on a weekday and the weekday in question doesn't exist
*/
RuleInfo.prototype.effectiveLess = function (other) {
if (this.from < other.from) {
return true;
}
if (this.from > other.from) {
return false;
}
if (this.inMonth < other.inMonth) {
return true;
}
if (this.inMonth > other.inMonth) {
return false;
}
if (this.effectiveDate(this.from) < other.effectiveDate(this.from)) {
return true;
}
return false;
};
/**
* Sort comparison
* @return (first effective date is equal to other's first effective date)
* @throws timezonecomplete.InvalidTimeZoneData for invalid internal structure of the database
*/
RuleInfo.prototype.effectiveEqual = function (other) {
if (this.from !== other.from) {
return false;
}
if (this.inMonth !== other.inMonth) {
return false;
}
if (!this.effectiveDate(this.from).equals(other.effectiveDate(this.from))) {
return false;
}
return true;
};
/**
* Returns the year-relative date that the rule takes effect. Depending on the rule this can be a UTC time, a wall clock time, or a
* time in standard offset (i.e. you still need to compensate for this.atType)
* @throws timezonecomplete.NotApplicable if this rule is not applicable in the given year
*/
RuleInfo.prototype.effectiveDate = function (year) {
(0, assert_1.default)(this.applicable(year), "timezonecomplete.NotApplicable", "Rule is not applicable in %d", year);
// year and month are given
var y = year;
var m = this.inMonth;
var d = 0;
// calculate day
switch (this.onType) {
case OnType.DayNum:
{
d = this.onDay;
}
break;
case OnType.GreqX:
{
try {
d = basics.weekDayOnOrAfter(y, m, this.onDay, this.onWeekDay);
}
catch (e) {
if ((0, error_1.errorIs)(e, "NotFound")) {
// Apr Sun>=27 actually means any sunday after April 27, i.e. it does not have to be in April. Try next month.
if (m + 1 <= 12) {
m = m + 1;
}
else {
m = 1;
y = y + 1;
}
d = basics.firstWeekDayOfMonth(y, m, this.onWeekDay);
}
}
}
break;
case OnType.LeqX:
{
try {
d = basics.weekDayOnOrBefore(y, m, this.onDay, this.onWeekDay);
}
catch (e) {
if ((0, error_1.errorIs)(e, "NotFound")) {
if (m > 1) {
m = m - 1;
}
else {
m = 12;
y = y - 1;
}
d = basics.lastWeekDayOfMonth(y, m, this.onWeekDay);
}
}
}
break;
case OnType.LastX:
{
d = basics.lastWeekDayOfMonth(y, m, this.onWeekDay);
}
break;
}
return basics_1.TimeStruct.fromComponents(y, m, d, this.atHour, this.atMinute, this.atSecond);
};
/**
* Effective date in UTC in the given year, in a specific time zone
* @param year
* @param standardOffset the standard offset from UT of the time zone
* @param dstOffset the DST offset before the rule
*/
RuleInfo.prototype.effectiveDateUtc = function (year, standardOffset, dstOffset) {
var d = this.effectiveDate(year);
switch (this.atType) {
case AtType.Utc: return d;
case AtType.Standard: {
// transition time is in zone local time without DST
var millis = d.unixMillis;
millis -= standardOffset.milliseconds();
return new basics_1.TimeStruct(millis);
}
case AtType.Wall: {
// transition time is in zone local time with DST
var millis = d.unixMillis;
millis -= standardOffset.milliseconds();
if (dstOffset) {
millis -= dstOffset.milliseconds();
}
return new basics_1.TimeStruct(millis);
}
}
};
return RuleInfo;
}());
exports.RuleInfo = RuleInfo;
/**
* Type of reference from zone to rule
*/
var RuleType;
(function (RuleType) {
/**
* No rule applies
*/
RuleType[RuleType["None"] = 0] = "None";
/**
* Fixed given offset
*/
RuleType[RuleType["Offset"] = 1] = "Offset";
/**
* Reference to a named set of rules
*/
RuleType[RuleType["RuleName"] = 2] = "RuleName";
})(RuleType || (exports.RuleType = RuleType = {}));
/**
* DO NOT USE THIS CLASS DIRECTLY, USE TimeZone
*
* See http://www.cstdbill.com/tzdb/tz-how-to.html
* First, and somewhat trivially, whereas Rules are considered to contain one or more records, a Zone is considered to
* be a single record with zero or more continuation lines. Thus, the keyword, “Zone,” and the zone name are not repeated.
* The last line is the one without anything in the [UNTIL] column.
* Second, and more fundamentally, each line of a Zone represents a steady state, not a transition between states.
* The state exists from the date and time in the previous line’s [UNTIL] column up to the date and time in the current line’s
* [UNTIL] column. In other words, the date and time in the [UNTIL] column is the instant that separates this state from the next.
* Where that would be ambiguous because we’re setting our clocks back, the [UNTIL] column specifies the first occurrence of the instant.
* The state specified by the last line, the one without anything in the [UNTIL] column, continues to the present.
* The first line typically specifies the mean solar time observed before the introduction of standard time. Since there’s no line before
* that, it has no beginning. 8-) For some places near the International Date Line, the first two lines will show solar times differing by
* 24 hours; this corresponds to a movement of the Date Line. For example:
* # Zone NAME GMTOFF RULES FORMAT [UNTIL]
* Zone America/Juneau 15:02:19 - LMT 1867 Oct 18
* -8:57:41 - LMT ...
* When Alaska was purchased from Russia in 1867, the Date Line moved from the Alaska/Canada border to the Bering Strait; and the time in
* Alaska was then 24 hours earlier than it had been. <aside>(6 October in the Julian calendar, which Russia was still using then for
* religious reasons, was followed by a second instance of the same day with a different name, 18 October in the Gregorian calendar.
* Isn’t civil time wonderful? 8-))</aside>
* The abbreviation, “LMT,” stands for “local mean time,” which is an invention of the tz database and was probably never actually
* used during the period. Furthermore, the value is almost certainly wrong except in the archetypal place after which the zone is named.
* (The tz database usually doesn’t provide a separate Zone record for places where nothing significant happened after 1970.)
*/
var ZoneInfo = /** @class */ (function () {
/**
* Constructor
* @param gmtoff
* @param ruleType
* @param ruleOffset
* @param ruleName
* @param format
* @param until
* @throws nothing
*/
function ZoneInfo(
/**
* GMT offset in fractional minutes, POSITIVE to UTC (note JavaScript.Date gives offsets
* contrary to what you might expect). E.g. Europe/Amsterdam has +60 minutes in this field because
* it is one hour ahead of UTC
*/
gmtoff,
/**
* The RULES column tells us whether daylight saving time is being observed:
* A hyphen, a kind of null value, means that we have not set our clocks ahead of standard time.
* An amount of time (usually but not necessarily “1:00” meaning one hour) means that we have set our clocks ahead by that amount.
* Some alphabetic string means that we might have set our clocks ahead; and we need to check the rule
* the name of which is the given alphabetic string.
*/
ruleType,
/**
* If the rule column is an offset, this is the offset
*/
ruleOffset,
/**
* If the rule column is a rule name, this is the rule name
*/
ruleName,
/**
* The FORMAT column specifies the usual abbreviation of the time zone name. It can have one of four forms:
* the string, “zzz,” which is a kind of null value (don’t ask)
* a single alphabetic string other than “zzz,” in which case that’s the abbreviation
* a pair of strings separated by a slash (‘/’), in which case the first string is the abbreviation
* for the standard time name and the second string is the abbreviation for the daylight saving time name
* a string containing “%s,” in which case the “%s” will be replaced by the text in the appropriate Rule’s LETTER column
*/
format,
/**
* Until timestamp in unix utc millis. The zone info is valid up to
* and excluding this timestamp.
* Note this value can be undefined (for the first rule)
*/
until) {
this.gmtoff = gmtoff;
this.ruleType = ruleType;
this.ruleOffset = ruleOffset;
this.ruleName = ruleName;
this.format = format;
this.until = until;
if (this.ruleOffset) {
this.ruleOffset = this.ruleOffset.convert(basics.TimeUnit.Hour);
}
}
return ZoneInfo;
}());
exports.ZoneInfo = ZoneInfo;
var TzMonthNames;
(function (TzMonthNames) {
TzMonthNames[TzMonthNames["Jan"] = 1] = "Jan";
TzMonthNames[TzMonthNames["Feb"] = 2] = "Feb";
TzMonthNames[TzMonthNames["Mar"] = 3] = "Mar";
TzMonthNames[TzMonthNames["Apr"] = 4] = "Apr";
TzMonthNames[TzMonthNames["May"] = 5] = "May";
TzMonthNames[TzMonthNames["Jun"] = 6] = "Jun";
TzMonthNames[TzMonthNames["Jul"] = 7] = "Jul";
TzMonthNames[TzMonthNames["Aug"] = 8] = "Aug";
TzMonthNames[TzMonthNames["Sep"] = 9] = "Sep";
TzMonthNames[TzMonthNames["Oct"] = 10] = "Oct";
TzMonthNames[TzMonthNames["Nov"] = 11] = "Nov";
TzMonthNames[TzMonthNames["Dec"] = 12] = "Dec";
})(TzMonthNames || (TzMonthNames = {}));
/**
* Turns a month name from the TZ database into a number 1-12
* @param name
* @throws timezonecomplete.InvalidTimeZoneData for invalid month name
*/
function monthNameToNumber(name) {
for (var i = 1; i <= 12; ++i) {
if (TzMonthNames[i] === name) {
return i;
}
}
return (0, error_1.throwError)("InvalidTimeZoneData", "Invalid month name '%s'", name);
}
var TzDayNames;
(function (TzDayNames) {
TzDayNames[TzDayNames["Sun"] = 0] = "Sun";
TzDayNames[TzDayNames["Mon"] = 1] = "Mon";
TzDayNames[TzDayNames["Tue"] = 2] = "Tue";
TzDayNames[TzDayNames["Wed"] = 3] = "Wed";
TzDayNames[TzDayNames["Thu"] = 4] = "Thu";
TzDayNames[TzDayNames["Fri"] = 5] = "Fri";
TzDayNames[TzDayNames["Sat"] = 6] = "Sat";
})(TzDayNames || (TzDayNames = {}));
/**
* Returns true if the given string is a valid offset string i.e.
* 1, -1, +1, 01, 1:00, 1:23:25.143
* @throws nothing
*/
function isValidOffsetString(s) {
return /^(\-|\+)?([0-9]+((\:[0-9]+)?(\:[0-9]+(\.[0-9]+)?)?))$/.test(s);
}
exports.isValidOffsetString = isValidOffsetString;
/**
* Defines a moment at which the given rule becomes valid
*/
var Transition = /** @class */ (function () {
/**
* Constructor
* @param at
* @param offset
* @param letter
* @throws nothing
*/
function Transition(
/**
* Transition time in UTC millis
*/
at,
/**
* New offset (type of offset depends on the function)
*/
offset,
/**
* New timzone abbreviation letter
*/
letter) {
this.at = at;
this.offset = offset;
this.letter = letter;
if (this.offset) {
this.offset = this.offset.convert(basics.TimeUnit.Hour);
}
}
return Transition;
}());
exports.Transition = Transition;
/**
* Option for TzDatabase#normalizeLocal()
*/
var NormalizeOption;
(function (NormalizeOption) {
/**
* Normalize non-existing times by ADDING the DST offset
*/
NormalizeOption[NormalizeOption["Up"] = 0] = "Up";
/**
* Normalize non-existing times by SUBTRACTING the DST offset
*/
NormalizeOption[NormalizeOption["Down"] = 1] = "Down";
})(NormalizeOption || (exports.NormalizeOption = NormalizeOption = {}));
/**
* This class is a wrapper around time zone data JSON object from the tzdata NPM module.
* You usually do not need to use this directly, use TimeZone and DateTime instead.
*/
var TzDatabase = /** @class */ (function () {
/**
* Constructor - do not use, this is a singleton class. Use TzDatabase.instance() instead
* @throws AlreadyCreated if an instance already exists
* @throws timezonecomplete.InvalidTimeZoneData if `data` is empty or invalid
*/
function TzDatabase(data) {
var _this = this;
/**
* Performance improvement: zone info cache
*/
this._zoneInfoCache = {};
/**
* Performance improvement: rule info cache
*/
this._ruleInfoCache = {};
/**
* pre-calculated transitions per zone
*/
this._zoneTransitionsCache = new Map();
/**
* pre-calculated transitions per ruleset
*/
this._ruleTransitionsCache = new Map();
(0, assert_1.default)(!TzDatabase._instance, "AlreadyCreated", "You should not create an instance of the TzDatabase class yourself. Use TzDatabase.instance()");
(0, assert_1.default)(data.length > 0, "InvalidTimeZoneData", "Timezonecomplete needs time zone data. You need to install one of the tzdata NPM modules before using timezonecomplete.");
if (data.length === 1) {
this._data = data[0];
}
else {
this._data = { zones: {}, rules: {} };
data.forEach(function (d) {
if (d && d.rules && d.zones) {
for (var _i = 0, _a = Object.keys(d.rules); _i < _a.length; _i++) {
var key = _a[_i];
_this._data.rules[key] = d.rules[key];
}
for (var _b = 0, _c = Object.keys(d.zones); _b < _c.length; _b++) {
var key = _c[_b];
_this._data.zones[key] = d.zones[key];
}
}
});
}
this._minmax = validateData(this._data);
}
/**
* (re-) initialize timezonecomplete with time zone data
*
* @param data TZ data as JSON object (from one of the tzdata NPM modules).
* If not given, Timezonecomplete will search for installed modules.
* @throws timezonecomplete.InvalidTimeZoneData if `data` or the global time zone data is invalid
*/
TzDatabase.init = function (data) {
TzDatabase._instance = undefined; // needed for assert in constructor
if (data) {
TzDatabase._instance = new TzDatabase(Array.isArray(data) ? data : [data]);
}
else {
var data_1 = [];
// try to find TZ data in global variables
var g = void 0;
if (typeof window !== "undefined") {
g = window;
}
else if (typeof global !== "undefined") {
g = global;
}
else if (typeof self !== "undefined") {
g = self;
}
else {
g = {};
}
if (g) {
for (var _i = 0, _a = Object.keys(g); _i < _a.length; _i++) {
var key = _a[_i];
if (key.startsWith("tzdata")) {
if (typeof g[key] === "object" && g[key].rules && g[key].zones) {
data_1.push(g[key]);
}
}
}
}
// try to find TZ data as installed NPM modules
var findNodeModules = function (require) {
try {
// first try tzdata which contains all data
var tzDataName = "tzdata";
var d = require(tzDataName); // use variable to avoid browserify acting up
data_1.push(d);
}
catch (e) {
// then try subsets
var moduleNames = [
"tzdata-africa",
"tzdata-antarctica",
"tzdata-asia",
"tzdata-australasia",
"tzdata-backward",
"tzdata-backward-utc",
"tzdata-etcetera",
"tzdata-europe",
"tzdata-northamerica",
"tzdata-pacificnew",
"tzdata-southamerica",
"tzdata-systemv"
];
moduleNames.forEach(function (moduleName) {
try {
var d = require(moduleName);
data_1.push(d);
}
catch (e) {
// nothing
}
});
}
};
if (data_1.length === 0) {
if (typeof module === "object" && typeof module.exports === "object") {
findNodeModules(require); // need to put require into a function to make webpack happy
}
}
TzDatabase._instance = new TzDatabase(data_1);
}
};
/**
* Single instance of this database
* @throws timezonecomplete.InvalidTimeZoneData if the global time zone data is invalid
*/
TzDatabase.instance = function () {
if (!TzDatabase._instance) {
TzDatabase.init();
}
return TzDatabase._instance;
};
/**
* Returns a sorted list of all zone names
* @throws nothing
*/
TzDatabase.prototype.zoneNames = function () {
if (!this._zoneNames) {
this._zoneNames = Object.keys(this._data.zones);
this._zoneNames.sort();
}
return this._zoneNames;
};
/**
* Returns true iff the given zone name exists
* @param zoneName
* @throws nothing
*/
TzDatabase.prototype.exists = function (zoneName) {
return this._data.zones.hasOwnProperty(zoneName);
};
/**
* Minimum non-zero DST offset (which excludes standard offset) of all rules in the database.
* Note that DST offsets need not be whole hours.
*
* Does return zero if a zoneName is given and there is no DST at all for the zone.
*
* @param zoneName (optional) if given, the result for the given zone is returned
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.minDstSave = function (zoneName) {
try {
if (zoneName) {
var zoneInfos = this.getZoneInfos(zoneName);
var result = void 0;
var ruleNames = [];
for (var _i = 0, zoneInfos_1 = zoneInfos; _i < zoneInfos_1.length; _i++) {
var zoneInfo = zoneInfos_1[_i];
if (zoneInfo.ruleType === RuleType.Offset) {
if (!result || result.greaterThan(zoneInfo.ruleOffset)) {
if (zoneInfo.ruleOffset.milliseconds() !== 0) {
result = zoneInfo.ruleOffset;
}
}
}
if (zoneInfo.ruleType === RuleType.RuleName && ruleNames.indexOf(zoneInfo.ruleName) === -1) {
ruleNames.push(zoneInfo.ruleName);
var temp = this.getRuleInfos(zoneInfo.ruleName);
for (var _a = 0, temp_1 = temp; _a < temp_1.length; _a++) {
var ruleInfo = temp_1[_a];
if (!result || result.greaterThan(ruleInfo.save)) {
if (ruleInfo.save.milliseconds() !== 0) {
result = ruleInfo.save;
}
}
}
}
}
if (!result) {
result = duration_1.Duration.hours(0);
}
return result.clone();
}
else {
return duration_1.Duration.minutes(this._minmax.minDstSave);
}
}
catch (e) {
if ((0, error_1.errorIs)(e, ["NotFound.Rule", "Argument.N"])) {
e = (0, error_1.error)("InvalidTimeZoneData", e.message);
}
throw e;
}
};
/**
* Maximum DST offset (which excludes standard offset) of all rules in the database.
* Note that DST offsets need not be whole hours.
*
* Returns 0 if zoneName given and no DST observed.
*
* @param zoneName (optional) if given, the result for the given zone is returned
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.maxDstSave = function (zoneName) {
try {
if (zoneName) {
var zoneInfos = this.getZoneInfos(zoneName);
var result = void 0;
var ruleNames = [];
for (var _i = 0, zoneInfos_2 = zoneInfos; _i < zoneInfos_2.length; _i++) {
var zoneInfo = zoneInfos_2[_i];
if (zoneInfo.ruleType === RuleType.Offset) {
if (!result || result.lessThan(zoneInfo.ruleOffset)) {
result = zoneInfo.ruleOffset;
}
}
if (zoneInfo.ruleType === RuleType.RuleName
&& ruleNames.indexOf(zoneInfo.ruleName) === -1) {
ruleNames.push(zoneInfo.ruleName);
var temp = this.getRuleInfos(zoneInfo.ruleName);
for (var _a = 0, temp_2 = temp; _a < temp_2.length; _a++) {
var ruleInfo = temp_2[_a];
if (!result || result.lessThan(ruleInfo.save)) {
result = ruleInfo.save;
}
}
}
}
if (!result) {
result = duration_1.Duration.hours(0);
}
return result.clone();
}
else {
return duration_1.Duration.minutes(this._minmax.maxDstSave);
}
}
catch (e) {
if ((0, error_1.errorIs)(e, ["NotFound.Rule", "Argument.N"])) {
e = (0, error_1.error)("InvalidTimeZoneData", e.message);
}
throw e;
}
};
/**
* Checks whether the zone has DST at all
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.hasDst = function (zoneName) {
return (this.maxDstSave(zoneName).milliseconds() !== 0);
};
TzDatabase.prototype.nextDstChange = function (zoneName, a) {
var utcTime = (typeof a === "number" ? new basics_1.TimeStruct(a) : a);
var zone = this._getZoneTransitions(zoneName);
var iterator = zone.findFirst();
if (iterator && iterator.transition.atUtc > utcTime) {
return iterator.transition.atUtc.unixMillis;
}
while (iterator) {
iterator = zone.findNext(iterator);
if (iterator && iterator.transition.atUtc > utcTime) {
return iterator.transition.atUtc.unixMillis;
}
}
return undefined;
};
/**
* Returns true iff the given zone name eventually links to
* "Etc/UTC", "Etc/GMT" or "Etc/UCT" in the TZ database. This is true e.g. for
* "UTC", "GMT", "Etc/GMT" etc.
*
* @param zoneName IANA time zone name.
* @throws nothing
*/
TzDatabase.prototype.zoneIsUtc = function (zoneName) {
var actualZoneName = zoneName;
var zoneEntries = this._data.zones[zoneName];
// follow links
while (typeof (zoneEntries) === "string") {
/* istanbul ignore if */
if (!this._data.zones.hasOwnProperty(zoneEntries)) {
throw new Error("Zone \"" + zoneEntries + "\" not found (referred to in link from \""
+ zoneName + "\" via \"" + actualZoneName + "\"");
}
actualZoneName = zoneEntries;
zoneEntries = this._data.zones[actualZoneName];
}
return (actualZoneName === "Etc/UTC" || actualZoneName === "Etc/GMT" || actualZoneName === "Etc/UCT");
};
TzDatabase.prototype.normalizeLocal = function (zoneName, a, opt) {
if (opt === void 0) { opt = NormalizeOption.Up; }
if (this.hasDst(zoneName)) {
var localTime = (typeof a === "number" ? new basics_1.TimeStruct(a) : a);
// local times behave like this during DST changes:
// forward change (1h): 0 1 3 4 5
// forward change (2h): 0 1 4 5 6
// backward change (1h): 1 2 2 3 4
// backward change (2h): 1 2 1 2 3
// Therefore, binary searching is not possible.
// Instead, we should check the DST forward transitions within a window around the local time
// get all transitions (note this includes fake transition rules for zone offset changes)
var zone = this._getZoneTransitions(zoneName);
var transitions = zone.transitionsInYears(localTime.components.year - 1, localTime.components.year + 1);
// find the DST forward transitions
var prev = duration_1.Duration.hours(0);
for (var _i = 0, transitions_1 = transitions; _i < transitions_1.length; _i++) {
var transition = transitions_1[_i];
var offset = transition.newState.dstOffset.add(transition.newState.standardOffset);
// forward transition?
if (offset.greaterThan(prev)) {
var localBefore = transition.atUtc.unixMillis + prev.milliseconds();
var localAfter = transition.atUtc.unixMillis + offset.milliseconds();
if (localTime.unixMillis >= localBefore && localTime.unixMillis < localAfter) {
var forwardChange = offset.sub(prev);
// non-existing time
var factor = (opt === NormalizeOption.Up ? 1 : -1);
var resultMillis = localTime.unixMillis + factor * forwardChange.milliseconds();
return (typeof a === "number" ? resultMillis : new basics_1.TimeStruct(resultMillis));
}
}
prev = offset;
}
// no non-existing time
}
return (typeof a === "number" ? a : a.clone());
};
/**
* Returns the standard time zone offset from UTC, without DST.
* Throws if info not found.
* @param zoneName IANA time zone name
* @param utcTime Timestamp in UTC, either as TimeStruct or as Unix millisecond value
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.standardOffset = function (zoneName, utcTime) {
var zoneInfo = this.getZoneInfo(zoneName, utcTime);
return zoneInfo.gmtoff.clone();
};
/**
* Returns the total time zone offset from UTC, including DST, at
* the given UTC timestamp.
* Throws if zone info not found.
*
* @param zoneName IANA time zone name
* @param utcTime Timestamp in UTC, either as TimeStruct or as Unix millisecond value
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.totalOffset = function (zoneName, utcTime) {
var u = typeof utcTime === "number" ? new basics_1.TimeStruct(utcTime) : utcTime;
var zone = this._getZoneTransitions(zoneName);
var state = zone.stateAt(u);
return state.dstOffset.add(state.standardOffset);
};
/**
* The time zone rule abbreviation, e.g. CEST for Central European Summer Time.
* Note this is dependent on the time, because with time different rules are in effect
* and therefore different abbreviations. They also change with DST: e.g. CEST or CET.
*
* @param zoneName IANA zone name
* @param utcTime Timestamp in UTC unix milliseconds
* @param dstDependent (default true) set to false for a DST-agnostic abbreviation
* @return The abbreviation of the rule that is in effect
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.abbreviation = function (zoneName, utcTime, dstDependent) {
if (dstDependent === void 0) { dstDependent = true; }
var u = typeof utcTime === "number" ? new basics_1.TimeStruct(utcTime) : utcTime;
var zone = this._getZoneTransitions(zoneName);
if (dstDependent) {
var state = zone.stateAt(u);
return state.abbreviation;
}
else {
var lastNonDst = zone.initialState.dstOffset.milliseconds() === 0 ? zone.initialState.abbreviation : "";
var iterator = zone.findFirst();
if ((iterator === null || iterator === void 0 ? void 0 : iterator.transition.newState.dstOffset.milliseconds()) === 0) {
lastNonDst = iterator.transition.newState.abbreviation;
}
while (iterator && iterator.transition.atUtc <= u) {
iterator = zone.findNext(iterator);
if ((iterator === null || iterator === void 0 ? void 0 : iterator.transition.newState.dstOffset.milliseconds()) === 0) {
lastNonDst = iterator.transition.newState.abbreviation;
}
}
return lastNonDst;
}
};
/**
* Returns the standard time zone offset from UTC, excluding DST, at
* the given LOCAL timestamp, again excluding DST.
*
* If the local timestamp exists twice (as can occur very rarely due to zone changes)
* then the first occurrence is returned.
*
* Throws if zone info not found.
*
* @param zoneName IANA time zone name
* @param localTime Timestamp in time zone time
* @throws timezonecomplete.NotFound.Zone if zoneName not found
* @throws timezonecomplete.InvalidTimeZoneData if an error is discovered in the time zone database
*/
TzDatabase.prototype.standardOffsetLocal = function (zoneName, localTime) {
var unixMillis = (typeof localTime === "number" ? localTime : localTime.unixMillis);
var zoneInfos = this.getZoneInfos(zoneName);
for (var _i = 0, zoneInfos_3 = zoneInfos; _i < zoneInfos_3.length; _i++) {
var zoneInfo = zoneInfos_3[_i];
if (zoneInfo.until === undefined || zoneInfo.until + zoneInfo.gmtoff.milliseconds() > unixMillis) {
return zoneInfo.gmtoff.clone();
}
}
/* istanbul ignore if */
/* istanbul ignore next */
if (true) {
return (0, error_1.throwError)("InvalidTimeZoneData", "No zone info found");
}
};
/**
* Returns the total time zone offset from UTC, including DST, at
* the given LOCAL timestamp. Non-existing local time is normalized out.
* There can be multiple UTC times and therefore multiple offsets for a local time
* namely during a backward DST change. This returns the FIRST such offset.
* Throws if zone info not found.
*
* @param zoneName IANA time zone name
* @param localTime Timestamp in time zone time
* @throws timezonecomplete.NotFound.Zone if zoneName not found
* @throws timezonecomplete.InvalidTimeZoneData if an error is discovered in the time zone database
*/
TzDatabase.prototype.totalOffsetLocal = function (zoneName, localTime) {
var ts = (typeof localTime === "number" ? new basics_1.TimeStruct(localTime) : localTime);
var normalizedTm = this.normalizeLocal(zoneName, ts);
/// Note: during offset changes, local time can behave like:
// forward change (1h): 0 1 3 4 5
// forward change (2h): 0 1 4 5 6
// backward change (1h): 1 2 2 3 4
// backward change (2h): 1 2 1 2 3 <-- note time going BACKWARD
// Therefore binary search does not apply. Linear search through transitions
// and return the first offset that matches
var zone = this._getZoneTransitions(zoneName);
var transitions = zone.transitionsInYears(normalizedTm.components.year - 1, normalizedTm.components.year + 2);
var prev;
var prevPrev;
for (var _i = 0, transitions_2 = transitions; _i < transitions_2.length; _i++) {
var transition = transitions_2[_i];
var offset = transition.newState.dstOffset.add(transition.newState.standardOffset);
if (transition.atUtc.unixMillis + offset.milliseconds() > normalizedTm.unixMillis) {
// found offset: prev.offset applies
break;
}
prevPrev = prev;
prev = transition;
}
/* istanbul ignore else */
if (prev) {
// special care during backward change: take first occurrence of local time
var prevOffset = prev.newState.dstOffset.add(prev.newState.standardOffset);
var prevPrevOffset = prevPrev ? prevPrev.newState.dstOffset.add(prevPrev.newState.standardOffset) : undefined;
if (prevPrev && prevPrevOffset !== undefined && prevPrevOffset.greaterThan(prevOffset)) {
// backward change
var diff = prevPrevOffset.sub(prevOffset);
if (normalizedTm.unixMillis >= prev.atUtc.unixMillis + prevOffset.milliseconds()
&& normalizedTm.unixMillis < prev.atUtc.unixMillis + prevOffset.milliseconds() + diff.milliseconds()) {
// within duplicate range
return prevPrevOffset.clone();
}
else {
return prevOffset.clone();
}
}
else {
return prevOffset.clone();
}
}
else {
var state = zone.stateAt(normalizedTm);
return state.dstOffset.add(state.standardOffset);
}
};
/**
* DEPRECATED because DST offset depends on the zone too, not just on the ruleset
* Returns the DST offset (WITHOUT the standard zone offset) for the given ruleset and the given UTC timestamp
*
* @deprecated
* @param ruleName name of ruleset
* @param utcTime UTC timestamp
* @param standardOffset Standard offset without DST for the time zone
* @throws timezonecomplete.NotFound.Rule if ruleName not found
* @throws timezonecomplete.InvalidTimeZoneData if an error is discovered in the time zone database
*/
TzDatabase.prototype.dstOffsetForRule = function (ruleName, utcTime, standardOffset) {
var ts = (typeof utcTime === "number" ? new basics_1.TimeStruct(utcTime) : utcTime);
// find applicable transition moments
var transitions = this.getTransitionsDstOffsets(ruleName, ts.components.year - 1, ts.components.year, standardOffset);
// find the last prior to given date
var offset;
for (var i = transitions.length - 1; i >= 0; i--) {
var transition = transitions[i];
if (transition.at <= ts.unixMillis) {
offset = transition.offset.clone();
break;
}
}
/* istanbul ignore if */
if (!offset) {
// apparently no longer DST, as e.g. for Asia/Tokyo
offset = duration_1.Duration.minutes(0);
}
return offset;
};
/**
* Returns the time zone letter for the given
* ruleset and the given UTC timestamp
*
* @deprecated
* @param ruleName name of ruleset
* @param utcTime UTC timestamp as TimeStruct or unix millis
* @param standardOffset Standard offset without DST for the time zone
* @throws timezonecomplete.NotFound.Rule if ruleName not found
* @throws timezonecomplete.InvalidTimeZoneData if an error is discovered in the time zone database
*/
TzDatabase.prototype.letterForRule = function (ruleName, utcTime, standardOffset) {
var ts = (typeof utcTime === "number" ? new basics_1.TimeStruct(utcTime) : utcTime);
// find applicable transition moments
var transitions = this.getTransitionsDstOffsets(ruleName, ts.components.year - 1, ts.components.year, standardOffset);
// find the last prior to given date
var letter;
for (var i = transitions.length - 1; i >= 0; i--) {
var transition = transitions[i];
if (transition.at <= ts.unixMillis) {
letter = transition.letter;
break;
}
}
/* istanbul ignore if */
if (!letter) {
// apparently no longer DST, as e.g. for Asia/Tokyo
letter = "";
}
return letter;
};
/**
* DEPRECATED because DST offset depends on the zone too, not just on the ruleset
* Return a list of all transitions in [fromYear..toYear] sorted by effective date
*
* @deprecated
* @param ruleName Name of the rule set
* @param fromYear first year to return transitions for
* @param toYear Last year to return transitions for
* @param standardOffset Standard offset without DST for the time zone
*
* @return Transitions, with DST offsets (no standard offset included)
* @throws timezonecomplete.Argument.FromYear if fromYear > toYear
* @throws timezonecomplete.NotFound.Rule if ruleName not found
* @throws timezonecomplete.InvalidTimeZoneData if an error is discovered in the time zone database
*/
TzDatabase.prototype.getTransitionsDstOffsets = function (ruleName, fromYear, toYear, standardOffset) {
(0, assert_1.default)(fromYear <= toYear, "Argument.FromYear", "fromYear must be <= toYear");
var rules = this._getRuleTransitions(ruleName);
var result = [];
var prevDst = (0, duration_1.hours)(0); // wrong, but that's why the function is deprecated
var iterator = rules.findFirst();
while (iterator && iterator.transition.at.year <= toYear) {
if (iterator.transition.at.year >= fromYear && iterator.transition.at.year <= toYear) {
result.push({
at: ruleTransitionUtc(iterator.transition, standardOffset, prevDst).unixMillis,
letter: iterator.transition.newState.letter || "",
offset: iterator.transition.newState.dstOffset
});
}
prevDst = iterator.transition.newState.dstOffset;
iterator = rules.findNext(iterator);
}
result.sort(function (a, b) {
return a.at - b.at;
});
return result;
};
/**
* Return both zone and rule changes as total (std + dst) offsets.
* Adds an initial transition if there is none within the range.
*
* @param zoneName IANA zone name
* @param fromYear First year to include
* @param toYear Last year to include
* @throws timezonecomplete.Argument.FromYear if fromYear > toYear
* @throws timezonecomplete.NotFound.Zone if zoneName not found
* @throws timezonecomplete.InvalidTimeZoneData if an error is discovered in the time zone database
*/
TzDatabase.prototype.getTransitionsTotalOffsets = function (zoneName, fromYear, toYear) {
(0, assert_1.default)(fromYear <= toYear, "Argument.FromYear", "fromYear must be <= toYear");
var zone = this._getZoneTransitions(zoneName);
var result = [];
var startState = zone.stateAt(new basics_1.TimeStruct({ year: fromYear, month: 1, day: 1 }));
result.push({
at: new basics_1.TimeStruct({ year: fromYear }).unixMillis,
letter: startState.letter,
offset: startState.dstOffset.add(startState.standardOffset)
});
var iterator = zone.findFirst();
while (iterator && iterator.transition.atUtc.year <= toYear) {
if (iterator.transition.atUtc.year >= fromYear) {
result.push({
at: iterator.transition.atUtc.unixMillis,
letter: iterator.transition.newState.letter || "",
offset: iterator.transition.newState.dstOffset.add(iterator.transition.newState.standardOffset)
});
}
iterator = zone.findNext(iterator);
}
result.sort(function (a, b) {
return a.at - b.at;
});
return result;
};
/**
* Get the zone info for the given UTC timestamp. Throws if not found.
* @param zoneName IANA time zone name
* @param utcTime UTC time stamp as unix milliseconds or as a TimeStruct
* @returns ZoneInfo object. Do not change, we cache this object.
* @throws timezonecomplete.NotFound.Zone if zone name not found or a linked zone not found
* @throws timezonecomplete.InvalidTimeZoneData if values in the time zone database are invalid
*/
TzDatabase.prototype.getZoneInfo = function (zoneName, utcTime) {
var unixMillis = (typeof utcTime === "number" ? utcTime : utcTime.unixMillis);
var zoneInfos = this.getZoneInfos(zoneName);
for (var _i = 0, zoneInfos_4 = zoneInfos; _i < zoneInfos_4.length; _i++) {
var zoneInfo = zoneInfos_4[_i];
if (zoneInfo.until === undefined || zoneInfo.until > unixMillis) {
return zoneInfo;
}
}
return (0, error_1.throwError)("NotFound.Zone", "no zone info found for zone '%s'", zoneName);
};
/**
* Return the zone records for a given zone name sorted by UNTIL, after
* following any links.
*
* @param zoneName IANA zone name like "Pacific/Efate"
* @return Array of zone infos. Do not change, this is a cached value.
* @throws timezonecomplete.NotFound.Zone if zone does not exist or a linked zone does not exit
*/
TzDatabase.prototype.getZoneInfos = function (zoneName) {
// FIRST validate zone name before searching cache
/* istanbul ignore if */
(0, assert_1.default)(this._data.zones.hasOwnProperty(zoneName), "NotFound.Zone", "zone not found: '%s'", zoneName);
// Take from cache
if (this._zoneInfoCache.hasOwnProperty(zoneName)) {
return this._zoneInfoCache[zoneName];
}
var result = [];
var actualZoneName = zoneName;
var zoneEntries = this._data.zones[zoneName];
// follow links
while (typeof (zoneEntries) === "string") {
/* istanbul ignore if */
if (!this._data.zones.hasOwnProperty(zoneEntries)) {
return (0, error_1.throwError)("NotFound.Zone", "Zone \"" + zoneEntries + "\" not found (referred to in link from \""
+ zoneName + "\" via