From d983dfacc960b2c86edad3e2451bec96c02da3b2 Mon Sep 17 00:00:00 2001
From: Justin Grant
Date: Mon, 12 Jun 2023 15:12:57 -0700
Subject: [PATCH 1/2] Editorial: Refactor time zone identifier spec text
This commit builds on the new ECMA-262 text for time zone identifiers
that was introduced in https://github.com/tc39/ecma262/pull/3035.
This commit adds Temporal-specific AOs and makes a handful of
edits to ECMA-262 AOs for changes that didn't apply to %Date% but that
will be required after Temporal is merged into ECMA-262.
This commit also revises the ASCII-case-insensitive section to address
feedback from ECMA-262 editors before this text was removed from
https://github.com/tc39/ecma262/pull/3035:
* Remove the "sequence of code points" info because only Strings seemed
to use these definitions.
* Minor wordsmithing, e.g. "string value" => "String"
---
package-lock.json | 14 +--
package.json | 2 +-
spec/intl.html | 187 ++++++++++++++++++++++++++++------------
spec/mainadditions.html | 81 ++++++++++++++---
spec/temporal.html | 6 +-
spec/timezone.html | 148 ++++++++++---------------------
6 files changed, 262 insertions(+), 176 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index fa8ffd313a..8b64fdad77 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "BSD-3-Clause",
"devDependencies": {
- "@tc39/ecma262-biblio": "=2.1.2576",
+ "@tc39/ecma262-biblio": "=2.1.2577",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"ecmarkup": "^17.0.0",
@@ -323,9 +323,9 @@
}
},
"node_modules/@tc39/ecma262-biblio": {
- "version": "2.1.2576",
- "resolved": "https://registry.npmjs.org/@tc39/ecma262-biblio/-/ecma262-biblio-2.1.2576.tgz",
- "integrity": "sha512-GG+y00NrCcRy6oN2/4t50xaKXE0FA76ZuVAw0NjhjVS/yboxkN0QrPUMUY6OI9TzzuTM0sazRJOp6qsOJ0Kerg==",
+ "version": "2.1.2577",
+ "resolved": "https://registry.npmjs.org/@tc39/ecma262-biblio/-/ecma262-biblio-2.1.2577.tgz",
+ "integrity": "sha512-Grv210DOA5HGNo56tHAxP4ScujqG7NRMWIraGNSxtTxKxUdcTAubrcglqa6bbKNsTK+Dg2Bew8cSr06fJX827g==",
"dev": true
},
"node_modules/@tootallnate/once": {
@@ -3062,9 +3062,9 @@
}
},
"@tc39/ecma262-biblio": {
- "version": "2.1.2576",
- "resolved": "https://registry.npmjs.org/@tc39/ecma262-biblio/-/ecma262-biblio-2.1.2576.tgz",
- "integrity": "sha512-GG+y00NrCcRy6oN2/4t50xaKXE0FA76ZuVAw0NjhjVS/yboxkN0QrPUMUY6OI9TzzuTM0sazRJOp6qsOJ0Kerg==",
+ "version": "2.1.2577",
+ "resolved": "https://registry.npmjs.org/@tc39/ecma262-biblio/-/ecma262-biblio-2.1.2577.tgz",
+ "integrity": "sha512-Grv210DOA5HGNo56tHAxP4ScujqG7NRMWIraGNSxtTxKxUdcTAubrcglqa6bbKNsTK+Dg2Bew8cSr06fJX827g==",
"dev": true
},
"@tootallnate/once": {
diff --git a/package.json b/package.json
index e84e0b727e..c3918f5973 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,7 @@
},
"main": "polyfill/lib/index.mjs",
"devDependencies": {
- "@tc39/ecma262-biblio": "=2.1.2576",
+ "@tc39/ecma262-biblio": "=2.1.2577",
"@typescript-eslint/eslint-plugin": "^5.59.9",
"@typescript-eslint/parser": "^5.59.9",
"ecmarkup": "^17.0.0",
diff --git a/spec/intl.html b/spec/intl.html
index 087d5e3d72..53e0579911 100644
--- a/spec/intl.html
+++ b/spec/intl.html
@@ -8,6 +8,7 @@ Amendments to the ECMAScript® 2023 Internationalization API Specification
This section lists amendments which must be made to ECMA-402, the ECMAScript® 2023 Internationalization API Specification.
Text to be added is marked like this, and text to be deleted is marked like this.
+ Blocks of unmodified text between modified sections are marked by [...].
This text is based on top of the ECMA-402 spec text from commit eb81befe8b739f976a3d6e68ec567302c4e217f0.
@@ -40,15 +41,89 @@
-
-
- IsValidTimeZoneName ( _timeZone_ )
+
+ IsValidTimeZoneName ( _timeZone_ )
-
- The abstract operation IsValidTimeZoneName takes argument _timeZone_, a String value, and verifies that it represents a valid Zone or Link name of the IANA Time Zone Database.
-
+
+ The abstract operation IsValidTimeZoneName takes argument _timeZone_, a String value, and verifies that it represents a valid Zone or Link name of the IANA Time Zone Database.
+
-
- 1. If one of the Zone or Link names of the IANA Time Zone Database is an ASCII-case-insensitive match of _timeZone_, return *true*.
- 1. If _timeZone_ is an ASCII-case-insensitive match of *"UTC"*, return *true*.
- 1. Return *false*.
-
-
-
+
+ 1. If one of the Zone or Link names of the IANA Time Zone Database is an ASCII-case-insensitive match of _timeZone_, return *true*.
+ 1. If _timeZone_ is an ASCII-case-insensitive match of *"UTC"*, return *true*.
+ 1. Return *false*.
+
+
-
+
CanonicalizeTimeZoneName (
- _timeZone_: a String value that is a validan available time zone name as verified by IsValidTimeZoneNameIsAvailableTimeZoneName,
+ _timeZone_: a String value that is a valid time zone name as verified by IsValidTimeZoneName,
)
-
1. Let _ianaTimeZone_ be the String value of the Zone or Link name of the IANA Time Zone Database that is an ASCII-case-insensitive match of _timeZone_.
1. If _ianaTimeZone_ is a Link name, let _ianaTimeZone_ be the String value of the corresponding Zone name as specified in the file backward of the IANA Time Zone Database.
1. If _ianaTimeZone_ is *"Etc/UTC"* or *"Etc/GMT"*, return *"UTC"*.
1. Return _ianaTimeZone_.
+
-
- This definition supersedes the definition provided in .
-
+
+ DefaultTimeZone ( ): a String
+
+
+
+
+ This definition supersedes the definition provided in es2024, .
+
-
-
-
- AvailableTimeZones (
- ): a List of Strings
-
-
-
- 1. Let _timeZones_ be a List containing the String value of each Zone or Link name in the IANA Time Zone Database that is supported by the implementation.
- 1. Assert: _timeZones_ contains *"UTC"*.
- 1. Assert: _timeZones_ does not contain any element that does not identify a Zone or Link name in the IANA Time Zone Database.
- 1. Return SortStringListByCodeUnit(_timeZones_).
-
+
+
+ AvailableTimeZones (
+ ): a List of Strings
+
+
+
+ 1. Let _timeZones_ be a List containing the String value of each Zone or Link name in the IANA Time Zone Database that is supported by the implementation.
+ 1. Assert: _timeZones_ contains *"UTC"*.
+ 1. Assert: _timeZones_ does not contain any element that does not identify a Zone or Link name in the IANA Time Zone Database.
+ 1. Return SortStringListByCodeUnit(_timeZones_).
+
- This definition supersedes the definition provided in .
-
-
+ This definition supersedes the definition provided in [removed].
+
+
Abstract Operations
@@ -244,13 +320,14 @@ InitializeDateTimeFormat ( _dateTimeFormat_, _locales_, _options_ [ , _
1. If _toLocaleStringTimeZone_ is present, then
1. Set _timeZone_ to _toLocaleStringTimeZone_.
1. Else,
- 1. Set _timeZone_ to DefaultTimeZone().
+ 1. Set _timeZone_ to SystemTimeZoneIdentifier().
1. Else,
1. If _toLocaleStringTimeZone_ is present, throw a *TypeError* exception.
1. Set _timeZone_ to ? ToString(_timeZone_).
- 1. If the result of IsValidTimeZoneName(_timeZone_)IsAvailableTimeZoneName(_timeZone_) is *false*, then
+ 1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_timeZone_).
+ 1. If the result of IsValidTimeZoneName(_timeZone_) is *false*_timeZoneIdentifierRecord_ is ~empty~, then
1. Throw a *RangeError* exception.
- 1. Set _timeZone_ to CanonicalizeTimeZoneName(_timeZone_).
+ 1. Set _timeZone_ to CanonicalizeTimeZoneName(_timeZone_)_timeZoneIdentifierRecord_.[[PrimaryIdentifier]].
1. Set _dateTimeFormat_.[[TimeZone]] to _timeZone_.
1. Let _formatOptions_ be a new Record.
1. Set _formatOptions_.[[hourCycle]] to _hc_.
@@ -1275,7 +1352,8 @@ Intl.DateTimeFormat.prototype.resolvedOptions ( )
- In this version of the ECMAScript 2023 Internationalization API, the *"timeZone"* property will be the name of the default time zone if no *"timeZone"* property was provided in the options object provided to the Intl.DateTimeFormat constructor. The first edition left the *"timeZone"* property *undefined* in this case.
+ In this version of the ECMAScript Internationalization API, the *"timeZone"* property will be the name of the system time zone identifier if no *"timeZone"* property was provided in the options object provided to the Intl.DateTimeFormat constructor.
+ The first edition left the *"timeZone"* property *undefined* in this case.
@@ -2465,8 +2543,9 @@ Temporal.ZonedDateTime.prototype.toLocaleString ( [ _locales_ [ , _options_
1. Let _dateTimeFormat_ be ! OrdinaryCreateFromConstructor(%DateTimeFormat%, %DateTimeFormat.prototype%, « [[InitializedDateTimeFormat]], [[Locale]], [[Calendar]], [[NumberingSystem]], [[TimeZone]], [[Weekday]], [[Era]], [[Year]], [[Month]], [[Day]], [[DayPeriod]], [[Hour]], [[Minute]], [[Second]], [[FractionalSecondDigits]], [[TimeZoneName]], [[HourCycle]], [[Pattern]], [[BoundFormat]] »).
1. Let _timeZone_ be ? ToTemporalTimeZoneIdentifier(_zonedDateTime_.[[TimeZone]]).
1. If IsTimeZoneOffsetString(_timeZone_) is *true*, throw a *RangeError* exception.
- 1. If IsAvailableTimeZoneName(_timeZone_) is *false*, throw a *RangeError* exception.
- 1. Set _timeZone_ to CanonicalizeTimeZoneName(_timeZone_).
+ 1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_timeZone_).
+ 1. If _timeZoneIdentifierRecord_ is ~empty~, throw a *RangeError* exception.
+ 1. Set _timeZone_ to _timeZoneIdentifierRecord_.[[PrimaryIdentifier]].
1. Perform ? InitializeDateTimeFormat(_dateTimeFormat_, _locales_, _options_, _timeZone_).
1. Let _calendar_ be ? ToTemporalCalendarIdentifier(_zonedDateTime_.[[Calendar]]).
1. If _calendar_ is not *"iso8601"* and not equal to _dateTimeFormat_.[[Calendar]], then
diff --git a/spec/mainadditions.html b/spec/mainadditions.html
index 2997428e6f..dc71ca178f 100644
--- a/spec/mainadditions.html
+++ b/spec/mainadditions.html
@@ -8,6 +8,7 @@ Amendments to the ECMAScript® 2023 Language Specification
This section lists amendments which must be made to ECMA-262, the ECMAScript® 2023 Language Specification, other than the addition of the new sections specifying the Temporal object and everything related to it.
Text to be added is marked like this, and text to be deleted is marked like this.
+ Blocks of unmodified text between modified sections are marked by [...].
@@ -16,22 +17,17 @@
- This section intends to move the definitions of ASCII-uppercase, ASCII-lowercase, and ASCII-case-insensitive match from ECMA-402 into ECMA-262, after the definition of the ASCII word characters, and generalizes the former two definitions to cover sequences of code points.
+ This section intends to move the definitions of ASCII-uppercase, ASCII-lowercase, and ASCII-case-insensitive match from ECMA-402 into ECMA-262, after the definition of the ASCII word characters.
+ The definitions include minor editorial changes from ECMA-402 to address feedback from the ECMA-262 editors.
[...]
-
- The ASCII-uppercase of a String value _S_ is the String value derived from _S_ by replacing each occurrence of an ASCII lowercase letter code unit (0x0061 through 0x007A, inclusive) with the corresponding ASCII uppercase letter code unit (0x0041 through 0x005A, inclusive) while preserving all other code units. The ASCII-uppercase of a sequence of Unicode code points _A_ is the sequence of code points derived from _A_ by replacing each occurrence of an ASCII lowercase letter code point (U+0061 through U+007A, inclusive) with the corresponding ASCII uppercase letter code point (U+0041 through U+005A, inclusive) while preserving all other code points.
-
-
- The ASCII-lowercase of a String value _S_ is the String value derived from _S_ by replacing each occurrence of an ASCII uppercase letter code unit (0x0041 through 0x005A, inclusive) with the corresponding ASCII lowercase letter code unit (0x0061 through 0x007A, inclusive) while preserving all other code units. The ASCII-lowercase of a sequence of Unicode code points _A_ is the sequence of code points derived from _A_ by replacing each occurrence of an ASCII uppercase letter code point (U+0041 through U+005A, inclusive) with the corresponding ASCII lowercase letter code point (U+0061 through U+007A, inclusive) while preserving all other code points.
-
-
- A String value _A_ is an ASCII-case-insensitive match for String value _B_ if the ASCII-uppercase of _A_ is exactly the same sequence of code units as the ASCII-uppercase of _B_. A sequence of Unicode code points _A_ is an ASCII-case-insensitive match for _B_ if _B_ is an ASCII-case-insensitive match for ! CodePointsToString(_A_).
-
+ The ASCII-uppercase of a String _S_ is the String derived from _S_ by replacing each occurrence of an ASCII lowercase letter code unit (0x0061 through 0x007A, inclusive) with the corresponding ASCII uppercase letter code unit (0x0041 through 0x005A, inclusive) while preserving all other code units.
+ The ASCII-lowercase of a String _S_ is the String derived from _S_ by replacing each occurrence of an ASCII uppercase letter code unit (0x0041 through 0x005A, inclusive) with the corresponding ASCII lowercase letter code unit (0x0061 through 0x007A, inclusive) while preserving all other code units.
+ A String _A_ is an ASCII-case-insensitive match for a String _B_ if the ASCII-lowercase of _A_ is the ASCII-lowercase of _B_.
@@ -212,6 +208,71 @@
+
+ Overview of Date Objects and Definitions of Abstract Operations
+
+ [...]
+
+
+ Time Zone Identifiers
+
+
+ Time zones in ECMAScript are represented by time zone identifiers, which are Strings composed entirely of code units in the inclusive interval from 0x0000 to 0x007F0x0021 to 0x007E.
+ Time zones supported by an ECMAScript implementation may be available named time zones, represented by the [[Identifier]] field of the Time Zone Identifier Records returned by AvailableNamedTimeZoneIdentifiers, or offset time zones, represented by Strings for which IsTimeZoneOffsetString returns *true*.
+ Time zone identifiers are compared using ASCII-case-insensitive comparisons.
+
+
+ A primary time zone identifier is the preferred identifier for an available named time zone.
+ A non-primary time zone identifier is an identifier for an available named time zone that is not a primary time zone identifier.
+ An available named time zone identifier is either a primary time zone identifier or a non-primary time zone identifier.
+ Each available named time zone identifier is associated with exactly one available named time zone.
+ Each available named time zone is associated with exactly one primary time zone identifier and zero or more non-primary time zone identifiers.
+
+
+ ECMAScript implementations must support an available named time zone with the identifier *"UTC"*, which must be the primary time zone identifier for the UTC time zone.
+ In addition, implementations may support any number of other available named time zones.
+
+
+ Implementations that follow the requirements for time zones as described in the ECMA-402 Internationalization API specification are called time zone aware.
+ Time zone aware implementations must support available named time zones corresponding to the Zone and Link names of the IANA Time Zone Database, and only such names.
+ In time zone aware implementations, a primary time zone identifier is a Zone name, and a non-primary time zone identifier is a Link name, respectively, in the IANA Time Zone Database except as specifically overridden by AvailableNamedTimeZoneIdentifiers as specified in the ECMA-402 specification.
+ Implementations that do not support the entire IANA Time Zone Database are still recommended to use IANA Time Zone Database names as identifiers to represent time zones.
+
+
+
+
+ AvailableNamedTimeZoneIdentifiers ( ): a List of Time Zone Identifier Records
+
+
+ Time zone aware implementations, including all implementations that implement the ECMA-402 Internationalization API, must implement the AvailableNamedTimeZoneIdentifiers abstract operation as specified in the ECMA-402 specification.
+ For implementations that are not time zone aware, AvailableNamedTimeZoneIdentifiers performs the following steps when called:
+
+
+ 1. If the implementation does not include local political rules for any time zones, then
+ 1. Return « the Time Zone Identifier Record { [[Identifier]]: *"UTC"*, [[PrimaryIdentifier]]: *"UTC"* } ».
+ 1. Let _identifiers_ be the List of unique available named time zone identifiers.
+ 1. Sort _identifiers_ into the same order as if an Array of the same values had been sorted using %Array.prototype.sort% with *undefined* as the argument.
+ 1. Set _identifiers_ to SortStringListByCodeUnit(_identifiers_).
+ 1. Let _result_ be a new empty List.
+ 1. For each element _identifier_ of _identifiers_, do
+ 1. Let _primary_ be _identifier_.
+ 1. If _identifier_ is a non-primary time zone identifier in this implementation and _identifier_ is not *"UTC"*, then
+ 1. Set _primary_ to the primary time zone identifier associated with _identifier_.
+ 1. NOTE: An implementation may need to resolve _identifier_ iteratively to obtain the primary time zone identifier.
+ 1. Let _record_ be the Time Zone Identifier Record { [[Identifier]]: _identifier_, [[PrimaryIdentifier]]: _primary_ }.
+ 1. Append _record_ to _result_.
+ 1. Assert: _result_ contains a Time Zone Identifier Record _r_ such that _r_.[[Identifier]] is *"UTC"* and _r_.[[PrimaryIdentifier]] is *"UTC"*.
+ 1. Return _result_.
+
+
+
+
The Date Constructor
diff --git a/spec/temporal.html b/spec/temporal.html
index 3b2bc25a68..454a9a47b3 100644
--- a/spec/temporal.html
+++ b/spec/temporal.html
@@ -97,7 +97,7 @@ Temporal.Now.timeZoneId ( )
This function performs the following steps when called:
- 1. Return DefaultTimeZone().
+ 1. Return SystemTimeZoneIdentifier().
@@ -250,7 +250,7 @@ SystemInstant ( )
SystemDateTime ( _temporalTimeZoneLike_, _calendarLike_ )
1. If _temporalTimeZoneLike_ is *undefined*, then
- 1. Let _timeZone_ be DefaultTimeZone().
+ 1. Let _timeZone_ be SystemTimeZoneIdentifier().
1. Else,
1. Let _timeZone_ be ? ToTemporalTimeZoneSlotValue(_temporalTimeZoneLike_).
1. Let _calendar_ be ? ToTemporalCalendarSlotValue(_calendarLike_).
@@ -263,7 +263,7 @@ SystemDateTime ( _temporalTimeZoneLike_, _calendarLike_ )
SystemZonedDateTime ( _temporalTimeZoneLike_, _calendarLike_ )
1. If _temporalTimeZoneLike_ is *undefined*, then
- 1. Let _timeZone_ be DefaultTimeZone().
+ 1. Let _timeZone_ be SystemTimeZoneIdentifier().
1. Else,
1. Let _timeZone_ be ? ToTemporalTimeZoneSlotValue(_temporalTimeZoneLike_).
1. Let _calendar_ be ? ToTemporalCalendarSlotValue(_calendarLike_).
diff --git a/spec/timezone.html b/spec/timezone.html
index f02fbfd02b..5ad6a4e1a3 100644
--- a/spec/timezone.html
+++ b/spec/timezone.html
@@ -5,99 +5,6 @@
Temporal.TimeZone Objects
A Temporal.TimeZone object is an Object referencing a time zone.
-
- Time Zone Names
-
-
- An ECMAScript implementation must support a number of built-in time zones.
- At a minimum, implementations must support a built-in time zone named *"UTC"*.
- If the return value _tz_ of DefaultTimeZone is different from *"UTC"*, and IsTimeZoneOffsetString(_tz_) is *false*, then implementations must support that time zone as a built-in time zone as well.
- In addition, implementations may support any number of other built-in time zones.
-
-
- Built-in time zones may be named time zones, represented by Strings for which IsAvailableTimeZoneName returns *true*.
- They may also be offset time zones, represented by Strings for which IsTimeZoneOffsetString returns *true*.
-
-
- The `Temporal.TimeZone` constructor, when called with the name of a built-in time zone as the argument, will return a valid `Temporal.TimeZone` object.
- When called with any other string, it will throw a *RangeError* exception.
-
-
- An ECMAScript implementation that includes the ECMA-402 Internationalization API must define built-in named time zones in correspondence with the Zone and Link names of the IANA Time Zone Database (and only such names) and use best available current and historical information about their offsets from UTC and their daylight saving time rules in calculations as specified in the ECMA-402 specification.
- Other implementations are encouraged to do the same.
-
-
-
-
- IsAvailableTimeZoneName (
- _timeZone_: a String,
- ): a Boolean
-
-
-
- Once IsAvailableTimeZoneName(_timeZone_) has returned *true*, for the lifetime of the surrounding agent, IsAvailableTimeZoneName(_variant_) must return *true* if _variant_ is an ASCII-case-insensitive match for either _timeZone_ or CanonicalizeTimeZoneName(_timeZone_).
-
-
-
- 1. Let _timeZones_ be AvailableTimeZones().
- 1. For each String _candidate_ in _timeZones_, do
- 1. If _timeZone_ is an ASCII-case-insensitive match for _candidate_, return *true*.
- 1. Return *false*.
-
-
-
-
-
- CanonicalizeTimeZoneName (
- _timeZone_: a String that is a valid time zone name as verified by IsAvailableTimeZoneName,
- )
-
-
-
-
- An ECMAScript implementation that includes the ECMA-402 Internationalization API must implement the CanonicalizeTimeZoneName abstract operation as specified in the ECMA-402 specification.
-
-
- The minimum implementation of CanonicalizeTimeZoneName for ECMAScript implementations that do not include local political rules for any time zones performs the following steps when called:
-
-
-
- 1. Assert: _timeZone_ is an ASCII-case-insensitive match for *"UTC"*.
- 1. Return *"UTC"*.
-
-
-
-
-
- AvailableTimeZones (
- ): a List of Strings
-
-
-
-
- An ECMAScript implementation that includes the ECMA-402 Internationalization API must implement the AvailableTimeZones abstract operation as specified in the ECMA-402 specification.
- If an ECMAScript implementation does not include the ECMA-402 API the following specification of the AvailableTimeZones abstract operation is used.
-
-
- 1. Let _timeZones_ be the List of String values representing time zones supported by the implementation.
- 1. Assert: _timeZones_ contains *"UTC"*.
- 1. Return SortStringListByCodeUnit(_timeZones_).
-
-
- For example, an ECMAScript implementation that does not include local political rules for any time zone could return a List with the single String *"UTC"* here.
-
-
-
-
The Temporal.TimeZone Constructor
The Temporal.TimeZone constructor:
@@ -126,9 +33,9 @@ Temporal.TimeZone ( _identifier_ )
1. Throw a *TypeError* exception.
1. Set _identifier_ to ? ToString(_identifier_).
1. If IsTimeZoneOffsetString(_identifier_) is *false*, then
- 1. If IsAvailableTimeZoneName(_identifier_) is *false*, then
- 1. Throw a *RangeError* exception.
- 1. Set _identifier_ to ! CanonicalizeTimeZoneName(_identifier_).
+ 1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_identifier_).
+ 1. If _timeZoneIdentifierRecord_ is ~empty~, throw a *RangeError* exception.
+ 1. Set _identifier_ to _timeZoneIdentifierRecord_.[[PrimaryIdentifier]].
1. Return ? CreateTemporalTimeZone(_identifier_, NewTarget).
@@ -385,6 +292,19 @@ Properties of Temporal.TimeZone Instances
Abstract operations
+
+ In ECMA-262, many time-zone-related sections and abstract operations are contained in the Date Objects section of the specification.
+ Now that ECMAScript has a built-in %Temporal.TimeZone% object, it may be appropriate to move those sections here, for example:
+
+ - Time Zone Identifiers
+ - AvailableNamedTimeZoneIdentifiers
+ - SystemTimeZoneIdentifier
+ - IsTimeZoneOffsetString
+ - GetNamedTimeZoneEpochNanoseconds
+ - GetNamedTimeZoneOffsetNanoseconds
+
+
+
CreateTemporalTimeZone (
@@ -403,7 +323,7 @@
1. Set _object_.[[Identifier]] to ~empty~.
1. Set _object_.[[OffsetNanoseconds]] to ParseTimeZoneOffsetString(_identifier_).
1. Else,
- 1. Assert: ! CanonicalizeTimeZoneName(_identifier_) is _identifier_.
+ 1. Assert: GetAvailableNamedTimeZoneIdentifier(_identifier_).[[PrimaryIdentifier]] is _identifier_.
1. Set _object_.[[Identifier]] to _identifier_.
1. Set _object_.[[OffsetNanoseconds]] to ~empty~.
1. Return _object_.
@@ -421,7 +341,32 @@
Implementations are free to store either or both representations.
-
+
+
+
+
+ GetAvailableNamedTimeZoneIdentifier (
+ _timeZoneIdentifier_: a String,
+ ): either a Time Zone Identifier Record or ~empty~
+
+
+
+ 1. For each element _record_ of AvailableNamedTimeZoneIdentifiers(), do
+ 1. If _record_.[[Identifier]] is an ASCII-case-insensitive match for _timeZoneIdentifier_, return _record_.
+ 1. Return ~empty~.
+
+
+ For any _timeZoneIdentifier_, or any value that is an ASCII-case-insensitive match for it, the resulting Time Zone Identifier Record must contain the same field values for the lifetime of the surrounding agent.
+ Furthermore, time zone identifiers must not dynamically change from primary to non-primary during the lifetime of the surrounding agent, meaning that if _timeZoneIdentifier_ is an ASCII-case-insensitive match for the [[PrimaryIdentifier]] field of the result of a previous call to GetAvailableNamedTimeZoneIdentifier, then GetAvailableNamedTimeZoneIdentifier(_timeZoneIdentifier_) must return a record where [[Identifier]] is [[PrimaryIdentifier]].
+ Due to the complexity of supporting these requirements, it is recommended that the result of AvailableNamedTimeZoneIdentifiers (and therefore GetAvailableNamedTimeZoneIdentifier) remains the same for the lifetime of the surrounding agent.
+
+
@@ -622,8 +567,9 @@
1. If _parseResult_.[[Name]] is not *undefined*, then
1. Let _name_ be _parseResult_.[[Name]].
1. If IsTimeZoneOffsetString(_name_) is *true*, return CanonicalizeTimeZoneOffsetString(_name_).
- 1. If IsAvailableTimeZoneName(_name_) is *false*, throw a *RangeError* exception.
- 1. Return ! CanonicalizeTimeZoneName(_name_).
+ 1. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_name_).
+ 1. If _timeZoneIdentifierRecord_ is ~empty~, throw a *RangeError* exception.
+ 1. Return _timeZoneIdentifierRecord_.[[PrimaryIdentifier]].
1. If _parseResult_.[[Z]] is *true*, return *"UTC"*.
1. Return CanonicalizeTimeZoneOffsetString(_parseResult_.[[OffsetString]]).
@@ -641,7 +587,7 @@
1. If _timeZoneSlotValue_ is a String, then
- 1. Assert: Either IsTimeZoneOffsetString(_timeZoneSlotValue_) or IsAvailableTimeZoneName(_timeZoneSlotValue_) is *true*.
+ 1. Assert: Either IsTimeZoneOffsetString(_timeZoneSlotValue_) is *true*, or GetAvailableNamedTimeZoneIdentifier(_timeZoneSlotValue_) is not ~empty~.
1. Return _timeZoneSlotValue_.
1. Let _identifier_ be ? Get(_timeZoneSlotValue_, *"id"*).
1. If _identifier_ is not a String, throw a *TypeError* exception.
From 192acba66059c776c9a286482249b5a9ec9bb2f2 Mon Sep 17 00:00:00 2001
From: Justin Grant
Date: Mon, 12 Jun 2023 21:28:30 -0700
Subject: [PATCH 2/2] Polyfill: Refactor time zone identifier handling
---
polyfill/lib/ecmascript.mjs | 95 +-
polyfill/lib/intl.mjs | 38 +-
polyfill/lib/timezone.mjs | 5 +-
polyfill/lib/zoneddatetime.mjs | 12 +-
polyfill/test/cldr-timezone.json | 1927 ++++++++++++++++++++++++++++++
polyfill/test/ecmascript.mjs | 56 +
6 files changed, 2108 insertions(+), 25 deletions(-)
create mode 100644 polyfill/test/cldr-timezone.json
diff --git a/polyfill/lib/ecmascript.mjs b/polyfill/lib/ecmascript.mjs
index 4777893822..25d54d96f2 100644
--- a/polyfill/lib/ecmascript.mjs
+++ b/polyfill/lib/ecmascript.mjs
@@ -4,6 +4,7 @@ const ArrayIncludes = Array.prototype.includes;
const ArrayPrototypePush = Array.prototype.push;
const ArrayPrototypeSort = Array.prototype.sort;
const IntlDateTimeFormat = globalThis.Intl.DateTimeFormat;
+const IntlSupportedValuesOf = globalThis.Intl.supportedValuesOf;
const MathAbs = Math.abs;
const MathFloor = Math.floor;
const MathMax = Math.max;
@@ -368,7 +369,9 @@ export function ParseTemporalTimeZone(stringIdent) {
const { tzName, offset, z } = ParseTemporalTimeZoneString(stringIdent);
if (tzName) {
if (IsTimeZoneOffsetString(tzName)) return CanonicalizeTimeZoneOffsetString(tzName);
- return GetCanonicalTimeZoneIdentifier(tzName);
+ const record = GetAvailableNamedTimeZoneIdentifier(tzName);
+ if (!record) throw new RangeError(`Unrecognized time zone ${tzName}`);
+ return record.primaryIdentifier;
}
if (z) return 'UTC';
// if !tzName && !z then offset must be present
@@ -2589,15 +2592,87 @@ export function ParseTimeZoneOffsetString(string) {
return sign * (((hours * 60 + minutes) * 60 + seconds) * 1e9 + nanoseconds);
}
-// In the spec, GetCanonicalTimeZoneIdentifier is infallible and is always
-// preceded by a call to IsAvailableTimeZoneName. However in the polyfill,
-// we don't (yet) have a way to check if a time zone ID is valid without
-// also canonicalizing it. So we combine both operations into one function,
-// which will return the canonical ID if the ID is valid, and will throw
-// if it's not.
-export function GetCanonicalTimeZoneIdentifier(timeZoneIdentifier) {
- const formatter = getIntlDateTimeFormatEnUsForTimeZone(timeZoneIdentifier);
- return formatter.resolvedOptions().timeZone;
+let canonicalTimeZoneIdsCache = undefined;
+
+export function GetAvailableNamedTimeZoneIdentifier(identifier) {
+ // The most common case is when the identifier is a canonical time zone ID.
+ // Fast-path that case by caching all canonical IDs. For old ECMAScript
+ // implementations lacking this API, set the cache to `null` to avoid retries.
+ if (canonicalTimeZoneIdsCache === undefined) {
+ const canonicalTimeZoneIds = IntlSupportedValuesOf?.('timeZone');
+ canonicalTimeZoneIdsCache = canonicalTimeZoneIds
+ ? new Map(canonicalTimeZoneIds.map((id) => [ASCIILowercase(id), id]))
+ : null;
+ }
+
+ const lower = ASCIILowercase(identifier);
+ let primaryIdentifier = canonicalTimeZoneIdsCache?.get(lower);
+ if (primaryIdentifier) return { identifier: primaryIdentifier, primaryIdentifier };
+
+ // It's not already a primary identifier, so get its primary identifier (or
+ // return if it's not an available named time zone ID).
+ try {
+ const formatter = getIntlDateTimeFormatEnUsForTimeZone(identifier);
+ primaryIdentifier = formatter.resolvedOptions().timeZone;
+ } catch {
+ return undefined;
+ }
+
+ // The identifier is an alias (a deprecated identifier that's a synonym for a
+ // primary identifier), so we need to case-normalize the identifier to match
+ // the IANA TZDB, e.g. america/new_york => America/New_York. There's no
+ // built-in way to do this using Intl.DateTimeFormat, but the we can normalize
+ // almost all aliases (modulo a few special cases) using the TZDB's basic
+ // capitalization pattern:
+ // 1. capitalize the first letter of the identifier
+ // 2. capitalize the letter after every slash, dash, or underscore delimiter
+ const standardCase = [...lower]
+ .map((c, i) => (i === 0 || '/-_'.includes(lower[i - 1]) ? c.toUpperCase() : c))
+ .join('');
+ const segments = standardCase.split('/');
+
+ if (segments.length === 1) {
+ // If a single-segment legacy ID is 2-3 chars or contains a number or dash, then
+ // (except for the "GB-Eire" special case) the case-normalized form is uppercase.
+ // These are: GMT+0, GMT-0, GB, NZ, PRC, ROC, ROK, UCT, GMT, GMT0, CET, CST6CDT,
+ // EET, EST, HST, MET, MST, MST7MDT, PST8PDT, WET, NZ-CHAT, and W-SU.
+ // Otherwise it's standard form: first letter capitalized, e.g. Iran, Egypt, Hongkong
+ if (lower === 'gb-eire') return { identifier: 'GB-Eire', primaryIdentifier };
+ return {
+ identifier: lower.length <= 3 || /[-0-9]/.test(lower) ? lower.toUpperCase() : segments[0],
+ primaryIdentifier
+ };
+ }
+
+ // All Etc zone names are uppercase except three exceptions.
+ if (segments[0] === 'Etc') {
+ const etcName = ['Zulu', 'Greenwich', 'Universal'].includes(segments[1]) ? segments[1] : segments[1].toUpperCase();
+ return { identifier: `Etc/${etcName}`, primaryIdentifier };
+ }
+
+ // Legacy US identifiers like US/Alaska or US/Indiana-Starke are 2 segments and use standard form.
+ if (segments[0] === 'Us') return { identifier: `US/${segments[1]}`, primaryIdentifier };
+
+ // For multi-segment IDs, there's a few special cases in the second/third segments
+ const specialCases = {
+ Act: 'ACT',
+ Lhi: 'LHI',
+ Nsw: 'NSW',
+ Dar_Es_Salaam: 'Dar_es_Salaam',
+ Port_Of_Spain: 'Port_of_Spain',
+ Isle_Of_Man: 'Isle_of_Man',
+ Comodrivadavia: 'ComodRivadavia',
+ Knox_In: 'Knox_IN',
+ Dumontdurville: 'DumontDUrville',
+ Mcmurdo: 'McMurdo',
+ Denoronha: 'DeNoronha',
+ Easterisland: 'EasterIsland',
+ Bajanorte: 'BajaNorte',
+ Bajasur: 'BajaSur'
+ };
+ segments[1] = specialCases[segments[1]] ?? segments[1];
+ if (segments.length > 2) segments[2] = specialCases[segments[2]] ?? segments[2];
+ return { identifier: segments.join('/'), primaryIdentifier };
}
export function GetNamedTimeZoneOffsetNanoseconds(id, epochNanoseconds) {
diff --git a/polyfill/lib/intl.mjs b/polyfill/lib/intl.mjs
index 3312d581ee..e3c6a40369 100644
--- a/polyfill/lib/intl.mjs
+++ b/polyfill/lib/intl.mjs
@@ -21,7 +21,8 @@ const TIME = Symbol('time');
const DATETIME = Symbol('datetime');
const INST = Symbol('instant');
const ORIGINAL = Symbol('original');
-const TZ_RESOLVED = Symbol('timezone');
+const TZ_CANONICAL = Symbol('timezone-canonical');
+const TZ_ORIGINAL = Symbol('timezone-original');
const CAL_ID = Symbol('calendar-id');
const LOCALE = Symbol('locale');
const OPTIONS = Symbol('options');
@@ -81,7 +82,7 @@ export function DateTimeFormat(locale = undefined, options = undefined) {
this[LOCALE] = ro.locale;
this[ORIGINAL] = original;
- this[TZ_RESOLVED] = ro.timeZone;
+ this[TZ_CANONICAL] = ro.timeZone;
this[CAL_ID] = ro.calendar;
this[DATE] = dateAmend;
this[YM] = yearMonthAmend;
@@ -89,6 +90,25 @@ export function DateTimeFormat(locale = undefined, options = undefined) {
this[TIME] = timeAmend;
this[DATETIME] = datetimeAmend;
this[INST] = instantAmend;
+
+ // Save the original time zone, for a few reasons:
+ // - Clearer error messages
+ // - More clearly follows the spec for InitializeDateTimeFormat
+ // - Because it follows the spec more closely, will make it easier to integrate
+ // support of offset strings and other potential changes like proposal-canonical-tz.
+ const timeZoneOption = hasOptions ? options.timeZone : undefined;
+ if (timeZoneOption === undefined) {
+ this[TZ_ORIGINAL] = ro.timeZone;
+ } else {
+ const id = ES.ToString(timeZoneOption);
+ if (ES.IsTimeZoneOffsetString(id)) {
+ // Note: https://github.com/tc39/ecma402/issues/683 will remove this
+ throw new RangeError('Intl.DateTimeFormat does not currently support offset time zones');
+ }
+ const record = ES.GetAvailableNamedTimeZoneIdentifier(id);
+ if (!record) throw new RangeError(`Intl.DateTimeFormat formats built-in time zones, not ${id}`);
+ this[TZ_ORIGINAL] = record.identifier;
+ }
}
DateTimeFormat.supportedLocalesOf = function (...args) {
@@ -118,7 +138,9 @@ Object.defineProperty(DateTimeFormat, 'prototype', {
});
function resolvedOptions() {
- return this[ORIGINAL].resolvedOptions();
+ const resolved = this[ORIGINAL].resolvedOptions();
+ resolved.timeZone = this[TZ_CANONICAL];
+ return resolved;
}
function format(datetime, ...rest) {
@@ -335,7 +357,7 @@ function extractOverrides(temporalObj, main) {
const nanosecond = GetSlot(temporalObj, ISO_NANOSECOND);
const datetime = new DateTime(1970, 1, 1, hour, minute, second, millisecond, microsecond, nanosecond, main[CAL_ID]);
return {
- instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
+ instant: ES.GetInstantFor(main[TZ_CANONICAL], datetime, 'compatible'),
formatter: getPropLazy(main, TIME)
};
}
@@ -352,7 +374,7 @@ function extractOverrides(temporalObj, main) {
}
const datetime = new DateTime(isoYear, isoMonth, referenceISODay, 12, 0, 0, 0, 0, 0, calendar);
return {
- instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
+ instant: ES.GetInstantFor(main[TZ_CANONICAL], datetime, 'compatible'),
formatter: getPropLazy(main, YM)
};
}
@@ -369,7 +391,7 @@ function extractOverrides(temporalObj, main) {
}
const datetime = new DateTime(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0, calendar);
return {
- instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
+ instant: ES.GetInstantFor(main[TZ_CANONICAL], datetime, 'compatible'),
formatter: getPropLazy(main, MD)
};
}
@@ -384,7 +406,7 @@ function extractOverrides(temporalObj, main) {
}
const datetime = new DateTime(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0, main[CAL_ID]);
return {
- instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
+ instant: ES.GetInstantFor(main[TZ_CANONICAL], datetime, 'compatible'),
formatter: getPropLazy(main, DATE)
};
}
@@ -421,7 +443,7 @@ function extractOverrides(temporalObj, main) {
);
}
return {
- instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
+ instant: ES.GetInstantFor(main[TZ_CANONICAL], datetime, 'compatible'),
formatter: getPropLazy(main, DATETIME)
};
}
diff --git a/polyfill/lib/timezone.mjs b/polyfill/lib/timezone.mjs
index 97071c83fd..9796e980f6 100644
--- a/polyfill/lib/timezone.mjs
+++ b/polyfill/lib/timezone.mjs
@@ -26,12 +26,13 @@ export class TimeZone {
if (arguments.length < 1) {
throw new RangeError('missing argument: identifier is required');
}
-
let stringIdentifier = ES.ToString(identifier);
if (ES.IsTimeZoneOffsetString(stringIdentifier)) {
stringIdentifier = ES.CanonicalizeTimeZoneOffsetString(stringIdentifier);
} else {
- stringIdentifier = ES.GetCanonicalTimeZoneIdentifier(stringIdentifier);
+ const record = ES.GetAvailableNamedTimeZoneIdentifier(stringIdentifier);
+ if (!record) throw new RangeError(`Invalid time zone identifier: ${stringIdentifier}`);
+ stringIdentifier = record.primaryIdentifier;
}
CreateSlots(this);
SetSlot(this, TIMEZONE_ID, stringIdentifier);
diff --git a/polyfill/lib/zoneddatetime.mjs b/polyfill/lib/zoneddatetime.mjs
index b386343aef..1a593c7f15 100644
--- a/polyfill/lib/zoneddatetime.mjs
+++ b/polyfill/lib/zoneddatetime.mjs
@@ -471,13 +471,15 @@ export class ZonedDateTime {
// The rest of the defaults will be filled in by formatting the Instant
}
- let timeZone = ES.ToTemporalTimeZoneIdentifier(GetSlot(this, TIME_ZONE));
- if (ES.IsTimeZoneOffsetString(timeZone)) {
+ const timeZoneIdentifier = ES.ToTemporalTimeZoneIdentifier(GetSlot(this, TIME_ZONE));
+ if (ES.IsTimeZoneOffsetString(timeZoneIdentifier)) {
// Note: https://github.com/tc39/ecma402/issues/683 will remove this
- throw new RangeError('toLocaleString does not support offset string time zones');
+ throw new RangeError('toLocaleString does not currently support offset time zones');
+ } else {
+ const record = ES.GetAvailableNamedTimeZoneIdentifier(timeZoneIdentifier);
+ if (!record) throw new RangeError(`toLocaleString formats built-in time zones, not ${timeZoneIdentifier}`);
+ optionsCopy.timeZone = record.primaryIdentifier;
}
- timeZone = ES.GetCanonicalTimeZoneIdentifier(timeZone);
- optionsCopy.timeZone = timeZone;
const formatter = new DateTimeFormat(locales, optionsCopy);
diff --git a/polyfill/test/cldr-timezone.json b/polyfill/test/cldr-timezone.json
new file mode 100644
index 0000000000..80c80a785b
--- /dev/null
+++ b/polyfill/test/cldr-timezone.json
@@ -0,0 +1,1927 @@
+{
+ "version": {
+ "_number": "$Revision$"
+ },
+ "keyword": {
+ "u": {
+ "tz": {
+ "_description": "Time zone key",
+ "_alias": "timezone",
+ "adalv": {
+ "_description": "Andorra",
+ "_alias": "Europe/Andorra"
+ },
+ "aedxb": {
+ "_description": "Dubai, United Arab Emirates",
+ "_alias": "Asia/Dubai"
+ },
+ "afkbl": {
+ "_description": "Kabul, Afghanistan",
+ "_alias": "Asia/Kabul"
+ },
+ "aganu": {
+ "_description": "Antigua",
+ "_alias": "America/Antigua"
+ },
+ "aiaxa": {
+ "_description": "Anguilla",
+ "_alias": "America/Anguilla"
+ },
+ "altia": {
+ "_description": "Tirane, Albania",
+ "_alias": "Europe/Tirane"
+ },
+ "amevn": {
+ "_description": "Yerevan, Armenia",
+ "_alias": "Asia/Yerevan"
+ },
+ "ancur": {
+ "_description": "Curaçao",
+ "_alias": "America/Curacao"
+ },
+ "aolad": {
+ "_description": "Luanda, Angola",
+ "_alias": "Africa/Luanda"
+ },
+ "aqams": {
+ "_deprecated": true,
+ "_description": "Amundsen-Scott Station, South Pole",
+ "_preferred": "nzakl"
+ },
+ "aqcas": {
+ "_description": "Casey Station, Bailey Peninsula",
+ "_alias": "Antarctica/Casey"
+ },
+ "aqdav": {
+ "_description": "Davis Station, Vestfold Hills",
+ "_alias": "Antarctica/Davis"
+ },
+ "aqddu": {
+ "_description": "Dumont d'Urville Station, Terre Adélie",
+ "_alias": "Antarctica/DumontDUrville"
+ },
+ "aqmaw": {
+ "_description": "Mawson Station, Holme Bay",
+ "_alias": "Antarctica/Mawson"
+ },
+ "aqmcm": {
+ "_description": "McMurdo Station, Ross Island",
+ "_alias": "Antarctica/McMurdo"
+ },
+ "aqplm": {
+ "_description": "Palmer Station, Anvers Island",
+ "_alias": "Antarctica/Palmer"
+ },
+ "aqrot": {
+ "_description": "Rothera Station, Adelaide Island",
+ "_alias": "Antarctica/Rothera"
+ },
+ "aqsyw": {
+ "_description": "Syowa Station, East Ongul Island",
+ "_alias": "Antarctica/Syowa"
+ },
+ "aqtrl": {
+ "_description": "Troll Station, Queen Maud Land",
+ "_alias": "Antarctica/Troll",
+ "_since": "26"
+ },
+ "aqvos": {
+ "_description": "Vostok Station, Lake Vostok",
+ "_alias": "Antarctica/Vostok"
+ },
+ "arbue": {
+ "_description": "Buenos Aires, Argentina",
+ "_alias": "America/Buenos_Aires America/Argentina/Buenos_Aires"
+ },
+ "arcor": {
+ "_description": "Córdoba, Argentina",
+ "_alias": "America/Cordoba America/Argentina/Cordoba America/Rosario"
+ },
+ "arctc": {
+ "_description": "Catamarca, Argentina",
+ "_alias": "America/Catamarca America/Argentina/Catamarca America/Argentina/ComodRivadavia"
+ },
+ "arirj": {
+ "_description": "La Rioja, Argentina",
+ "_alias": "America/Argentina/La_Rioja"
+ },
+ "arjuj": {
+ "_description": "Jujuy, Argentina",
+ "_alias": "America/Jujuy America/Argentina/Jujuy"
+ },
+ "arluq": {
+ "_description": "San Luis, Argentina",
+ "_alias": "America/Argentina/San_Luis"
+ },
+ "armdz": {
+ "_description": "Mendoza, Argentina",
+ "_alias": "America/Mendoza America/Argentina/Mendoza"
+ },
+ "arrgl": {
+ "_description": "Río Gallegos, Argentina",
+ "_alias": "America/Argentina/Rio_Gallegos"
+ },
+ "arsla": {
+ "_description": "Salta, Argentina",
+ "_alias": "America/Argentina/Salta"
+ },
+ "artuc": {
+ "_description": "Tucumán, Argentina",
+ "_alias": "America/Argentina/Tucuman"
+ },
+ "aruaq": {
+ "_description": "San Juan, Argentina",
+ "_alias": "America/Argentina/San_Juan"
+ },
+ "arush": {
+ "_description": "Ushuaia, Argentina",
+ "_alias": "America/Argentina/Ushuaia"
+ },
+ "asppg": {
+ "_description": "Pago Pago, American Samoa",
+ "_alias": "Pacific/Pago_Pago Pacific/Samoa US/Samoa"
+ },
+ "atvie": {
+ "_description": "Vienna, Austria",
+ "_alias": "Europe/Vienna"
+ },
+ "auadl": {
+ "_description": "Adelaide, Australia",
+ "_alias": "Australia/Adelaide Australia/South"
+ },
+ "aubhq": {
+ "_description": "Broken Hill, Australia",
+ "_alias": "Australia/Broken_Hill Australia/Yancowinna"
+ },
+ "aubne": {
+ "_description": "Brisbane, Australia",
+ "_alias": "Australia/Brisbane Australia/Queensland"
+ },
+ "audrw": {
+ "_description": "Darwin, Australia",
+ "_alias": "Australia/Darwin Australia/North"
+ },
+ "aueuc": {
+ "_description": "Eucla, Australia",
+ "_alias": "Australia/Eucla"
+ },
+ "auhba": {
+ "_description": "Hobart, Australia",
+ "_alias": "Australia/Hobart Australia/Tasmania"
+ },
+ "aukns": {
+ "_description": "Currie, Australia",
+ "_alias": "Australia/Currie"
+ },
+ "auldc": {
+ "_description": "Lindeman Island, Australia",
+ "_alias": "Australia/Lindeman"
+ },
+ "auldh": {
+ "_description": "Lord Howe Island, Australia",
+ "_alias": "Australia/Lord_Howe Australia/LHI"
+ },
+ "aumel": {
+ "_description": "Melbourne, Australia",
+ "_alias": "Australia/Melbourne Australia/Victoria"
+ },
+ "aumqi": {
+ "_description": "Macquarie Island Station, Macquarie Island",
+ "_alias": "Antarctica/Macquarie",
+ "_since": "1.8.1"
+ },
+ "auper": {
+ "_description": "Perth, Australia",
+ "_alias": "Australia/Perth Australia/West"
+ },
+ "ausyd": {
+ "_description": "Sydney, Australia",
+ "_alias": "Australia/Sydney Australia/ACT Australia/Canberra Australia/NSW"
+ },
+ "awaua": {
+ "_description": "Aruba",
+ "_alias": "America/Aruba"
+ },
+ "azbak": {
+ "_description": "Baku, Azerbaijan",
+ "_alias": "Asia/Baku"
+ },
+ "basjj": {
+ "_description": "Sarajevo, Bosnia and Herzegovina",
+ "_alias": "Europe/Sarajevo"
+ },
+ "bbbgi": {
+ "_description": "Barbados",
+ "_alias": "America/Barbados"
+ },
+ "bddac": {
+ "_description": "Dhaka, Bangladesh",
+ "_alias": "Asia/Dhaka Asia/Dacca"
+ },
+ "bebru": {
+ "_description": "Brussels, Belgium",
+ "_alias": "Europe/Brussels"
+ },
+ "bfoua": {
+ "_description": "Ouagadougou, Burkina Faso",
+ "_alias": "Africa/Ouagadougou"
+ },
+ "bgsof": {
+ "_description": "Sofia, Bulgaria",
+ "_alias": "Europe/Sofia"
+ },
+ "bhbah": {
+ "_description": "Bahrain",
+ "_alias": "Asia/Bahrain"
+ },
+ "bibjm": {
+ "_description": "Bujumbura, Burundi",
+ "_alias": "Africa/Bujumbura"
+ },
+ "bjptn": {
+ "_description": "Porto-Novo, Benin",
+ "_alias": "Africa/Porto-Novo"
+ },
+ "bmbda": {
+ "_description": "Bermuda",
+ "_alias": "Atlantic/Bermuda"
+ },
+ "bnbwn": {
+ "_description": "Brunei",
+ "_alias": "Asia/Brunei"
+ },
+ "bolpb": {
+ "_description": "La Paz, Bolivia",
+ "_alias": "America/La_Paz"
+ },
+ "bqkra": {
+ "_description": "Bonaire, Sint Estatius and Saba",
+ "_alias": "America/Kralendijk",
+ "_since": "21"
+ },
+ "braux": {
+ "_description": "Araguaína, Brazil",
+ "_alias": "America/Araguaina"
+ },
+ "brbel": {
+ "_description": "Belém, Brazil",
+ "_alias": "America/Belem"
+ },
+ "brbvb": {
+ "_description": "Boa Vista, Brazil",
+ "_alias": "America/Boa_Vista"
+ },
+ "brcgb": {
+ "_description": "Cuiabá, Brazil",
+ "_alias": "America/Cuiaba"
+ },
+ "brcgr": {
+ "_description": "Campo Grande, Brazil",
+ "_alias": "America/Campo_Grande"
+ },
+ "brern": {
+ "_description": "Eirunepé, Brazil",
+ "_alias": "America/Eirunepe"
+ },
+ "brfen": {
+ "_description": "Fernando de Noronha, Brazil",
+ "_alias": "America/Noronha Brazil/DeNoronha"
+ },
+ "brfor": {
+ "_description": "Fortaleza, Brazil",
+ "_alias": "America/Fortaleza"
+ },
+ "brmao": {
+ "_description": "Manaus, Brazil",
+ "_alias": "America/Manaus Brazil/West"
+ },
+ "brmcz": {
+ "_description": "Maceió, Brazil",
+ "_alias": "America/Maceio"
+ },
+ "brpvh": {
+ "_description": "Porto Velho, Brazil",
+ "_alias": "America/Porto_Velho"
+ },
+ "brrbr": {
+ "_description": "Rio Branco, Brazil",
+ "_alias": "America/Rio_Branco America/Porto_Acre Brazil/Acre"
+ },
+ "brrec": {
+ "_description": "Recife, Brazil",
+ "_alias": "America/Recife"
+ },
+ "brsao": {
+ "_description": "São Paulo, Brazil",
+ "_alias": "America/Sao_Paulo Brazil/East"
+ },
+ "brssa": {
+ "_description": "Bahia, Brazil",
+ "_alias": "America/Bahia"
+ },
+ "brstm": {
+ "_description": "Santarém, Brazil",
+ "_alias": "America/Santarem"
+ },
+ "bsnas": {
+ "_description": "Nassau, Bahamas",
+ "_alias": "America/Nassau"
+ },
+ "btthi": {
+ "_description": "Thimphu, Bhutan",
+ "_alias": "Asia/Thimphu Asia/Thimbu"
+ },
+ "bwgbe": {
+ "_description": "Gaborone, Botswana",
+ "_alias": "Africa/Gaborone"
+ },
+ "bymsq": {
+ "_description": "Minsk, Belarus",
+ "_alias": "Europe/Minsk"
+ },
+ "bzbze": {
+ "_description": "Belize",
+ "_alias": "America/Belize"
+ },
+ "cacfq": {
+ "_description": "Creston, Canada",
+ "_alias": "America/Creston",
+ "_since": "21.0.1"
+ },
+ "caedm": {
+ "_description": "Edmonton, Canada",
+ "_alias": "America/Edmonton Canada/Mountain"
+ },
+ "caffs": {
+ "_description": "Rainy River, Canada",
+ "_alias": "America/Rainy_River"
+ },
+ "cafne": {
+ "_description": "Fort Nelson, Canada",
+ "_alias": "America/Fort_Nelson",
+ "_since": "29"
+ },
+ "caglb": {
+ "_description": "Glace Bay, Canada",
+ "_alias": "America/Glace_Bay"
+ },
+ "cagoo": {
+ "_description": "Goose Bay, Canada",
+ "_alias": "America/Goose_Bay"
+ },
+ "cahal": {
+ "_description": "Halifax, Canada",
+ "_alias": "America/Halifax Canada/Atlantic"
+ },
+ "caiql": {
+ "_description": "Iqaluit, Canada",
+ "_alias": "America/Iqaluit"
+ },
+ "camon": {
+ "_description": "Moncton, Canada",
+ "_alias": "America/Moncton"
+ },
+ "camtr": {
+ "_deprecated": true,
+ "_description": "Montreal, Canada",
+ "_preferred": "cator"
+ },
+ "canpg": {
+ "_description": "Nipigon, Canada",
+ "_alias": "America/Nipigon"
+ },
+ "capnt": {
+ "_description": "Pangnirtung, Canada",
+ "_alias": "America/Pangnirtung"
+ },
+ "careb": {
+ "_description": "Resolute, Canada",
+ "_alias": "America/Resolute"
+ },
+ "careg": {
+ "_description": "Regina, Canada",
+ "_alias": "America/Regina Canada/East-Saskatchewan Canada/Saskatchewan"
+ },
+ "casjf": {
+ "_description": "St. John's, Canada",
+ "_alias": "America/St_Johns Canada/Newfoundland"
+ },
+ "cathu": {
+ "_description": "Thunder Bay, Canada",
+ "_alias": "America/Thunder_Bay"
+ },
+ "cator": {
+ "_description": "Toronto, Canada",
+ "_alias": "America/Toronto America/Montreal Canada/Eastern"
+ },
+ "cavan": {
+ "_description": "Vancouver, Canada",
+ "_alias": "America/Vancouver Canada/Pacific"
+ },
+ "cawnp": {
+ "_description": "Winnipeg, Canada",
+ "_alias": "America/Winnipeg Canada/Central"
+ },
+ "caybx": {
+ "_description": "Blanc-Sablon, Canada",
+ "_alias": "America/Blanc-Sablon"
+ },
+ "caycb": {
+ "_description": "Cambridge Bay, Canada",
+ "_alias": "America/Cambridge_Bay"
+ },
+ "cayda": {
+ "_description": "Dawson, Canada",
+ "_alias": "America/Dawson"
+ },
+ "caydq": {
+ "_description": "Dawson Creek, Canada",
+ "_alias": "America/Dawson_Creek"
+ },
+ "cayek": {
+ "_description": "Rankin Inlet, Canada",
+ "_alias": "America/Rankin_Inlet"
+ },
+ "cayev": {
+ "_description": "Inuvik, Canada",
+ "_alias": "America/Inuvik"
+ },
+ "cayxy": {
+ "_description": "Whitehorse, Canada",
+ "_alias": "America/Whitehorse Canada/Yukon"
+ },
+ "cayyn": {
+ "_description": "Swift Current, Canada",
+ "_alias": "America/Swift_Current"
+ },
+ "cayzf": {
+ "_description": "Yellowknife, Canada",
+ "_alias": "America/Yellowknife"
+ },
+ "cayzs": {
+ "_description": "Atikokan, Canada",
+ "_alias": "America/Coral_Harbour America/Atikokan"
+ },
+ "cccck": {
+ "_description": "Cocos (Keeling) Islands",
+ "_alias": "Indian/Cocos"
+ },
+ "cdfbm": {
+ "_description": "Lubumbashi, Democratic Republic of the Congo",
+ "_alias": "Africa/Lubumbashi"
+ },
+ "cdfih": {
+ "_description": "Kinshasa, Democratic Republic of the Congo",
+ "_alias": "Africa/Kinshasa"
+ },
+ "cfbgf": {
+ "_description": "Bangui, Central African Republic",
+ "_alias": "Africa/Bangui"
+ },
+ "cgbzv": {
+ "_description": "Brazzaville, Republic of the Congo",
+ "_alias": "Africa/Brazzaville"
+ },
+ "chzrh": {
+ "_description": "Zurich, Switzerland",
+ "_alias": "Europe/Zurich"
+ },
+ "ciabj": {
+ "_description": "Abidjan, Côte d'Ivoire",
+ "_alias": "Africa/Abidjan"
+ },
+ "ckrar": {
+ "_description": "Rarotonga, Cook Islands",
+ "_alias": "Pacific/Rarotonga"
+ },
+ "clipc": {
+ "_description": "Easter Island, Chile",
+ "_alias": "Pacific/Easter Chile/EasterIsland"
+ },
+ "clpuq": {
+ "_description": "Punta Arenas, Chile",
+ "_alias": "America/Punta_Arenas",
+ "_since": "31"
+ },
+ "clscl": {
+ "_description": "Santiago, Chile",
+ "_alias": "America/Santiago Chile/Continental"
+ },
+ "cmdla": {
+ "_description": "Douala, Cameroon",
+ "_alias": "Africa/Douala"
+ },
+ "cnckg": {
+ "_deprecated": true,
+ "_description": "Chongqing, China",
+ "_preferred": "cnsha"
+ },
+ "cnhrb": {
+ "_deprecated": true,
+ "_description": "Harbin, China",
+ "_preferred": "cnsha"
+ },
+ "cnkhg": {
+ "_deprecated": true,
+ "_description": "Kashgar, China",
+ "_preferred": "cnurc"
+ },
+ "cnsha": {
+ "_description": "Shanghai, China",
+ "_alias": "Asia/Shanghai Asia/Chongqing Asia/Chungking Asia/Harbin PRC"
+ },
+ "cnurc": {
+ "_description": "Ürümqi, China",
+ "_alias": "Asia/Urumqi Asia/Kashgar"
+ },
+ "cobog": {
+ "_description": "Bogotá, Colombia",
+ "_alias": "America/Bogota"
+ },
+ "crsjo": {
+ "_description": "Costa Rica",
+ "_alias": "America/Costa_Rica"
+ },
+ "cst6cdt": {
+ "_description": "POSIX style time zone for US Central Time",
+ "_alias": "CST6CDT",
+ "_since": "1.8"
+ },
+ "cuhav": {
+ "_description": "Havana, Cuba",
+ "_alias": "America/Havana Cuba"
+ },
+ "cvrai": {
+ "_description": "Cape Verde",
+ "_alias": "Atlantic/Cape_Verde"
+ },
+ "cxxch": {
+ "_description": "Christmas Island",
+ "_alias": "Indian/Christmas"
+ },
+ "cyfmg": {
+ "_description": "Famagusta, Cyprus",
+ "_alias": "Asia/Famagusta",
+ "_since": "31"
+ },
+ "cynic": {
+ "_description": "Nicosia, Cyprus",
+ "_alias": "Asia/Nicosia Europe/Nicosia"
+ },
+ "czprg": {
+ "_description": "Prague, Czech Republic",
+ "_alias": "Europe/Prague"
+ },
+ "deber": {
+ "_description": "Berlin, Germany",
+ "_alias": "Europe/Berlin"
+ },
+ "debsngn": {
+ "_description": "Busingen, Germany",
+ "_alias": "Europe/Busingen",
+ "_since": "23"
+ },
+ "djjib": {
+ "_description": "Djibouti",
+ "_alias": "Africa/Djibouti"
+ },
+ "dkcph": {
+ "_description": "Copenhagen, Denmark",
+ "_alias": "Europe/Copenhagen"
+ },
+ "dmdom": {
+ "_description": "Dominica",
+ "_alias": "America/Dominica"
+ },
+ "dosdq": {
+ "_description": "Santo Domingo, Dominican Republic",
+ "_alias": "America/Santo_Domingo"
+ },
+ "dzalg": {
+ "_description": "Algiers, Algeria",
+ "_alias": "Africa/Algiers"
+ },
+ "ecgps": {
+ "_description": "Galápagos Islands, Ecuador",
+ "_alias": "Pacific/Galapagos"
+ },
+ "ecgye": {
+ "_description": "Guayaquil, Ecuador",
+ "_alias": "America/Guayaquil"
+ },
+ "eetll": {
+ "_description": "Tallinn, Estonia",
+ "_alias": "Europe/Tallinn"
+ },
+ "egcai": {
+ "_description": "Cairo, Egypt",
+ "_alias": "Africa/Cairo Egypt"
+ },
+ "eheai": {
+ "_description": "El Aaiún, Western Sahara",
+ "_alias": "Africa/El_Aaiun"
+ },
+ "erasm": {
+ "_description": "Asmara, Eritrea",
+ "_alias": "Africa/Asmera Africa/Asmara"
+ },
+ "esceu": {
+ "_description": "Ceuta, Spain",
+ "_alias": "Africa/Ceuta"
+ },
+ "eslpa": {
+ "_description": "Canary Islands, Spain",
+ "_alias": "Atlantic/Canary"
+ },
+ "esmad": {
+ "_description": "Madrid, Spain",
+ "_alias": "Europe/Madrid"
+ },
+ "est5edt": {
+ "_description": "POSIX style time zone for US Eastern Time",
+ "_alias": "EST5EDT",
+ "_since": "1.8"
+ },
+ "etadd": {
+ "_description": "Addis Ababa, Ethiopia",
+ "_alias": "Africa/Addis_Ababa"
+ },
+ "fihel": {
+ "_description": "Helsinki, Finland",
+ "_alias": "Europe/Helsinki"
+ },
+ "fimhq": {
+ "_description": "Mariehamn, Åland, Finland",
+ "_alias": "Europe/Mariehamn"
+ },
+ "fjsuv": {
+ "_description": "Fiji",
+ "_alias": "Pacific/Fiji"
+ },
+ "fkpsy": {
+ "_description": "Stanley, Falkland Islands",
+ "_alias": "Atlantic/Stanley"
+ },
+ "fmksa": {
+ "_description": "Kosrae, Micronesia",
+ "_alias": "Pacific/Kosrae"
+ },
+ "fmpni": {
+ "_description": "Pohnpei, Micronesia",
+ "_alias": "Pacific/Ponape Pacific/Pohnpei"
+ },
+ "fmtkk": {
+ "_description": "Chuuk, Micronesia",
+ "_alias": "Pacific/Truk Pacific/Chuuk Pacific/Yap"
+ },
+ "fotho": {
+ "_description": "Faroe Islands",
+ "_alias": "Atlantic/Faeroe Atlantic/Faroe"
+ },
+ "frpar": {
+ "_description": "Paris, France",
+ "_alias": "Europe/Paris"
+ },
+ "galbv": {
+ "_description": "Libreville, Gabon",
+ "_alias": "Africa/Libreville"
+ },
+ "gaza": {
+ "_deprecated": true,
+ "_description": "Gaza Strip, Palestinian Territories",
+ "_preferred": "gazastrp"
+ },
+ "gazastrp": {
+ "_description": "Gaza Strip, Palestinian Territories",
+ "_alias": "Asia/Gaza",
+ "_since": "40"
+ },
+ "gblon": {
+ "_description": "London, United Kingdom",
+ "_alias": "Europe/London Europe/Belfast GB GB-Eire"
+ },
+ "gdgnd": {
+ "_description": "Grenada",
+ "_alias": "America/Grenada"
+ },
+ "getbs": {
+ "_description": "Tbilisi, Georgia",
+ "_alias": "Asia/Tbilisi"
+ },
+ "gfcay": {
+ "_description": "Cayenne, French Guiana",
+ "_alias": "America/Cayenne"
+ },
+ "gggci": {
+ "_description": "Guernsey",
+ "_alias": "Europe/Guernsey"
+ },
+ "ghacc": {
+ "_description": "Accra, Ghana",
+ "_alias": "Africa/Accra"
+ },
+ "gigib": {
+ "_description": "Gibraltar",
+ "_alias": "Europe/Gibraltar"
+ },
+ "gldkshvn": {
+ "_description": "Danmarkshavn, Greenland",
+ "_alias": "America/Danmarkshavn"
+ },
+ "glgoh": {
+ "_description": "Nuuk (Godthåb), Greenland",
+ "_alias": "America/Godthab America/Nuuk"
+ },
+ "globy": {
+ "_description": "Ittoqqortoormiit (Scoresbysund), Greenland",
+ "_alias": "America/Scoresbysund"
+ },
+ "glthu": {
+ "_description": "Qaanaaq (Thule), Greenland",
+ "_alias": "America/Thule"
+ },
+ "gmbjl": {
+ "_description": "Banjul, Gambia",
+ "_alias": "Africa/Banjul"
+ },
+ "gmt": {
+ "_description": "Greenwich Mean Time",
+ "_alias": "Etc/GMT Etc/GMT+0 Etc/GMT-0 Etc/GMT0 Etc/Greenwich GMT GMT+0 GMT-0 GMT0 Greenwich",
+ "_since": "31"
+ },
+ "gncky": {
+ "_description": "Conakry, Guinea",
+ "_alias": "Africa/Conakry"
+ },
+ "gpbbr": {
+ "_description": "Guadeloupe",
+ "_alias": "America/Guadeloupe"
+ },
+ "gpmsb": {
+ "_description": "Marigot, Saint Martin",
+ "_alias": "America/Marigot"
+ },
+ "gpsbh": {
+ "_description": "Saint Barthélemy",
+ "_alias": "America/St_Barthelemy"
+ },
+ "gqssg": {
+ "_description": "Malabo, Equatorial Guinea",
+ "_alias": "Africa/Malabo"
+ },
+ "grath": {
+ "_description": "Athens, Greece",
+ "_alias": "Europe/Athens"
+ },
+ "gsgrv": {
+ "_description": "South Georgia and the South Sandwich Islands",
+ "_alias": "Atlantic/South_Georgia"
+ },
+ "gtgua": {
+ "_description": "Guatemala",
+ "_alias": "America/Guatemala"
+ },
+ "gugum": {
+ "_description": "Guam",
+ "_alias": "Pacific/Guam"
+ },
+ "gwoxb": {
+ "_description": "Bissau, Guinea-Bissau",
+ "_alias": "Africa/Bissau"
+ },
+ "gygeo": {
+ "_description": "Guyana",
+ "_alias": "America/Guyana"
+ },
+ "hebron": {
+ "_description": "West Bank, Palestinian Territories",
+ "_alias": "Asia/Hebron",
+ "_since": "21"
+ },
+ "hkhkg": {
+ "_description": "Hong Kong SAR China",
+ "_alias": "Asia/Hong_Kong Hongkong"
+ },
+ "hntgu": {
+ "_description": "Tegucigalpa, Honduras",
+ "_alias": "America/Tegucigalpa"
+ },
+ "hrzag": {
+ "_description": "Zagreb, Croatia",
+ "_alias": "Europe/Zagreb"
+ },
+ "htpap": {
+ "_description": "Port-au-Prince, Haiti",
+ "_alias": "America/Port-au-Prince"
+ },
+ "hubud": {
+ "_description": "Budapest, Hungary",
+ "_alias": "Europe/Budapest"
+ },
+ "iddjj": {
+ "_description": "Jayapura, Indonesia",
+ "_alias": "Asia/Jayapura"
+ },
+ "idjkt": {
+ "_description": "Jakarta, Indonesia",
+ "_alias": "Asia/Jakarta"
+ },
+ "idmak": {
+ "_description": "Makassar, Indonesia",
+ "_alias": "Asia/Makassar Asia/Ujung_Pandang"
+ },
+ "idpnk": {
+ "_description": "Pontianak, Indonesia",
+ "_alias": "Asia/Pontianak"
+ },
+ "iedub": {
+ "_description": "Dublin, Ireland",
+ "_alias": "Europe/Dublin Eire"
+ },
+ "imdgs": {
+ "_description": "Isle of Man",
+ "_alias": "Europe/Isle_of_Man"
+ },
+ "inccu": {
+ "_description": "Kolkata, India",
+ "_alias": "Asia/Calcutta Asia/Kolkata"
+ },
+ "iodga": {
+ "_description": "Chagos Archipelago",
+ "_alias": "Indian/Chagos"
+ },
+ "iqbgw": {
+ "_description": "Baghdad, Iraq",
+ "_alias": "Asia/Baghdad"
+ },
+ "irthr": {
+ "_description": "Tehran, Iran",
+ "_alias": "Asia/Tehran Iran"
+ },
+ "isrey": {
+ "_description": "Reykjavik, Iceland",
+ "_alias": "Atlantic/Reykjavik Iceland"
+ },
+ "itrom": {
+ "_description": "Rome, Italy",
+ "_alias": "Europe/Rome"
+ },
+ "jeruslm": {
+ "_description": "Jerusalem",
+ "_alias": "Asia/Jerusalem Asia/Tel_Aviv Israel"
+ },
+ "jesth": {
+ "_description": "Jersey",
+ "_alias": "Europe/Jersey"
+ },
+ "jmkin": {
+ "_description": "Jamaica",
+ "_alias": "America/Jamaica Jamaica"
+ },
+ "joamm": {
+ "_description": "Amman, Jordan",
+ "_alias": "Asia/Amman"
+ },
+ "jptyo": {
+ "_description": "Tokyo, Japan",
+ "_alias": "Asia/Tokyo Japan"
+ },
+ "kenbo": {
+ "_description": "Nairobi, Kenya",
+ "_alias": "Africa/Nairobi"
+ },
+ "kgfru": {
+ "_description": "Bishkek, Kyrgyzstan",
+ "_alias": "Asia/Bishkek"
+ },
+ "khpnh": {
+ "_description": "Phnom Penh, Cambodia",
+ "_alias": "Asia/Phnom_Penh"
+ },
+ "kicxi": {
+ "_description": "Kiritimati, Kiribati",
+ "_alias": "Pacific/Kiritimati"
+ },
+ "kipho": {
+ "_description": "Enderbury Island, Kiribati",
+ "_alias": "Pacific/Enderbury Pacific/Kanton"
+ },
+ "kitrw": {
+ "_description": "Tarawa, Kiribati",
+ "_alias": "Pacific/Tarawa"
+ },
+ "kmyva": {
+ "_description": "Comoros",
+ "_alias": "Indian/Comoro"
+ },
+ "knbas": {
+ "_description": "Saint Kitts",
+ "_alias": "America/St_Kitts"
+ },
+ "kpfnj": {
+ "_description": "Pyongyang, North Korea",
+ "_alias": "Asia/Pyongyang"
+ },
+ "krsel": {
+ "_description": "Seoul, South Korea",
+ "_alias": "Asia/Seoul ROK"
+ },
+ "kwkwi": {
+ "_description": "Kuwait",
+ "_alias": "Asia/Kuwait"
+ },
+ "kygec": {
+ "_description": "Cayman Islands",
+ "_alias": "America/Cayman"
+ },
+ "kzaau": {
+ "_description": "Aqtau, Kazakhstan",
+ "_alias": "Asia/Aqtau"
+ },
+ "kzakx": {
+ "_description": "Aqtobe, Kazakhstan",
+ "_alias": "Asia/Aqtobe"
+ },
+ "kzala": {
+ "_description": "Almaty, Kazakhstan",
+ "_alias": "Asia/Almaty"
+ },
+ "kzguw": {
+ "_description": "Atyrau (Guryev), Kazakhstan",
+ "_alias": "Asia/Atyrau",
+ "_since": "31"
+ },
+ "kzksn": {
+ "_description": "Qostanay (Kostanay), Kazakhstan",
+ "_alias": "Asia/Qostanay",
+ "_since": "35"
+ },
+ "kzkzo": {
+ "_description": "Kyzylorda, Kazakhstan",
+ "_alias": "Asia/Qyzylorda"
+ },
+ "kzura": {
+ "_description": "Oral, Kazakhstan",
+ "_alias": "Asia/Oral"
+ },
+ "lavte": {
+ "_description": "Vientiane, Laos",
+ "_alias": "Asia/Vientiane"
+ },
+ "lbbey": {
+ "_description": "Beirut, Lebanon",
+ "_alias": "Asia/Beirut"
+ },
+ "lccas": {
+ "_description": "Saint Lucia",
+ "_alias": "America/St_Lucia"
+ },
+ "livdz": {
+ "_description": "Vaduz, Liechtenstein",
+ "_alias": "Europe/Vaduz"
+ },
+ "lkcmb": {
+ "_description": "Colombo, Sri Lanka",
+ "_alias": "Asia/Colombo"
+ },
+ "lrmlw": {
+ "_description": "Monrovia, Liberia",
+ "_alias": "Africa/Monrovia"
+ },
+ "lsmsu": {
+ "_description": "Maseru, Lesotho",
+ "_alias": "Africa/Maseru"
+ },
+ "ltvno": {
+ "_description": "Vilnius, Lithuania",
+ "_alias": "Europe/Vilnius"
+ },
+ "lulux": {
+ "_description": "Luxembourg",
+ "_alias": "Europe/Luxembourg"
+ },
+ "lvrix": {
+ "_description": "Riga, Latvia",
+ "_alias": "Europe/Riga"
+ },
+ "lytip": {
+ "_description": "Tripoli, Libya",
+ "_alias": "Africa/Tripoli Libya"
+ },
+ "macas": {
+ "_description": "Casablanca, Morocco",
+ "_alias": "Africa/Casablanca"
+ },
+ "mcmon": {
+ "_description": "Monaco",
+ "_alias": "Europe/Monaco"
+ },
+ "mdkiv": {
+ "_description": "Chişinău, Moldova",
+ "_alias": "Europe/Chisinau Europe/Tiraspol"
+ },
+ "metgd": {
+ "_description": "Podgorica, Montenegro",
+ "_alias": "Europe/Podgorica"
+ },
+ "mgtnr": {
+ "_description": "Antananarivo, Madagascar",
+ "_alias": "Indian/Antananarivo"
+ },
+ "mhkwa": {
+ "_description": "Kwajalein, Marshall Islands",
+ "_alias": "Pacific/Kwajalein Kwajalein"
+ },
+ "mhmaj": {
+ "_description": "Majuro, Marshall Islands",
+ "_alias": "Pacific/Majuro"
+ },
+ "mkskp": {
+ "_description": "Skopje, Macedonia",
+ "_alias": "Europe/Skopje"
+ },
+ "mlbko": {
+ "_description": "Bamako, Mali",
+ "_alias": "Africa/Bamako Africa/Timbuktu"
+ },
+ "mmrgn": {
+ "_description": "Yangon (Rangoon), Burma",
+ "_alias": "Asia/Rangoon Asia/Yangon"
+ },
+ "mncoq": {
+ "_description": "Choibalsan, Mongolia",
+ "_alias": "Asia/Choibalsan"
+ },
+ "mnhvd": {
+ "_description": "Khovd (Hovd), Mongolia",
+ "_alias": "Asia/Hovd"
+ },
+ "mnuln": {
+ "_description": "Ulaanbaatar (Ulan Bator), Mongolia",
+ "_alias": "Asia/Ulaanbaatar Asia/Ulan_Bator"
+ },
+ "momfm": {
+ "_description": "Macau SAR China",
+ "_alias": "Asia/Macau Asia/Macao"
+ },
+ "mpspn": {
+ "_description": "Saipan, Northern Mariana Islands",
+ "_alias": "Pacific/Saipan"
+ },
+ "mqfdf": {
+ "_description": "Martinique",
+ "_alias": "America/Martinique"
+ },
+ "mrnkc": {
+ "_description": "Nouakchott, Mauritania",
+ "_alias": "Africa/Nouakchott"
+ },
+ "msmni": {
+ "_description": "Montserrat",
+ "_alias": "America/Montserrat"
+ },
+ "mst7mdt": {
+ "_description": "POSIX style time zone for US Mountain Time",
+ "_alias": "MST7MDT",
+ "_since": "1.8"
+ },
+ "mtmla": {
+ "_description": "Malta",
+ "_alias": "Europe/Malta"
+ },
+ "muplu": {
+ "_description": "Mauritius",
+ "_alias": "Indian/Mauritius"
+ },
+ "mvmle": {
+ "_description": "Maldives",
+ "_alias": "Indian/Maldives"
+ },
+ "mwblz": {
+ "_description": "Blantyre, Malawi",
+ "_alias": "Africa/Blantyre"
+ },
+ "mxchi": {
+ "_description": "Chihuahua, Mexico",
+ "_alias": "America/Chihuahua"
+ },
+ "mxcjs": {
+ "_description": "Ciudad Juárez, Mexico",
+ "_alias": "America/Ciudad_Juarez",
+ "_since": "43"
+ },
+ "mxcun": {
+ "_description": "Cancún, Mexico",
+ "_alias": "America/Cancun"
+ },
+ "mxhmo": {
+ "_description": "Hermosillo, Mexico",
+ "_alias": "America/Hermosillo"
+ },
+ "mxmam": {
+ "_description": "Matamoros, Mexico",
+ "_alias": "America/Matamoros"
+ },
+ "mxmex": {
+ "_description": "Mexico City, Mexico",
+ "_alias": "America/Mexico_City Mexico/General"
+ },
+ "mxmid": {
+ "_description": "Mérida, Mexico",
+ "_alias": "America/Merida"
+ },
+ "mxmty": {
+ "_description": "Monterrey, Mexico",
+ "_alias": "America/Monterrey"
+ },
+ "mxmzt": {
+ "_description": "Mazatlán, Mexico",
+ "_alias": "America/Mazatlan Mexico/BajaSur"
+ },
+ "mxoji": {
+ "_description": "Ojinaga, Mexico",
+ "_alias": "America/Ojinaga"
+ },
+ "mxpvr": {
+ "_description": "Bahía de Banderas, Mexico",
+ "_alias": "America/Bahia_Banderas",
+ "_since": "1.9"
+ },
+ "mxstis": {
+ "_description": "Santa Isabel (Baja California), Mexico",
+ "_alias": "America/Santa_Isabel"
+ },
+ "mxtij": {
+ "_description": "Tijuana, Mexico",
+ "_alias": "America/Tijuana America/Ensenada Mexico/BajaNorte"
+ },
+ "mykch": {
+ "_description": "Kuching, Malaysia",
+ "_alias": "Asia/Kuching"
+ },
+ "mykul": {
+ "_description": "Kuala Lumpur, Malaysia",
+ "_alias": "Asia/Kuala_Lumpur"
+ },
+ "mzmpm": {
+ "_description": "Maputo, Mozambique",
+ "_alias": "Africa/Maputo"
+ },
+ "nawdh": {
+ "_description": "Windhoek, Namibia",
+ "_alias": "Africa/Windhoek"
+ },
+ "ncnou": {
+ "_description": "Noumea, New Caledonia",
+ "_alias": "Pacific/Noumea"
+ },
+ "nenim": {
+ "_description": "Niamey, Niger",
+ "_alias": "Africa/Niamey"
+ },
+ "nfnlk": {
+ "_description": "Norfolk Island",
+ "_alias": "Pacific/Norfolk"
+ },
+ "nglos": {
+ "_description": "Lagos, Nigeria",
+ "_alias": "Africa/Lagos"
+ },
+ "nimga": {
+ "_description": "Managua, Nicaragua",
+ "_alias": "America/Managua"
+ },
+ "nlams": {
+ "_description": "Amsterdam, Netherlands",
+ "_alias": "Europe/Amsterdam"
+ },
+ "noosl": {
+ "_description": "Oslo, Norway",
+ "_alias": "Europe/Oslo"
+ },
+ "npktm": {
+ "_description": "Kathmandu, Nepal",
+ "_alias": "Asia/Katmandu Asia/Kathmandu"
+ },
+ "nrinu": {
+ "_description": "Nauru",
+ "_alias": "Pacific/Nauru"
+ },
+ "nuiue": {
+ "_description": "Niue",
+ "_alias": "Pacific/Niue"
+ },
+ "nzakl": {
+ "_description": "Auckland, New Zealand",
+ "_alias": "Pacific/Auckland Antarctica/South_Pole NZ"
+ },
+ "nzcht": {
+ "_description": "Chatham Islands, New Zealand",
+ "_alias": "Pacific/Chatham NZ-CHAT"
+ },
+ "ommct": {
+ "_description": "Muscat, Oman",
+ "_alias": "Asia/Muscat"
+ },
+ "papty": {
+ "_description": "Panama",
+ "_alias": "America/Panama"
+ },
+ "pelim": {
+ "_description": "Lima, Peru",
+ "_alias": "America/Lima"
+ },
+ "pfgmr": {
+ "_description": "Gambiera Islands, French Polynesia",
+ "_alias": "Pacific/Gambier"
+ },
+ "pfnhv": {
+ "_description": "Marquesas Islands, French Polynesia",
+ "_alias": "Pacific/Marquesas"
+ },
+ "pfppt": {
+ "_description": "Tahiti, French Polynesia",
+ "_alias": "Pacific/Tahiti"
+ },
+ "pgpom": {
+ "_description": "Port Moresby, Papua New Guinea",
+ "_alias": "Pacific/Port_Moresby"
+ },
+ "pgraw": {
+ "_description": "Bougainville, Papua New Guinea",
+ "_alias": "Pacific/Bougainville",
+ "_since": "27"
+ },
+ "phmnl": {
+ "_description": "Manila, Philippines",
+ "_alias": "Asia/Manila"
+ },
+ "pkkhi": {
+ "_description": "Karachi, Pakistan",
+ "_alias": "Asia/Karachi"
+ },
+ "plwaw": {
+ "_description": "Warsaw, Poland",
+ "_alias": "Europe/Warsaw Poland"
+ },
+ "pmmqc": {
+ "_description": "Saint Pierre and Miquelon",
+ "_alias": "America/Miquelon"
+ },
+ "pnpcn": {
+ "_description": "Pitcairn Islands",
+ "_alias": "Pacific/Pitcairn"
+ },
+ "prsju": {
+ "_description": "Puerto Rico",
+ "_alias": "America/Puerto_Rico"
+ },
+ "pst8pdt": {
+ "_description": "POSIX style time zone for US Pacific Time",
+ "_alias": "PST8PDT",
+ "_since": "1.8"
+ },
+ "ptfnc": {
+ "_description": "Madeira, Portugal",
+ "_alias": "Atlantic/Madeira"
+ },
+ "ptlis": {
+ "_description": "Lisbon, Portugal",
+ "_alias": "Europe/Lisbon Portugal"
+ },
+ "ptpdl": {
+ "_description": "Azores, Portugal",
+ "_alias": "Atlantic/Azores"
+ },
+ "pwror": {
+ "_description": "Palau",
+ "_alias": "Pacific/Palau"
+ },
+ "pyasu": {
+ "_description": "Asunción, Paraguay",
+ "_alias": "America/Asuncion"
+ },
+ "qadoh": {
+ "_description": "Qatar",
+ "_alias": "Asia/Qatar"
+ },
+ "rereu": {
+ "_description": "Réunion",
+ "_alias": "Indian/Reunion"
+ },
+ "robuh": {
+ "_description": "Bucharest, Romania",
+ "_alias": "Europe/Bucharest"
+ },
+ "rsbeg": {
+ "_description": "Belgrade, Serbia",
+ "_alias": "Europe/Belgrade"
+ },
+ "ruasf": {
+ "_description": "Astrakhan, Russia",
+ "_alias": "Europe/Astrakhan",
+ "_since": "30"
+ },
+ "rubax": {
+ "_description": "Barnaul, Russia",
+ "_alias": "Asia/Barnaul",
+ "_since": "30"
+ },
+ "ruchita": {
+ "_description": "Chita Zabaykalsky, Russia",
+ "_alias": "Asia/Chita",
+ "_since": "26"
+ },
+ "rudyr": {
+ "_description": "Anadyr, Russia",
+ "_alias": "Asia/Anadyr"
+ },
+ "rugdx": {
+ "_description": "Magadan, Russia",
+ "_alias": "Asia/Magadan"
+ },
+ "ruikt": {
+ "_description": "Irkutsk, Russia",
+ "_alias": "Asia/Irkutsk"
+ },
+ "rukgd": {
+ "_description": "Kaliningrad, Russia",
+ "_alias": "Europe/Kaliningrad"
+ },
+ "rukhndg": {
+ "_description": "Khandyga Tomponsky, Russia",
+ "_alias": "Asia/Khandyga",
+ "_since": "23"
+ },
+ "rukra": {
+ "_description": "Krasnoyarsk, Russia",
+ "_alias": "Asia/Krasnoyarsk"
+ },
+ "rukuf": {
+ "_description": "Samara, Russia",
+ "_alias": "Europe/Samara"
+ },
+ "rukvx": {
+ "_description": "Kirov, Russia",
+ "_alias": "Europe/Kirov",
+ "_since": "30"
+ },
+ "rumow": {
+ "_description": "Moscow, Russia",
+ "_alias": "Europe/Moscow W-SU"
+ },
+ "runoz": {
+ "_description": "Novokuznetsk, Russia",
+ "_alias": "Asia/Novokuznetsk"
+ },
+ "ruoms": {
+ "_description": "Omsk, Russia",
+ "_alias": "Asia/Omsk"
+ },
+ "ruovb": {
+ "_description": "Novosibirsk, Russia",
+ "_alias": "Asia/Novosibirsk"
+ },
+ "rupkc": {
+ "_description": "Kamchatka Peninsula, Russia",
+ "_alias": "Asia/Kamchatka"
+ },
+ "rurtw": {
+ "_description": "Saratov, Russia",
+ "_alias": "Europe/Saratov",
+ "_since": "31"
+ },
+ "rusred": {
+ "_description": "Srednekolymsk, Russia",
+ "_alias": "Asia/Srednekolymsk",
+ "_since": "26"
+ },
+ "rutof": {
+ "_description": "Tomsk, Russia",
+ "_alias": "Asia/Tomsk",
+ "_since": "30"
+ },
+ "ruuly": {
+ "_description": "Ulyanovsk, Russia",
+ "_alias": "Europe/Ulyanovsk",
+ "_since": "30"
+ },
+ "ruunera": {
+ "_description": "Ust-Nera Oymyakonsky, Russia",
+ "_alias": "Asia/Ust-Nera",
+ "_since": "23"
+ },
+ "ruuus": {
+ "_description": "Sakhalin, Russia",
+ "_alias": "Asia/Sakhalin"
+ },
+ "ruvog": {
+ "_description": "Volgograd, Russia",
+ "_alias": "Europe/Volgograd"
+ },
+ "ruvvo": {
+ "_description": "Vladivostok, Russia",
+ "_alias": "Asia/Vladivostok"
+ },
+ "ruyek": {
+ "_description": "Yekaterinburg, Russia",
+ "_alias": "Asia/Yekaterinburg"
+ },
+ "ruyks": {
+ "_description": "Yakutsk, Russia",
+ "_alias": "Asia/Yakutsk"
+ },
+ "rwkgl": {
+ "_description": "Kigali, Rwanda",
+ "_alias": "Africa/Kigali"
+ },
+ "saruh": {
+ "_description": "Riyadh, Saudi Arabia",
+ "_alias": "Asia/Riyadh"
+ },
+ "sbhir": {
+ "_description": "Guadalcanal, Solomon Islands",
+ "_alias": "Pacific/Guadalcanal"
+ },
+ "scmaw": {
+ "_description": "Mahé, Seychelles",
+ "_alias": "Indian/Mahe"
+ },
+ "sdkrt": {
+ "_description": "Khartoum, Sudan",
+ "_alias": "Africa/Khartoum"
+ },
+ "sesto": {
+ "_description": "Stockholm, Sweden",
+ "_alias": "Europe/Stockholm"
+ },
+ "sgsin": {
+ "_description": "Singapore",
+ "_alias": "Asia/Singapore Singapore"
+ },
+ "shshn": {
+ "_description": "Saint Helena",
+ "_alias": "Atlantic/St_Helena"
+ },
+ "silju": {
+ "_description": "Ljubljana, Slovenia",
+ "_alias": "Europe/Ljubljana"
+ },
+ "sjlyr": {
+ "_description": "Longyearbyen, Svalbard",
+ "_alias": "Arctic/Longyearbyen Atlantic/Jan_Mayen"
+ },
+ "skbts": {
+ "_description": "Bratislava, Slovakia",
+ "_alias": "Europe/Bratislava"
+ },
+ "slfna": {
+ "_description": "Freetown, Sierra Leone",
+ "_alias": "Africa/Freetown"
+ },
+ "smsai": {
+ "_description": "San Marino",
+ "_alias": "Europe/San_Marino"
+ },
+ "sndkr": {
+ "_description": "Dakar, Senegal",
+ "_alias": "Africa/Dakar"
+ },
+ "somgq": {
+ "_description": "Mogadishu, Somalia",
+ "_alias": "Africa/Mogadishu"
+ },
+ "srpbm": {
+ "_description": "Paramaribo, Suriname",
+ "_alias": "America/Paramaribo"
+ },
+ "ssjub": {
+ "_description": "Juba, South Sudan",
+ "_alias": "Africa/Juba",
+ "_since": "21"
+ },
+ "sttms": {
+ "_description": "São Tomé, São Tomé and Príncipe",
+ "_alias": "Africa/Sao_Tome"
+ },
+ "svsal": {
+ "_description": "El Salvador",
+ "_alias": "America/El_Salvador"
+ },
+ "sxphi": {
+ "_description": "Sint Maarten",
+ "_alias": "America/Lower_Princes",
+ "_since": "21"
+ },
+ "sydam": {
+ "_description": "Damascus, Syria",
+ "_alias": "Asia/Damascus"
+ },
+ "szqmn": {
+ "_description": "Mbabane, Swaziland",
+ "_alias": "Africa/Mbabane"
+ },
+ "tcgdt": {
+ "_description": "Grand Turk, Turks and Caicos Islands",
+ "_alias": "America/Grand_Turk"
+ },
+ "tdndj": {
+ "_description": "N'Djamena, Chad",
+ "_alias": "Africa/Ndjamena"
+ },
+ "tfpfr": {
+ "_description": "Kerguelen Islands, French Southern Territories",
+ "_alias": "Indian/Kerguelen"
+ },
+ "tglfw": {
+ "_description": "Lomé, Togo",
+ "_alias": "Africa/Lome"
+ },
+ "thbkk": {
+ "_description": "Bangkok, Thailand",
+ "_alias": "Asia/Bangkok"
+ },
+ "tjdyu": {
+ "_description": "Dushanbe, Tajikistan",
+ "_alias": "Asia/Dushanbe"
+ },
+ "tkfko": {
+ "_description": "Fakaofo, Tokelau",
+ "_alias": "Pacific/Fakaofo"
+ },
+ "tldil": {
+ "_description": "Dili, East Timor",
+ "_alias": "Asia/Dili"
+ },
+ "tmasb": {
+ "_description": "Ashgabat, Turkmenistan",
+ "_alias": "Asia/Ashgabat Asia/Ashkhabad"
+ },
+ "tntun": {
+ "_description": "Tunis, Tunisia",
+ "_alias": "Africa/Tunis"
+ },
+ "totbu": {
+ "_description": "Tongatapu, Tonga",
+ "_alias": "Pacific/Tongatapu"
+ },
+ "trist": {
+ "_description": "Istanbul, Türkiye",
+ "_alias": "Europe/Istanbul Asia/Istanbul Turkey"
+ },
+ "ttpos": {
+ "_description": "Port of Spain, Trinidad and Tobago",
+ "_alias": "America/Port_of_Spain"
+ },
+ "tvfun": {
+ "_description": "Funafuti, Tuvalu",
+ "_alias": "Pacific/Funafuti"
+ },
+ "twtpe": {
+ "_description": "Taipei, Taiwan",
+ "_alias": "Asia/Taipei ROC"
+ },
+ "tzdar": {
+ "_description": "Dar es Salaam, Tanzania",
+ "_alias": "Africa/Dar_es_Salaam"
+ },
+ "uaiev": {
+ "_description": "Kyiv, Ukraine",
+ "_alias": "Europe/Kiev Europe/Kyiv"
+ },
+ "uaozh": {
+ "_description": "Zaporizhia (Zaporozhye), Ukraine",
+ "_alias": "Europe/Zaporozhye"
+ },
+ "uasip": {
+ "_description": "Simferopol, Ukraine",
+ "_alias": "Europe/Simferopol"
+ },
+ "uauzh": {
+ "_description": "Uzhhorod (Uzhgorod), Ukraine",
+ "_alias": "Europe/Uzhgorod"
+ },
+ "ugkla": {
+ "_description": "Kampala, Uganda",
+ "_alias": "Africa/Kampala"
+ },
+ "umawk": {
+ "_description": "Wake Island, U.S. Minor Outlying Islands",
+ "_alias": "Pacific/Wake"
+ },
+ "umjon": {
+ "_description": "Johnston Atoll, U.S. Minor Outlying Islands",
+ "_alias": "Pacific/Johnston"
+ },
+ "ummdy": {
+ "_description": "Midway Islands, U.S. Minor Outlying Islands",
+ "_alias": "Pacific/Midway"
+ },
+ "unk": {
+ "_description": "Unknown time zone",
+ "_alias": "Etc/Unknown"
+ },
+ "usadk": {
+ "_description": "Adak (Alaska), United States",
+ "_alias": "America/Adak America/Atka US/Aleutian"
+ },
+ "usaeg": {
+ "_description": "Marengo (Indiana), United States",
+ "_alias": "America/Indiana/Marengo"
+ },
+ "usanc": {
+ "_description": "Anchorage, United States",
+ "_alias": "America/Anchorage US/Alaska"
+ },
+ "usboi": {
+ "_description": "Boise (Idaho), United States",
+ "_alias": "America/Boise"
+ },
+ "uschi": {
+ "_description": "Chicago, United States",
+ "_alias": "America/Chicago US/Central"
+ },
+ "usden": {
+ "_description": "Denver, United States",
+ "_alias": "America/Denver America/Shiprock Navajo US/Mountain"
+ },
+ "usdet": {
+ "_description": "Detroit, United States",
+ "_alias": "America/Detroit US/Michigan"
+ },
+ "ushnl": {
+ "_description": "Honolulu, United States",
+ "_alias": "Pacific/Honolulu US/Hawaii"
+ },
+ "usind": {
+ "_description": "Indianapolis, United States",
+ "_alias": "America/Indianapolis America/Fort_Wayne America/Indiana/Indianapolis US/East-Indiana"
+ },
+ "usinvev": {
+ "_description": "Vevay (Indiana), United States",
+ "_alias": "America/Indiana/Vevay"
+ },
+ "usjnu": {
+ "_description": "Juneau (Alaska), United States",
+ "_alias": "America/Juneau"
+ },
+ "usknx": {
+ "_description": "Knox (Indiana), United States",
+ "_alias": "America/Indiana/Knox America/Knox_IN US/Indiana-Starke"
+ },
+ "uslax": {
+ "_description": "Los Angeles, United States",
+ "_alias": "America/Los_Angeles US/Pacific US/Pacific-New"
+ },
+ "uslui": {
+ "_description": "Louisville (Kentucky), United States",
+ "_alias": "America/Louisville America/Kentucky/Louisville"
+ },
+ "usmnm": {
+ "_description": "Menominee (Michigan), United States",
+ "_alias": "America/Menominee"
+ },
+ "usmoc": {
+ "_description": "Monticello (Kentucky), United States",
+ "_alias": "America/Kentucky/Monticello"
+ },
+ "usmtm": {
+ "_description": "Metlakatla (Alaska), United States",
+ "_alias": "America/Metlakatla",
+ "_since": "1.9.1"
+ },
+ "usnavajo": {
+ "_deprecated": true,
+ "_description": "Shiprock (Navajo), United States",
+ "_preferred": "usden"
+ },
+ "usndcnt": {
+ "_description": "Center (North Dakota), United States",
+ "_alias": "America/North_Dakota/Center"
+ },
+ "usndnsl": {
+ "_description": "New Salem (North Dakota), United States",
+ "_alias": "America/North_Dakota/New_Salem"
+ },
+ "usnyc": {
+ "_description": "New York, United States",
+ "_alias": "America/New_York US/Eastern"
+ },
+ "usoea": {
+ "_description": "Vincennes (Indiana), United States",
+ "_alias": "America/Indiana/Vincennes"
+ },
+ "usome": {
+ "_description": "Nome (Alaska), United States",
+ "_alias": "America/Nome"
+ },
+ "usphx": {
+ "_description": "Phoenix, United States",
+ "_alias": "America/Phoenix US/Arizona"
+ },
+ "ussit": {
+ "_description": "Sitka (Alaska), United States",
+ "_alias": "America/Sitka",
+ "_since": "1.9.1"
+ },
+ "ustel": {
+ "_description": "Tell City (Indiana), United States",
+ "_alias": "America/Indiana/Tell_City"
+ },
+ "uswlz": {
+ "_description": "Winamac (Indiana), United States",
+ "_alias": "America/Indiana/Winamac"
+ },
+ "uswsq": {
+ "_description": "Petersburg (Indiana), United States",
+ "_alias": "America/Indiana/Petersburg"
+ },
+ "usxul": {
+ "_description": "Beulah (North Dakota), United States",
+ "_alias": "America/North_Dakota/Beulah",
+ "_since": "1.9.1"
+ },
+ "usyak": {
+ "_description": "Yakutat (Alaska), United States",
+ "_alias": "America/Yakutat"
+ },
+ "utc": {
+ "_description": "UTC (Coordinated Universal Time)",
+ "_alias": "Etc/UTC Etc/UCT Etc/Universal Etc/Zulu UCT UTC Universal Zulu"
+ },
+ "utce01": {
+ "_description": "1 hour ahead of UTC",
+ "_alias": "Etc/GMT-1"
+ },
+ "utce02": {
+ "_description": "2 hours ahead of UTC",
+ "_alias": "Etc/GMT-2"
+ },
+ "utce03": {
+ "_description": "3 hours ahead of UTC",
+ "_alias": "Etc/GMT-3"
+ },
+ "utce04": {
+ "_description": "4 hours ahead of UTC",
+ "_alias": "Etc/GMT-4"
+ },
+ "utce05": {
+ "_description": "5 hours ahead of UTC",
+ "_alias": "Etc/GMT-5"
+ },
+ "utce06": {
+ "_description": "6 hours ahead of UTC",
+ "_alias": "Etc/GMT-6"
+ },
+ "utce07": {
+ "_description": "7 hours ahead of UTC",
+ "_alias": "Etc/GMT-7"
+ },
+ "utce08": {
+ "_description": "8 hours ahead of UTC",
+ "_alias": "Etc/GMT-8"
+ },
+ "utce09": {
+ "_description": "9 hours ahead of UTC",
+ "_alias": "Etc/GMT-9"
+ },
+ "utce10": {
+ "_description": "10 hours ahead of UTC",
+ "_alias": "Etc/GMT-10"
+ },
+ "utce11": {
+ "_description": "11 hours ahead of UTC",
+ "_alias": "Etc/GMT-11"
+ },
+ "utce12": {
+ "_description": "12 hours ahead of UTC",
+ "_alias": "Etc/GMT-12"
+ },
+ "utce13": {
+ "_description": "13 hours ahead of UTC",
+ "_alias": "Etc/GMT-13"
+ },
+ "utce14": {
+ "_description": "14 hours ahead of UTC",
+ "_alias": "Etc/GMT-14"
+ },
+ "utcw01": {
+ "_description": "1 hour behind UTC",
+ "_alias": "Etc/GMT+1"
+ },
+ "utcw02": {
+ "_description": "2 hours behind UTC",
+ "_alias": "Etc/GMT+2"
+ },
+ "utcw03": {
+ "_description": "3 hours behind UTC",
+ "_alias": "Etc/GMT+3"
+ },
+ "utcw04": {
+ "_description": "4 hours behind UTC",
+ "_alias": "Etc/GMT+4"
+ },
+ "utcw05": {
+ "_description": "5 hours behind UTC",
+ "_alias": "Etc/GMT+5 EST"
+ },
+ "utcw06": {
+ "_description": "6 hours behind UTC",
+ "_alias": "Etc/GMT+6"
+ },
+ "utcw07": {
+ "_description": "7 hours behind UTC",
+ "_alias": "Etc/GMT+7 MST"
+ },
+ "utcw08": {
+ "_description": "8 hours behind UTC",
+ "_alias": "Etc/GMT+8"
+ },
+ "utcw09": {
+ "_description": "9 hours behind UTC",
+ "_alias": "Etc/GMT+9"
+ },
+ "utcw10": {
+ "_description": "10 hours behind UTC",
+ "_alias": "Etc/GMT+10 HST"
+ },
+ "utcw11": {
+ "_description": "11 hours behind UTC",
+ "_alias": "Etc/GMT+11"
+ },
+ "utcw12": {
+ "_description": "12 hours behind UTC",
+ "_alias": "Etc/GMT+12"
+ },
+ "uymvd": {
+ "_description": "Montevideo, Uruguay",
+ "_alias": "America/Montevideo"
+ },
+ "uzskd": {
+ "_description": "Samarkand, Uzbekistan",
+ "_alias": "Asia/Samarkand"
+ },
+ "uztas": {
+ "_description": "Tashkent, Uzbekistan",
+ "_alias": "Asia/Tashkent"
+ },
+ "vavat": {
+ "_description": "Vatican City",
+ "_alias": "Europe/Vatican"
+ },
+ "vcsvd": {
+ "_description": "Saint Vincent, Saint Vincent and the Grenadines",
+ "_alias": "America/St_Vincent"
+ },
+ "veccs": {
+ "_description": "Caracas, Venezuela",
+ "_alias": "America/Caracas"
+ },
+ "vgtov": {
+ "_description": "Tortola, British Virgin Islands",
+ "_alias": "America/Tortola"
+ },
+ "vistt": {
+ "_description": "Saint Thomas, U.S. Virgin Islands",
+ "_alias": "America/St_Thomas America/Virgin"
+ },
+ "vnsgn": {
+ "_description": "Ho Chi Minh City, Vietnam",
+ "_alias": "Asia/Saigon Asia/Ho_Chi_Minh"
+ },
+ "vuvli": {
+ "_description": "Efate, Vanuatu",
+ "_alias": "Pacific/Efate"
+ },
+ "wfmau": {
+ "_description": "Wallis Islands, Wallis and Futuna",
+ "_alias": "Pacific/Wallis"
+ },
+ "wsapw": {
+ "_description": "Apia, Samoa",
+ "_alias": "Pacific/Apia"
+ },
+ "yeade": {
+ "_description": "Aden, Yemen",
+ "_alias": "Asia/Aden"
+ },
+ "ytmam": {
+ "_description": "Mayotte",
+ "_alias": "Indian/Mayotte"
+ },
+ "zajnb": {
+ "_description": "Johannesburg, South Africa",
+ "_alias": "Africa/Johannesburg"
+ },
+ "zmlun": {
+ "_description": "Lusaka, Zambia",
+ "_alias": "Africa/Lusaka"
+ },
+ "zwhre": {
+ "_description": "Harare, Zimbabwe",
+ "_alias": "Africa/Harare"
+ }
+ }
+ }
+ }
+}
diff --git a/polyfill/test/ecmascript.mjs b/polyfill/test/ecmascript.mjs
index 03fc56e7e1..94d3c752b3 100644
--- a/polyfill/test/ecmascript.mjs
+++ b/polyfill/test/ecmascript.mjs
@@ -8,6 +8,7 @@ import { strict as assert } from 'assert';
const { deepEqual, equal, throws } = assert;
import bigInt from 'big-integer';
+import { readFileSync } from 'fs';
import * as ES from '../lib/ecmascript.mjs';
import { GetSlot, TIMEZONE_ID } from '../lib/slots.mjs';
@@ -64,6 +65,61 @@ describe('ECMAScript', () => {
});
}
});
+
+ describe('GetAvailableNamedTimeZoneIdentifier', () => {
+ it('Case-normalizes time zone IDs', () => {
+ // eslint-disable-next-line max-len
+ // curl -s https://raw.githubusercontent.com/unicode-org/cldr-json/main/cldr-json/cldr-bcp47/bcp47/timezone.json > cldr-timezone.json
+ const cldrTimeZonePath = new URL('./cldr-timezone.json', import.meta.url);
+ const cldrTimeZoneJson = JSON.parse(readFileSync(cldrTimeZonePath));
+
+ // get CLDR's time zone IDs
+ const cldrIdentifiers = Object.entries(cldrTimeZoneJson.keyword.u.tz)
+ .filter((z) => !z[0].startsWith('_')) // ignore metadata elements
+ .map((z) => z[1]._alias) // pull out the list of IANA IDs for each CLDR zone
+ .filter(Boolean) // CLDR deprecated zones no longer have an IANA ID
+ .flatMap((ids) => ids.split(' ')) // expand all space-delimited IANA IDs for each zone
+ .filter((id) => !['America/Ciudad_Juarez'].includes(id)) // exclude IDs that are too new to be supported
+ .filter((id) => !['Etc/Unknown'].includes(id)); // see https://github.com/tc39/proposal-canonical-tz/pull/25
+
+ // These 4 legacy IDs are in TZDB, in Wikipedia, and accepted by ICU, but they're not in CLDR data.
+ // Not sure where they come from, perhaps hard-coded into ICU, but we'll test them anyway.
+ const missingFromCLDR = ['CET', 'EET', 'MET', 'WET'];
+
+ // All IDs that we know about
+ const ids = [...new Set([...missingFromCLDR, ...cldrIdentifiers, ...Intl.supportedValuesOf('timeZone')])];
+
+ for (const id of ids) {
+ const lower = id.toLowerCase();
+ const upper = id.toUpperCase();
+ equal(ES.GetAvailableNamedTimeZoneIdentifier(id)?.identifier, id);
+ equal(ES.GetAvailableNamedTimeZoneIdentifier(upper)?.identifier, id);
+ equal(ES.GetAvailableNamedTimeZoneIdentifier(lower)?.identifier, id);
+ }
+ });
+ it('Returns canonical IDs', () => {
+ const ids = Intl.supportedValuesOf('timeZone');
+ for (const id of ids) {
+ equal(ES.GetAvailableNamedTimeZoneIdentifier(id).primaryIdentifier, id);
+ }
+ const knownAliases = [
+ ['America/Atka', 'America/Adak'],
+ ['America/Knox_IN', 'America/Indiana/Knox'],
+ ['Asia/Ashkhabad', 'Asia/Ashgabat'],
+ ['Asia/Dacca', 'Asia/Dhaka'],
+ ['Asia/Istanbul', 'Europe/Istanbul'],
+ ['Asia/Macao', 'Asia/Macau'],
+ ['Asia/Thimbu', 'Asia/Thimphu'],
+ ['Asia/Ujung_Pandang', 'Asia/Makassar'],
+ ['Asia/Ulan_Bator', 'Asia/Ulaanbaatar']
+ ];
+ for (const [identifier, primaryIdentifier] of knownAliases) {
+ const record = ES.GetAvailableNamedTimeZoneIdentifier(identifier);
+ equal(record.identifier, identifier);
+ equal(record.primaryIdentifier, primaryIdentifier);
+ }
+ });
+ });
});
import { normalize } from 'path';