1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 19:27:45 +00:00

LibJS: Add calendarName: "critical" option to toString() methods

This is a normative change in the Temporal spec.

See: e715a50
This commit is contained in:
Luke Wilde 2022-11-02 19:24:47 +00:00 committed by Linus Groh
parent 192aa75279
commit 4a167cfbec
9 changed files with 73 additions and 15 deletions

View file

@ -246,8 +246,8 @@ ThrowCompletionOr<String> to_temporal_offset(VM& vm, Object const* options, Stri
// 13.9 ToShowCalendarOption ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-toshowcalendaroption // 13.9 ToShowCalendarOption ( normalizedOptions ), https://tc39.es/proposal-temporal/#sec-temporal-toshowcalendaroption
ThrowCompletionOr<String> to_show_calendar_option(VM& vm, Object const& normalized_options) ThrowCompletionOr<String> to_show_calendar_option(VM& vm, Object const& normalized_options)
{ {
// 1. Return ? GetOption(normalizedOptions, "calendarName", "string", « "auto", "always", "never" », "auto"). // 1. Return ? GetOption(normalizedOptions, "calendarName", "string", « "auto", "always", "never", "critical" », "auto").
auto option = TRY(get_option(vm, normalized_options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv }, "auto"sv)); auto option = TRY(get_option(vm, normalized_options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv, "critical"sv }, "auto"sv));
VERIFY(option.is_string()); VERIFY(option.is_string());
return option.as_string().string(); return option.as_string().string();

View file

@ -579,19 +579,21 @@ ThrowCompletionOr<String> maybe_format_calendar_annotation(VM& vm, Object const*
// 12.2.27 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation // 12.2.27 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation
String format_calendar_annotation(StringView id, StringView show_calendar) String format_calendar_annotation(StringView id, StringView show_calendar)
{ {
// 1. Assert: showCalendar is "auto", "always", or "never". VERIFY(show_calendar == "auto"sv || show_calendar == "always"sv || show_calendar == "never"sv || show_calendar == "critical"sv);
VERIFY(show_calendar == "auto"sv || show_calendar == "always"sv || show_calendar == "never"sv);
// 2. If showCalendar is "never", return the empty String. // 1. If showCalendar is "never", return the empty String.
if (show_calendar == "never"sv) if (show_calendar == "never"sv)
return String::empty(); return String::empty();
// 3. If showCalendar is "auto" and id is "iso8601", return the empty String. // 2. If showCalendar is "auto" and id is "iso8601", return the empty String.
if (show_calendar == "auto"sv && id == "iso8601"sv) if (show_calendar == "auto"sv && id == "iso8601"sv)
return String::empty(); return String::empty();
// 4. Return the string-concatenation of "[u-ca=", id, and "]". // 3. If showCalendar is "critical", let flag be "!"; else, let flag be the empty String.
return String::formatted("[u-ca={}]", id); auto flag = show_calendar == "critical"sv ? "!"sv : ""sv;
// 4. Return the string-concatenation of "[", flag, "u-ca=", id, and "]".
return String::formatted("[{}u-ca={}]", flag, id);
} }
// 12.2.28 CalendarEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-calendarequals // 12.2.28 CalendarEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-calendarequals

View file

@ -186,8 +186,8 @@ ThrowCompletionOr<String> temporal_month_day_to_string(VM& vm, PlainMonthDay& mo
// 6. Let calendarID be ? ToString(monthDay.[[Calendar]]). // 6. Let calendarID be ? ToString(monthDay.[[Calendar]]).
auto calendar_id = TRY(Value(&month_day.calendar()).to_string(vm)); auto calendar_id = TRY(Value(&month_day.calendar()).to_string(vm));
// 7. If showCalendar is "always" or if calendarID is not "iso8601", then // 7. If showCalendar is one of "always" or "critical", or if calendarID is not "iso8601", then
if (show_calendar == "always"sv || calendar_id != "iso8601"sv) { if (show_calendar.is_one_of("always"sv, "critical"sv) || calendar_id != "iso8601"sv) {
// a. Let year be ! PadISOYear(monthDay.[[ISOYear]]). // a. Let year be ! PadISOYear(monthDay.[[ISOYear]]).
// b. Set result to the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and result. // b. Set result to the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and result.
result = String::formatted("{}-{}", pad_iso_year(month_day.iso_year()), result); result = String::formatted("{}-{}", pad_iso_year(month_day.iso_year()), result);

View file

@ -213,8 +213,8 @@ ThrowCompletionOr<String> temporal_year_month_to_string(VM& vm, PlainYearMonth&
// 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]). // 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]).
auto calendar_id = TRY(Value(&year_month.calendar()).to_string(vm)); auto calendar_id = TRY(Value(&year_month.calendar()).to_string(vm));
// 7. If showCalendar is "always" or if calendarID is not "iso8601", then // 7. If showCalendar is one of "always" or "critical", or if calendarID is not "iso8601", then
if (show_calendar == "always"sv || calendar_id != "iso8601") { if (show_calendar.is_one_of("always"sv, "critical"sv) || calendar_id != "iso8601") {
// a. Let day be ToZeroPaddedDecimalString(yearMonth.[[ISODay]], 2). // a. Let day be ToZeroPaddedDecimalString(yearMonth.[[ISODay]], 2).
// b. Set result to the string-concatenation of result, the code unit 0x002D (HYPHEN-MINUS), and day. // b. Set result to the string-concatenation of result, the code unit 0x002D (HYPHEN-MINUS), and day.
result = String::formatted("{}-{:02}", result, year_month.iso_day()); result = String::formatted("{}-{:02}", result, year_month.iso_day());

View file

@ -11,12 +11,14 @@ describe("correct behavior", () => {
expect(plainDate.toString({ calendarName: "auto" })).toBe("2021-07-06"); expect(plainDate.toString({ calendarName: "auto" })).toBe("2021-07-06");
expect(plainDate.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=iso8601]"); expect(plainDate.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=iso8601]");
expect(plainDate.toString({ calendarName: "never" })).toBe("2021-07-06"); expect(plainDate.toString({ calendarName: "never" })).toBe("2021-07-06");
expect(plainDate.toString({ calendarName: "critical" })).toBe("2021-07-06[!u-ca=iso8601]");
plainDate = new Temporal.PlainDate(2021, 7, 6, { toString: () => "foo" }); plainDate = new Temporal.PlainDate(2021, 7, 6, { toString: () => "foo" });
expect(plainDate.toString()).toBe("2021-07-06[u-ca=foo]"); expect(plainDate.toString()).toBe("2021-07-06[u-ca=foo]");
expect(plainDate.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]"); expect(plainDate.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]");
expect(plainDate.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]"); expect(plainDate.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]");
expect(plainDate.toString({ calendarName: "never" })).toBe("2021-07-06"); expect(plainDate.toString({ calendarName: "never" })).toBe("2021-07-06");
expect(plainDate.toString({ calendarName: "critical" })).toBe("2021-07-06[!u-ca=foo]");
plainDate = new Temporal.PlainDate(0, 1, 1); plainDate = new Temporal.PlainDate(0, 1, 1);
expect(plainDate.toString()).toBe("0000-01-01"); expect(plainDate.toString()).toBe("0000-01-01");
@ -62,7 +64,7 @@ describe("errors", () => {
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate");
}); });
test("calendarName option must be one of 'auto', 'always', 'never'", () => { test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {
const plainDate = new Temporal.PlainDate(2021, 7, 6); const plainDate = new Temporal.PlainDate(2021, 7, 6);
expect(() => { expect(() => {
plainDate.toString({ calendarName: "foo" }); plainDate.toString({ calendarName: "foo" });

View file

@ -80,6 +80,21 @@ describe("correct behavior", () => {
expect(plainDateTime.toString(options)).toBe("2022-08-08T14:38:40.1002003"); expect(plainDateTime.toString(options)).toBe("2022-08-08T14:38:40.1002003");
expect(calledToString).toBeFalse(); expect(calledToString).toBeFalse();
}); });
test("calendarName option", () => {
const plainDateTime = new Temporal.PlainDateTime(2022, 11, 2, 19, 4, 35, 100, 200, 300);
const values = [
["auto", "2022-11-02T19:04:35.1002003"],
["always", "2022-11-02T19:04:35.1002003[u-ca=iso8601]"],
["never", "2022-11-02T19:04:35.1002003"],
["critical", "2022-11-02T19:04:35.1002003[!u-ca=iso8601]"],
];
for (const [calendarName, expected] of values) {
const options = { calendarName };
expect(plainDateTime.toString(options)).toBe(expected);
}
});
}); });
describe("errors", () => { describe("errors", () => {
@ -88,4 +103,11 @@ describe("errors", () => {
Temporal.PlainDateTime.prototype.toString.call("foo"); Temporal.PlainDateTime.prototype.toString.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime"); }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
}); });
test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {
const plainDateTime = new Temporal.PlainDateTime(2022, 11, 2, 19, 5, 40, 100, 200, 300);
expect(() => {
plainDateTime.toString({ calendarName: "foo" });
}).toThrowWithMessage(RangeError, "foo is not a valid value for option calendarName");
});
}); });

View file

@ -11,12 +11,16 @@ describe("correct behavior", () => {
expect(plainMonthDay.toString({ calendarName: "auto" })).toBe("07-06"); expect(plainMonthDay.toString({ calendarName: "auto" })).toBe("07-06");
expect(plainMonthDay.toString({ calendarName: "always" })).toBe("1972-07-06[u-ca=iso8601]"); expect(plainMonthDay.toString({ calendarName: "always" })).toBe("1972-07-06[u-ca=iso8601]");
expect(plainMonthDay.toString({ calendarName: "never" })).toBe("07-06"); expect(plainMonthDay.toString({ calendarName: "never" })).toBe("07-06");
expect(plainMonthDay.toString({ calendarName: "critical" })).toBe(
"1972-07-06[!u-ca=iso8601]"
);
plainMonthDay = new Temporal.PlainMonthDay(7, 6, { toString: () => "foo" }, 2021); plainMonthDay = new Temporal.PlainMonthDay(7, 6, { toString: () => "foo" }, 2021);
expect(plainMonthDay.toString()).toBe("2021-07-06[u-ca=foo]"); expect(plainMonthDay.toString()).toBe("2021-07-06[u-ca=foo]");
expect(plainMonthDay.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]"); expect(plainMonthDay.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]");
expect(plainMonthDay.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]"); expect(plainMonthDay.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]");
expect(plainMonthDay.toString({ calendarName: "never" })).toBe("2021-07-06"); expect(plainMonthDay.toString({ calendarName: "never" })).toBe("2021-07-06");
expect(plainMonthDay.toString({ calendarName: "critical" })).toBe("2021-07-06[!u-ca=foo]");
}); });
}); });
@ -27,7 +31,7 @@ describe("errors", () => {
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainMonthDay"); }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainMonthDay");
}); });
test("calendarName option must be one of 'auto', 'always', 'never'", () => { test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {
const plainMonthDay = new Temporal.PlainMonthDay(7, 6); const plainMonthDay = new Temporal.PlainMonthDay(7, 6);
expect(() => { expect(() => {
plainMonthDay.toString({ calendarName: "foo" }); plainMonthDay.toString({ calendarName: "foo" });

View file

@ -13,12 +13,16 @@ describe("correct behavior", () => {
"2021-07-01[u-ca=iso8601]" "2021-07-01[u-ca=iso8601]"
); );
expect(plainYearMonth.toString({ calendarName: "never" })).toBe("2021-07"); expect(plainYearMonth.toString({ calendarName: "never" })).toBe("2021-07");
expect(plainYearMonth.toString({ calendarName: "critical" })).toBe(
"2021-07-01[!u-ca=iso8601]"
);
plainYearMonth = new Temporal.PlainYearMonth(2021, 7, { toString: () => "foo" }, 6); plainYearMonth = new Temporal.PlainYearMonth(2021, 7, { toString: () => "foo" }, 6);
expect(plainYearMonth.toString()).toBe("2021-07-06[u-ca=foo]"); expect(plainYearMonth.toString()).toBe("2021-07-06[u-ca=foo]");
expect(plainYearMonth.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]"); expect(plainYearMonth.toString({ calendarName: "auto" })).toBe("2021-07-06[u-ca=foo]");
expect(plainYearMonth.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]"); expect(plainYearMonth.toString({ calendarName: "always" })).toBe("2021-07-06[u-ca=foo]");
expect(plainYearMonth.toString({ calendarName: "never" })).toBe("2021-07-06"); expect(plainYearMonth.toString({ calendarName: "never" })).toBe("2021-07-06");
expect(plainYearMonth.toString({ calendarName: "critical" })).toBe("2021-07-06[!u-ca=foo]");
plainYearMonth = new Temporal.PlainYearMonth(0, 1); plainYearMonth = new Temporal.PlainYearMonth(0, 1);
expect(plainYearMonth.toString()).toBe("0000-01"); expect(plainYearMonth.toString()).toBe("0000-01");
@ -47,7 +51,7 @@ describe("errors", () => {
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth"); }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
}); });
test("calendarName option must be one of 'auto', 'always', 'never'", () => { test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7); const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
expect(() => { expect(() => {
plainYearMonth.toString({ calendarName: "foo" }); plainYearMonth.toString({ calendarName: "foo" });

View file

@ -125,6 +125,23 @@ describe("correct behavior", () => {
expect(zonedDateTime.toString(options)).toBe("2022-08-08T14:38:40.1002003+00:00[UTC]"); expect(zonedDateTime.toString(options)).toBe("2022-08-08T14:38:40.1002003+00:00[UTC]");
expect(calledToString).toBeFalse(); expect(calledToString).toBeFalse();
}); });
test("calendarName option", () => {
const plainDateTime = new Temporal.PlainDateTime(2022, 11, 2, 19, 4, 35, 100, 200, 300);
const timeZone = new Temporal.TimeZone("UTC");
const zonedDateTime = plainDateTime.toZonedDateTime(timeZone);
const values = [
["auto", "2022-11-02T19:04:35.1002003+00:00[UTC]"],
["always", "2022-11-02T19:04:35.1002003+00:00[UTC][u-ca=iso8601]"],
["never", "2022-11-02T19:04:35.1002003+00:00[UTC]"],
["critical", "2022-11-02T19:04:35.1002003+00:00[UTC][!u-ca=iso8601]"],
];
for (const [calendarName, expected] of values) {
const options = { calendarName };
expect(zonedDateTime.toString(options)).toBe(expected);
}
});
}); });
describe("errors", () => { describe("errors", () => {
@ -140,4 +157,11 @@ describe("errors", () => {
zonedDateTime.toString(); zonedDateTime.toString();
}).toThrowWithMessage(TypeError, "null is not a function"); }).toThrowWithMessage(TypeError, "null is not a function");
}); });
test("calendarName option must be one of 'auto', 'always', 'never', 'critical'", () => {
const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC");
expect(() => {
zonedDateTime.toString({ calendarName: "foo" });
}).toThrowWithMessage(RangeError, "foo is not a valid value for option calendarName");
});
}); });