New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Normative: specify time zone ID requirements to reduce divergence between engines #877
base: main
Are you sure you want to change the base?
Conversation
aa65347
to
3ea9cee
Compare
3ea9cee
to
6a544eb
Compare
Thanks for putting this together @justingrant! We can discuss this at the next TG2 meeting. In the mean time, I encourage the listed reviewers to take a look. |
It's fine to submit tests to test262 that no engine can pass yet, as long as they are correct according to the current snapshot of ECMA-262 or 402 or a Stage 3 proposal. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel qualified to review the details — I mostly haven't followed those discussions. At a general level, this all looks very reasonable.
c78de1d
to
3bb6dee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly editorial comments, but I am not comfortable with making the rename waiting period mandatory.
I assuem you mean to say "e.g. PST8PDT=>America/Los_Angeles" ? |
This PR resolves tc39#825 by adding spec text that defines how ECMA-402 implementations should decide which IANA time zone IDs should be primary vs. non-primary. This PR implements "Option C" in tc39#825 by deterministically defining ECMAScript's exceptions from the IANA Time Zone Database's defaults, and then pointing implementers at ICU as a convenient implementation of those exceptions. This PR also accommodates to web reality by aligning the 402 spec text with the existing behavior of ICU. This PR is stacked on top of tc39#876.
b4a630c
to
28d8a5a
Compare
28d8a5a
to
116ec3b
Compare
I just pushed a new commit that includes what I think resolves all review feedback. @gibson042 @sffc (and anyone else who's interested) do you want to re-review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
10 editorial suggestions and one request for better specification.
spec/locales-currencies-tz.html
Outdated
1. If _identifierCountryCode_ is _zoneCountryCode_, then | ||
1. Set _primary_ to _zone_. | ||
1. Else, | ||
1. Let _countryCodeLine_ be the line in file <code>zone.tab</code> of the IANA Time Zone Database where the "country-code" column is _identifierCountryCode_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of right now, 31 codes have more than one line in zone.tab:
$ curl -sSL 'https://github.com/eggert/tz/raw/main/zone.tab' \
| awk '{ if (match($1, "^[A-Z][A-Z]$")) print $1; }' \
| sort \
| uniq -c \
| awk '$1 > 1 { print; i++; } END { print i }'
10 AQ
12 AR
12 AU
16 BR
23 CA
2 CD
3 CL
2 CN
2 CY
2 DE
2 EC
3 ES
3 FM
4 GL
4 ID
3 KI
7 KZ
2 MH
3 MN
12 MX
2 MY
2 NZ
3 PF
2 PG
2 PS
3 PT
26 RU
2 UA
2 UM
29 US
2 UZ
31
For example, America/Blanc-Sablon (CA) is a Link to America/Puerto_Rico (PR), but CA has a total of 23 lines in zone.tab (including one that is specifically for America/Blanc-Sablon), and similar arrangements affect regions as populous as Africa/Kinshasa and Asia/Kuala_Lumpur. So this algorithm is underspecified. What is the actual intent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh, shoot. Yeah, we'll need a better solution, thanks for catching this.
The problem to solve here is multi-hop Links like A => B => C where A and B are in the the same country but C is in a different country. This happens when an earlier TZDB release created the A => B Link, then a later release folded B into C, so the link that used to be A => B now was changed to A => C (along with a new Link for B => C).
There are 15 cases like this, conveniently separated into three cases of 5 each.
Case 1: A is in zone.tab
- America/Kralendijk ⇨ America/Curacao ⇨ America/Puerto_Rico
- America/Lower_Princes ⇨ America/Curacao ⇨ America/Puerto_Rico
- America/Marigot ⇨ America/Port_of_Spain ⇨ America/Puerto_Rico
- America/St_Barthelemy ⇨ America/Port_of_Spain ⇨ America/Puerto_Rico
- Arctic/Longyearbyen ⇨ Europe/Oslo ⇨ Europe/Berlin
Case 2: A's country code has only one line in zone.tab
- America/Virgin ⇨ America/St_Thomas ⇨ America/Puerto_Rico
- Atlantic/Jan_Mayen ⇨ Europe/Oslo ⇨ Europe/Berlin
- Iceland ⇨ Atlantic/Reykjavik ⇨ Africa/Abidjan
- Africa/Asmera ⇨ Africa/Asmara ⇨ Africa/Nairobi
- Africa/Timbuktu ⇨ Africa/Bamako ⇨ Africa/Abidjan
Case 3: A's country code has multiple lines in zone.tab (broken case in current PR's spec text)
- (FM) Pacific/Ponape ⇨ Pacific/Pohnpei ⇨ Pacific/Guadalcanal
- (FM) Pacific/Truk ⇨ Pacific/Chuuk ⇨ Pacific/Port_Moresby
- (FM) Pacific/Yap ⇨ Pacific/Chuuk ⇨ Pacific/Port_Moresby
- (CA) America/Coral_Harbour ⇨ America/Atikokan ⇨ America/Panama
- (AQ) Antarctica/South_Pole ⇨ Antarctica/McMurdo ⇨ Pacific/Auckland
For that last case, the only way to know what B was is to look in the backzone
file in TZDB. The old Links are listed there.
I'll draft some spec text to handle that case, and will let you know once I push it because I'm sure it will require a round or two of polishing. I suspect that the cleanest solution will be to just to use backzone
for both the second and third cases above, and remove the complex spec text to handle the second case using zone.tab.
Thanks again for catching this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gibson042 I just pushed a solution to the problem you found, as well as fixing (I think) all the other review feedback you had except the quote characters. Want to review the latest?
For the cases above, it turned out that there wasn't a single algorithmic solution possible, so I used the existing PR's algorithm for Case 2 above, and used backzone
for Case 3.
Sadly, we can't use backzone
for both cases 2 and 3 because a single A => B => C chain in backzone
requires the zone.tab algorithm. The problematic identifier is Atlantic/Jan_Mayen. This ID is linked to Europe/Oslo (country code "NO") in backzone
, but it should be linked to Arctic/Longyearbyen which has the same country code ("SJ") as Atlantic/Jan_Mayen.
Let me know what you think of the latest commit.
spec/locales-currencies-tz.html
Outdated
1. Let _zone_ be the Zone name that _identifier_ resolves to, according to the rules for resolving Link names in the IANA Time Zone Database. | ||
1. If _zone_ starts with *"Etc/"*, then | ||
1. Set _primary_ to _zone_. | ||
1. Else, | ||
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_. | ||
1. Let _zoneCountryCode_ be the ISO 3166-1 Alpha-2 country code whose territory contains the geographical area corresponding to _zone_. | ||
1. If _identifierCountryCode_ is _zoneCountryCode_, then | ||
1. Set _primary_ to _zone_. | ||
1. Else, | ||
1. Let _countryCodeLine_ be the line in file <code>zone.tab</code> of the IANA Time Zone Database where the "country-code" column is _identifierCountryCode_. | ||
1. Set _primary_ to the contents of the "TZ" column of _countryCodeLine_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole block can be simplified a bit.
1. Let _zone_ be the Zone name that _identifier_ resolves to, according to the rules for resolving Link names in the IANA Time Zone Database. | |
1. If _zone_ starts with *"Etc/"*, then | |
1. Set _primary_ to _zone_. | |
1. Else, | |
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_. | |
1. Let _zoneCountryCode_ be the ISO 3166-1 Alpha-2 country code whose territory contains the geographical area corresponding to _zone_. | |
1. If _identifierCountryCode_ is _zoneCountryCode_, then | |
1. Set _primary_ to _zone_. | |
1. Else, | |
1. Let _countryCodeLine_ be the line in file <code>zone.tab</code> of the IANA Time Zone Database where the "country-code" column is _identifierCountryCode_. | |
1. Set _primary_ to the contents of the "TZ" column of _countryCodeLine_. | |
1. Set _primary_ to the Zone name that _identifier_ resolves to, according to the rules for resolving Link names in the IANA Time Zone Database. | |
1. If _primary_ does not start with *"Etc/"*, then | |
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_. | |
1. Let _zoneCountryCode_ be the ISO 3166-1 Alpha-2 country code whose territory contains the geographical area corresponding to _primary_. | |
1. If _identifierCountryCode_ is not _zoneCountryCode_, then | |
1. Let _countryCodeLine_ be the line in file <code>zone.tab</code> of the IANA Time Zone Database where the “country-code” column is _identifierCountryCode_. | |
1. Set _primary_ to the contents of the “TZ” column of _countryCodeLine_. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want to take a look at the latest commit and let me know if you think that it's simplified enough?
48ecaec
to
0810433
Compare
0810433
to
b458ba3
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The algorithm looks good, but I'd like to use more precise phrasing than «the first, space-delimited column in backzoneLinkLine after…».
spec/locales-currencies-tz.html
Outdated
1. Let _backzoneLinkLines_ be the subset of lines in the file <code>backzone</code> of the IANA Time Zone Database that start with either *"Link"* or *"#PACKRATLIST zone.tab Link"*. | ||
1. Assert: Exactly one line in _backzoneLinkLines_ contains _identifier_ in the second, space-delimited column after *"Link"* or *"#PACKRATLIST zone.tab Link"*. | ||
1. Let _backzoneLinkLine_ be the line in _backzoneLinkLines_ that contains _identifier_ in the second, space-delimited column after *"Link"* or *"#PACKRATLIST zone.tab Link"*. | ||
1. Set _primary_ to the contents of the first, space-delimited column in _backzoneLinkLine_ after *"Link"* or *"#PACKRATLIST zone.tab Link"*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've wanted a StringSplit operation for a while (IsWellFormedUnitIdentifier is practically begging for it), but in the absence of such I'd like to be crystal-clear in this kind of algorithm.
1. Let _backzoneLinkLines_ be the subset of lines in the file <code>backzone</code> of the IANA Time Zone Database that start with either *"Link"* or *"#PACKRATLIST zone.tab Link"*. | |
1. Assert: Exactly one line in _backzoneLinkLines_ contains _identifier_ in the second, space-delimited column after *"Link"* or *"#PACKRATLIST zone.tab Link"*. | |
1. Let _backzoneLinkLine_ be the line in _backzoneLinkLines_ that contains _identifier_ in the second, space-delimited column after *"Link"* or *"#PACKRATLIST zone.tab Link"*. | |
1. Set _primary_ to the contents of the first, space-delimited column in _backzoneLinkLine_ after *"Link"* or *"#PACKRATLIST zone.tab Link"*. | |
1. Let _backzone_ be *undefined*. | |
1. 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 "*. | |
1. For each element _line_ of _backzoneLinkLines_, do | |
1. If _line_ starts with *"#PACKRATLIST zone.tab "*, set _line_ to the substring of _line_ from 22. | |
1. Assert: _line_ starts with *"Link "*. | |
1. Let _i_ be 5. | |
1. Let _j_ be StringIndexOf(_line_, *" "*, _i_). | |
1. Assert: _j_ is not ~not-found~ and _j_ > _i_. | |
1. Let _k_ be StringIndexOf(_line_, *" "*, _j_ + 1). | |
1. If _k_ is ~not-found~, set _k_ to the length of _line_. | |
1. Assert: _k_ > _j_ + 1. | |
1. Let _alias_ be the substring of _line_ from _j_ + 1 to _k_. | |
1. If _alias_ is _identifier_, then | |
1. Assert: _backzone_ is *undefined*. | |
1. Set _backzone_ to the substring of _line_ from _i_ to _j_. | |
1. Assert: _backzone_ is not *undefined*. | |
1. Set _primary_ to _backzone_. |
A worked example of e.g. Pacific/Truk against current tzdata in an editorial note would also be helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wow that's hard to read. I wonder if it'd be better to introduce a string split AO into 402 in this PR and use it in this PR, then it could be re-used elsewhere in 402 later?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the commit I just pushed that includes a minimal string-split AO, borrowed from the lower half of the spec text of String.prototype.split
.
A worked example of e.g. Pacific/Truk against current tzdata in an editorial note would also be helpful.
When you say "worked example" what do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Take a look at the commit I just pushed that includes a minimal string-split AO, borrowed from the lower half of the spec text of
String.prototype.split
.
And while we're at it, I refactored IsWellFormedUnitIdentifier
to use the new AO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you say "worked example" what do you mean?
A note explaining something like this:
This algorithm attempts to resolve Links to primary time zone identifiers without crossing the boundaries of ISO 3166-1 Alpha-2 country codes, using data from files
zone.tab
andbackzone
of the IANA Time Zone Database as necessary.
For example, if "Pacific/Truk" (in country code "FM") is a Link to "Pacific/Port_Moresby" (in country code "PG") with default configuration, thenzone.tab
will be checked for lines corresponding with country code "FM". If there is only one such line, then the Zone of that line will be treated as the primary time zone identifier associated with "Pacific/Truk". But otherwise, that data will be taken from the unique Link inbackzone
with source name "Pacific/Truk" (e.g., line "Link Pacific/Chuuk Pacific/Truk" will result in primary time zone identifier "Pacific/Chuuk").
(or substitute any other example that requires use of backzone
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good! Some final tweaks to StringSplitToList and its use, then I think this is ready.
<h1> | ||
StringSplitToList ( | ||
_S_: a String, | ||
_separator_: a String that is not the empty String, | ||
_limit_: a mathematical value in the range 1 to 2<sup>32</sup> - 1 | ||
): a List of Strings | ||
</h1> | ||
<dl class="header"> | ||
<dt>description</dt> | ||
<dd> | ||
The returned List contains substrings of _S_. | ||
These substrings are determined by searching from left to right for occurrences of _separator_; these occurrences are not part of any String in the returned List, but serve to divide _S_ into substrings. | ||
The output List will contain no more than _limit_ elements; any additional separators and/or substrings present in _S_ will be ignored. | ||
</dd> | ||
</dl> | ||
<emu-alg> | ||
1. If _S_ is the empty String, return « ». | ||
1. Let _separatorLength_ be the length of _separator_. | ||
1. Assert: _separatorLength_ is not 0. | ||
1. Let _substrings_ be a new empty List. | ||
1. Let _i_ be 0. | ||
1. Let _j_ be StringIndexOf(_S_, _separator_, 0). | ||
1. Repeat, while _j_ ≠ -1, | ||
1. Let _T_ be the substring of _S_ from _i_ to _j_. | ||
1. Append _T_ to _substrings_. | ||
1. If the number of elements in _substrings_ is _limit_, return _substrings_. | ||
1. Set _i_ to _j_ + _separatorLength_. | ||
1. Set _j_ to StringIndexOf(_S_, _separator_, _i_). | ||
1. Let _T_ be the substring of _S_ from _i_. | ||
1. Append _T_ to _substrings_. | ||
1. Return _substrings_. | ||
</emu-alg> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks; I love seeing this! But let's keep it super narrow for now to support evolution of e.g. limit (which really should limit the search for separators rather than truncating the results) and empty string handling.
<h1> | |
StringSplitToList ( | |
_S_: a String, | |
_separator_: a String that is not the empty String, | |
_limit_: a mathematical value in the range 1 to 2<sup>32</sup> - 1 | |
): a List of Strings | |
</h1> | |
<dl class="header"> | |
<dt>description</dt> | |
<dd> | |
The returned List contains substrings of _S_. | |
These substrings are determined by searching from left to right for occurrences of _separator_; these occurrences are not part of any String in the returned List, but serve to divide _S_ into substrings. | |
The output List will contain no more than _limit_ elements; any additional separators and/or substrings present in _S_ will be ignored. | |
</dd> | |
</dl> | |
<emu-alg> | |
1. If _S_ is the empty String, return « ». | |
1. Let _separatorLength_ be the length of _separator_. | |
1. Assert: _separatorLength_ is not 0. | |
1. Let _substrings_ be a new empty List. | |
1. Let _i_ be 0. | |
1. Let _j_ be StringIndexOf(_S_, _separator_, 0). | |
1. Repeat, while _j_ ≠ -1, | |
1. Let _T_ be the substring of _S_ from _i_ to _j_. | |
1. Append _T_ to _substrings_. | |
1. If the number of elements in _substrings_ is _limit_, return _substrings_. | |
1. Set _i_ to _j_ + _separatorLength_. | |
1. Set _j_ to StringIndexOf(_S_, _separator_, _i_). | |
1. Let _T_ be the substring of _S_ from _i_. | |
1. Append _T_ to _substrings_. | |
1. Return _substrings_. | |
</emu-alg> | |
<h1> | |
StringSplitToList ( | |
_S_: a String, | |
_separator_: a String, | |
): a List of Strings | |
</h1> | |
<dl class="header"> | |
<dt>description</dt> | |
<dd> | |
The returned List contains all disjoint substrings of _S_ that do not contain _separator_ but are immediately preceded and/or immediately followed by an occurrence of _separator_. Each such <emu-not-ref>substring</emu-not-ref> will be the empty String in between adjacent occurrences of _separator_, before a _separator_ at the very start of _S_, or after a _separator_ at the very end of _S_, but otherwise will not be empty. | |
</dd> | |
</dl> | |
<emu-alg> | |
1. Assert: _S_ is not the empty String. | |
1. Assert: _separator_ is not the empty String. | |
1. Let _separatorLength_ be the length of _separator_. | |
1. Let _substrings_ be a new empty List. | |
1. Let _i_ be 0. | |
1. Let _j_ be StringIndexOf(_S_, _separator_, 0). | |
1. Repeat, while _j_ is not ~not-found~, | |
1. Let _T_ be the substring of _S_ from _i_ to _j_. | |
1. Append _T_ to _substrings_. | |
1. Set _i_ to _j_ + _separatorLength_. | |
1. Set _j_ to StringIndexOf(_S_, _separator_, _i_). | |
1. Let _T_ be the substring of _S_ from _i_. | |
1. Append _T_ to _substrings_. | |
1. Return _substrings_. | |
</emu-alg> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But let's keep it super narrow for now to support evolution of e.g. limit (which really should limit the search for separators rather than truncating the results) and empty string handling.
I'm happy to remove limit, but could you explain more about "should limit the search for separators rather than truncating the results"? Do you mean that limit should cause the last substring to contain any remaining content in the string, even if there are separators in it?
I'm asking because the two cases in the spec so far both perform well using truncation because both of those cases only care about the first few substrings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, if we wanted to promote this AO to 262, String.prototype.split
uses truncation so a "rest" result wouldn't work for that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean that limit should cause the last substring to contain any remaining content in the string, even if there are separators in it?
Yes, that is my position and IIRC there is regret on the committee that String.prototype.split
itself does not work like that (as it does e.g. Python str.split
).
the two cases in the spec so far both perform well using truncation because both of those cases only care about the first few substrings.
I agree, but am not confident that will remain the case and don't want to paint this into a corner. Anything that can be specified with truncation can also be specified without it, and implementations are not obligated to perform unobservable steps (which can even be reiterated in a note if we're concerned about it).
Also, if we wanted to promote this AO to 262,
String.prototype.split
uses truncation so a "rest" result wouldn't work for that case.
No, it would be fine as e.g.
1. Let _substrings_ be StringSplitToList(_S_, _R_, _lim_).
1. If the number of elements in _substrings_ > _lim_, remove the last element from _substrings_.
1. Assert: the number of elements in _substrings_ ≤ _lim_.
1. Return CreateArrayFromList(_substrings_).
or even (with a new maxLength parameter for CreateArrayFromList)
1. Let _substrings_ be StringSplitToList(_S_, _R_, _lim_).
1. Return CreateArrayFromList(_substrings_, _lim_).
<emu-note> | ||
<p> | ||
Substrings in the returned List may be the empty String if _S_ starts or ends with _separator_ or if _S_ contains adjacent occurrences of _separator_. | ||
</p> | ||
</emu-note> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<emu-note> | |
<p> | |
Substrings in the returned List may be the empty String if _S_ starts or ends with _separator_ or if _S_ contains adjacent occurrences of _separator_. | |
</p> | |
</emu-note> |
1. Let _backzone_ be *undefined*. | ||
1. 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 "*. | ||
1. For each element _line_ of _backzoneLinkLines_, do | ||
1. If _line_ starts with *"#PACKRATLIST zone.tab "*, set _line_ to the substring of _line_ from 22. | ||
1. Assert: _line_ starts with *"Link "*. | ||
1. Set _line_ to the substring of _line_ from 5. | ||
1. Let _backzoneAndLink_ be StringSplitToList(_line_, *" "*, 2). | ||
1. Assert: _backzoneAndLink_ has exactly two elements. | ||
1. If _backzoneAndLink_[1] is _identifier_, then | ||
1. Assert: _backzone_ is *undefined*. | ||
1. Set _primary_ to _backzoneAndLink_[0]. | ||
1. Assert: _backzone_ is not *undefined*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1. Let _backzone_ be *undefined*. | |
1. 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 "*. | |
1. For each element _line_ of _backzoneLinkLines_, do | |
1. If _line_ starts with *"#PACKRATLIST zone.tab "*, set _line_ to the substring of _line_ from 22. | |
1. Assert: _line_ starts with *"Link "*. | |
1. Set _line_ to the substring of _line_ from 5. | |
1. Let _backzoneAndLink_ be StringSplitToList(_line_, *" "*, 2). | |
1. Assert: _backzoneAndLink_ has exactly two elements. | |
1. If _backzoneAndLink_[1] is _identifier_, then | |
1. Assert: _backzone_ is *undefined*. | |
1. Set _primary_ to _backzoneAndLink_[0]. | |
1. Assert: _backzone_ is not *undefined*. | |
1. Let _backzone_ be *undefined*. | |
1. 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 "*. | |
1. For each element _line_ of _backzoneLinkLines_, do | |
1. Let _i_ be StringIndexOf(_line_, *"Link"*, 0). | |
1. Set _line_ to the substring of _line_ from _i_. | |
1. Let _fields_ be StringSplitToList(_line_, *" "*). | |
1. Assert: _fields_ has at least three elements, _fields_[0] is *"Link"*, _fields_[1] is not the empty String, and _fields_[2] is not the empty String. | |
1. If _fields_[2] is _identifier_, then | |
1. Assert: _backzone_ is *undefined*. | |
1. Set _backzone_ to _fields_[1]. | |
1. Assert: _backzone_ is not *undefined*. | |
1. Set _primary_ to _backzone_. |
This proposed change resolves #825 by adding normative spec text to clarify how ECMA-402 implementations should decide which IANA time zone IDs should be primary vs. non-primary. This will enable more consistency between ECMAScript implementations and prevent future divergence.
This PR also accommodates to web reality by aligning ECMA-402 with CLDR and ICU. This should make it easier for all ECMAScript engines to comply with the spec while still being able to use ICU.
This PR is stacked on #876, so please ignore the first commit when reviewing this PR.
Note that the problem of out-of-date primary IDs for renamed Zones (like Asia/Calcutta) is out of scope to this PR, because the spec already requires current IDs, and there's already a plan to fix it that requires no spec changes.
Summary of proposed changes
This PR implements "Option C" in #825 by deterministically defining ECMAScript's exceptions from the IANA Time Zone Database's defaults, and then pointing implementers at ICU as a convenient implementation of those exceptions.
We'll start with a baseline of IANA's Zone and Link names and specify a few exceptions:
This PR also makes two other smaller normative text changes that we expect to have zero impact on current engines:
Per-engine changes required
Implementing the changes in this PR will impact JS engines differently, given the current divergence between engines:
For V8 (cc @FrankYFTang) and JSC (cc @Constellation), requirements 1-3 above are already how these engines behave, and (4) should be simple to implement. Note that this PR doesn't affect the plan to fix out-of-date canonicalizations like Asia/Calcutta and Europe/Kiev. This plan is unchanged: as part of landing Temporal Stage 4, switch to use ICU's new
icu::TimeZone::getIanaID()
, which returns the latest IANA IDs instead of out-of-date canonical IDs like Asia/Calcutta.For SpiderMonkey (cc @anba), more changes are needed because currently SpiderMonkey conforms to the spec which requires using
backward
in TZDB to determine canonicalization. SM could useicu::TimeZone::getIanaID()
to implement (2) and (3) above, or could implement the same behavior by reading CLDR data or IANA data directly. Also, this PR will reduce SM's differences inIntl.supportedValuesOf('timeZone')
vs. V8/JSC.Testing
Test262 changes will be needed to validate these normative changes, but I'm not sure how we can run those tests except using the Temporal polyfill. @ptomato I'll be looking for your advice (and perhaps help writing tests!) on this point.
Feedback requested
Feedback is welcome on any part of this proposal, but I'm most interested in making sure that the spec text actually accomplishes what the summary above claims that it does.