canonicalize-timezone.js (5382B)
1 // Copyright 2025 Igalia, S.L. All rights reserved. 2 // This code is governed by the BSD license found in the LICENSE file. 3 /*--- 4 esid: sec-use-of-iana-time-zone-database 5 description: > 6 Primary and non-primary time zone identifiers must correspond with IANA Time 7 Zone Database Zones and Link names, subject to explicit exceptions, and must 8 direct time zone name canonicalization. 9 info: | 10 AvailableNamedTimeZoneIdentifiers ( ) 11 12 1. Let _identifiers_ be a List containing the String value of each Zone or Link name in the IANA Time Zone Database. 13 2. ... 14 3. ... 15 4. Let _result_ be a new empty List. 16 5. For each element _identifier_ of _identifiers_, do 17 a. Let _primary_ be _identifier_. 18 b. If _identifier_ is a Link name in the IANA Time Zone Database and _identifier_ is not present in the “TZ” column of <code>zone.tab</code> of the IANA Time Zone Database, then 19 i. Let _zone_ be the Zone name that _identifier_ resolves to, according to the rules for resolving Link names in the IANA Time Zone Database. 20 ii. If _zone_ starts with *"Etc/"*, then 21 1. Set _primary_ to _zone_. 22 iii. Else, 23 1. Let _identifierCountryCode_ be the <a href="https://www.iso.org/glossary-for-iso-3166.html">ISO 3166-1 Alpha-2</a> country code whose territory contains the geographical area corresponding to _identifier_. 24 2. Let _zoneCountryCode_ be the ISO 3166-1 Alpha-2 country code whose territory contains the geographical area corresponding to _zone_. 25 3. If _identifierCountryCode_ is _zoneCountryCode_, then 26 a. Set _primary_ to _zone_. 27 4. Else, 28 a. Let _countryCodeLineCount_ be the number of lines in file <code>zone.tab</code> of the IANA Time Zone Database where the “country-code” column is _identifierCountryCode_. 29 b. If _countryCodeLineCount_ is 1, then 30 i. Let _countryCodeLine_ be the line in file <code>zone.tab</code> of the IANA Time Zone Database where the “country-code” column is _identifierCountryCode_. 31 ii. Set _primary_ to the contents of the “TZ” column of _countryCodeLine_. 32 c. Else, 33 i. Let _backzone_ be *undefined*. 34 ii. Let _backzoneLinkLines_ be the List of lines in the file <code>backzone</code> of the IANA Time Zone Database that start with either *"Link "* or *"#PACKRATLIST zone.tab Link "*. 35 iii. ... 36 iv. Assert: _backzone_ is not *undefined*. 37 v. Set _primary_ to _backzone_. 38 c. If _primary_ is one of *"Etc/UTC"*, *"Etc/GMT"*, or *"GMT"*, set _primary_ to *"UTC"*. 39 d. ... 40 e. Let _record_ be the Time Zone Identifier Record { [[Identifier]]: _identifier_, [[PrimaryIdentifier]]: _primary_ }. 41 f. Append _record_ to _result_. 42 6. ... 43 7. Return _result_. 44 45 GetAvailableNamedTimeZoneIdentifier ( _timeZoneIdentifier_ ) 46 47 1. For each element _record_ of AvailableNamedTimeZoneIdentifiers(), do 48 a. If _record_.[[Identifier]] is an ASCII-case-insensitive match for _timeZoneIdentifier_, return record. 49 2. Return ~empty~. 50 51 CreateDateTimeFormat ( _newTarget_, _locales_, _options_, _required_, _defaults_ ) 52 53 29. If IsTimeZoneOffsetString(_timeZone_) is *true*, then 54 ... 55 30. Else, 56 a. Let _timeZoneIdentifierRecord_ be GetAvailableNamedTimeZoneIdentifier(_timeZone_). 57 b. If _timeZoneIdentifierRecord_ is ~empty~, throw a RangeError exception. 58 c. Set _timeZone_ to _timeZoneIdentifierRecord_.[[PrimaryIdentifier]]. 59 features: [canonical-tz] 60 ---*/ 61 62 const timeZones = [ 63 // Europe/Prague is not a Link name. 64 ["Europe/Prague", "Europe/Prague"], 65 66 // `backward` identifies "Europe/Bratislava" as a Link name targeting "Europe/Prague": 67 // Link Europe/Prague Europe/Bratislava 68 // Europe/Bratislava's country code "SK" has only one time zone in `zone.tab`. 69 ["Europe/Bratislava", "Europe/Bratislava"], 70 71 // `backward` identifies "Australia/Canberra" as a Link targeting "Australia/Sydney": 72 // Link Australia/Sydney Australia/Canberra 73 // Both share the country code "AU". 74 ["Australia/Canberra", "Australia/Sydney"], 75 76 // `backward` identifies "Atlantic/Jan_Mayen" as a Link name targeting "Europe/Berlin": 77 // Link Europe/Berlin Atlantic/Jan_Mayen 78 // Atlantic/Jan_Mayen's country code "SJ" has only one time zone in `zone.tab`. 79 ["Atlantic/Jan_Mayen", "Arctic/Longyearbyen"], 80 81 // `backward` identifies "Pacific/Truk" as a Link name targeting "Pacific/Port_Moresby": 82 // Link Pacific/Port_Moresby Pacific/Truk #= Pacific/Chuuk 83 // Pacific/Chuuk's country code "FM" has multiple time zones in `zone.tab`. 84 // `backzone` identifies "Pacific/Truk" as a Link name targeting "Pacific/Chuuk": 85 // Link Pacific/Chuuk Pacific/Truk 86 ["Pacific/Truk", "Pacific/Chuuk"], 87 88 // `backward` identifies "Etc/UCT" as a Link name targeting "Etc/UTC": 89 // Link Etc/UTC Etc/UCT 90 ["Etc/UCT", "UTC"], 91 92 // `backward` identifies "Etc/GMT0" as a Link name targeting "Etc/GMT": 93 // Link Etc/GMT Etc/GMT0 94 ["Etc/GMT0", "UTC"] 95 ]; 96 97 for (const [timeZone, linkTarget] of timeZones) { 98 assert.sameValue( 99 new Intl.DateTimeFormat([], { timeZone }).resolvedOptions().timeZone, 100 timeZone, 101 "Time zone name " + timeZone + " should be preserved and not canonicalized to " + linkTarget 102 ); 103 } 104 105 reportCompare(0, 0);