Bug 1855748: Handle related year and year names when computing the resolved components. r=dminor

Map both "r" (related Gregorian year) and "U" (cyclic year name) to a resolved numeric year.

From <https://github.com/tc39/ecma402/issues/816#issuecomment-1667481214>:
> Per 11.2.3 Internal slots, "{relatedYear}" can only appear in a pattern string
> when the format record has a [[year]] field. And if a [[year]] field is present,
> CreateDateTimeFormat will set the Intl.DateTimeFormat's [[Year]] field to the
> format records [[year]] value. Intl.DateTimeFormat.prototype.resolvedOptions
> should then return the value of the [[Year]] internal slot.

Fixes <https://github.com/tc39/ecma402/issues/816>.

Differential Revision: https://phabricator.services.mozilla.com/D189542
This commit is contained in:
André Bargull 2023-11-05 12:27:04 +00:00
parent c9f06ab4c4
commit 113bfe78cc
2 changed files with 97 additions and 0 deletions

View File

@ -880,6 +880,12 @@ DateTimeFormat::ResolveComponents() {
numeric = Numeric::Numeric;
}
break;
// "numeric" cases
case u'r':
case u'U':
// Both are mapped to numeric years.
numeric = Numeric::Numeric;
break;
// "text & number" cases
case u'M':
case u'L':
@ -915,6 +921,8 @@ DateTimeFormat::ResolveComponents() {
bag.era = Some(text);
break;
case u'y':
case u'r':
case u'U':
bag.year = Some(numeric);
break;
case u'M':

View File

@ -31,4 +31,93 @@ function assertParts(df, x, expected) {
assertEq(parts[i].type, expected[i].type, "type mismatch at " + i);
assertEq(parts[i].value, expected[i].value, "value mismatch at " + i);
}
// Formatted parts must be consistent with the resolved options.
var resolvedOptions = df.resolvedOptions();
assertEq("dateStyle" in resolvedOptions, false, "dateStyle isn't yet supported here");
assertEq("timeStyle" in resolvedOptions, false, "timeStyle isn't yet supported here");
// Every formatted part must be in the resolved options.
for (var {type} of expected) {
switch (type) {
case "weekday":
case "era":
case "month":
case "day":
case "hour":
case "minute":
case "second":
case "timeZoneName":
assertEq(type in resolvedOptions, true, JSON.stringify(resolvedOptions));
break;
case "year":
case "yearName":
case "relatedYear":
assertEq("year" in resolvedOptions, true);
break;
case "dayPeriod":
assertEq("dayPeriod" in resolvedOptions || resolvedOptions.hour12 === true, true);
break;
case "fractionalSecond":
assertEq("fractionalSecondDigits" in resolvedOptions, true);
break;
case "unknown":
case "literal":
break;
default:
assertEq(true, false, `invalid part: ${type}`);
}
}
function includesType(...types) {
return parts.some(({type}) => types.includes(type));
}
// Every resolved option must be in the formatted parts.
for (var key of Object.keys(resolvedOptions)) {
switch (key) {
case "locale":
case "calendar":
case "numberingSystem":
case "timeZone":
case "hourCycle":
// Skip over non-pattern keys.
break;
case "hour12":
if (resolvedOptions.hour12) {
assertEq(includesType("dayPeriod"), true);
}
break;
case "weekday":
case "era":
case "month":
case "day":
case "dayPeriod":
case "hour":
case "minute":
case "second":
case "timeZoneName":
assertEq(includesType(key), true);
break;
case "year":
assertEq(includesType("year", "yearName", "relatedYear"), true);
break;
case "fractionalSecondDigits":
assertEq(includesType("fractionalSecond"), true);
break;
default:
assertEq(true, false, `invalid key: ${key}`);
}
}
}