diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp index 4693f07ae5..97e4610d62 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.cpp @@ -502,7 +502,21 @@ ThrowCompletionOr calendar_month_day_from_fields(VM& vm, Object& return static_cast(month_day_object); } -// 12.2.26 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation +// 12.2.26 MaybeFormatCalendarAnnotation ( calendarObject, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-maybeformatcalendarannotation +ThrowCompletionOr maybe_format_calendar_annotation(VM& vm, Value calendar_object, StringView show_calendar) +{ + // 1. If showCalendar is "never", return the empty String. + if (show_calendar == "never"sv) + return String::empty(); + + // 2. Let calendarID be ? ToString(calendarObject). + auto calendar_id = TRY(calendar_object.to_string(vm)); + + // 3. Return FormatCalendarAnnotation(calendarID, showCalendar). + return format_calendar_annotation(calendar_id, show_calendar); +} + +// 12.2.27 FormatCalendarAnnotation ( id, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-formatcalendarannotation String format_calendar_annotation(StringView id, StringView show_calendar) { // 1. Assert: showCalendar is "auto", "always", or "never". @@ -520,7 +534,7 @@ String format_calendar_annotation(StringView id, StringView show_calendar) return String::formatted("[u-ca={}]", id); } -// 12.2.27 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 ThrowCompletionOr calendar_equals(VM& vm, Object& one, Object& two) { // 1. If one and two are the same Object value, return true. @@ -541,7 +555,7 @@ ThrowCompletionOr calendar_equals(VM& vm, Object& one, Object& two) return false; } -// 12.2.28 ConsolidateCalendars ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-consolidatecalendars +// 12.2.29 ConsolidateCalendars ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-consolidatecalendars ThrowCompletionOr consolidate_calendars(VM& vm, Object& one, Object& two) { // 1. If one and two are the same Object value, return two. @@ -570,7 +584,7 @@ ThrowCompletionOr consolidate_calendars(VM& vm, Object& one, Object& tw return vm.throw_completion(ErrorType::TemporalInvalidCalendar); } -// 12.2.29 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth +// 12.2.30 ISODaysInMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isodaysinmonth u8 iso_days_in_month(i32 year, u8 month) { // 1. Assert: year is an integer. @@ -590,7 +604,7 @@ u8 iso_days_in_month(i32 year, u8 month) return 28 + JS::in_leap_year(time_from_year(year)); } -// 12.2.30 ToISOWeekOfYear ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisoweekofyear +// 12.2.31 ToISOWeekOfYear ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-toisoweekofyear u8 to_iso_week_of_year(i32 year, u8 month, u8 day) { // 1. Assert: year is an integer. @@ -625,7 +639,7 @@ u8 to_iso_week_of_year(i32 year, u8 month, u8 day) return week; } -// 12.2.31 BuildISOMonthCode ( month ), https://tc39.es/proposal-temporal/#sec-buildisomonthcode +// 12.2.32 BuildISOMonthCode ( month ), https://tc39.es/proposal-temporal/#sec-buildisomonthcode String build_iso_month_code(u8 month) { // 1. Let numberPart be ToZeroPaddedDecimalString(month, 2). @@ -633,7 +647,7 @@ String build_iso_month_code(u8 month) return String::formatted("M{:02}", month); } -// 12.2.32 ResolveISOMonth ( fields ), https://tc39.es/proposal-temporal/#sec-temporal-resolveisomonth +// 12.2.33 ResolveISOMonth ( fields ), https://tc39.es/proposal-temporal/#sec-temporal-resolveisomonth ThrowCompletionOr resolve_iso_month(VM& vm, Object const& fields) { // 1. Assert: fields is an ordinary object with no more and no less than the own data properties listed in Table 13. @@ -694,7 +708,7 @@ ThrowCompletionOr resolve_iso_month(VM& vm, Object const& fields) return number_part_integer; } -// 12.2.33 ISODateFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isodatefromfields +// 12.2.34 ISODateFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isodatefromfields ThrowCompletionOr iso_date_from_fields(VM& vm, Object const& fields, Object const& options) { // 1. Assert: Type(fields) is Object. @@ -724,7 +738,7 @@ ThrowCompletionOr iso_date_from_fields(VM& vm, Object const& fiel return regulate_iso_date(vm, year.as_double(), month, day.as_double(), overflow); } -// 12.2.34 ISOYearMonthFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthfromfields +// 12.2.35 ISOYearMonthFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthfromfields ThrowCompletionOr iso_year_month_from_fields(VM& vm, Object const& fields, Object const& options) { // 1. Assert: Type(fields) is Object. @@ -751,7 +765,7 @@ ThrowCompletionOr iso_year_month_from_fields(VM& vm, Object const& return ISOYearMonth { .year = result.year, .month = result.month, .reference_iso_day = 1 }; } -// 12.2.35 ISOMonthDayFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthdayfromfields +// 12.2.36 ISOMonthDayFromFields ( fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthdayfromfields ThrowCompletionOr iso_month_day_from_fields(VM& vm, Object const& fields, Object const& options) { // 1. Assert: Type(fields) is Object. @@ -809,7 +823,7 @@ ThrowCompletionOr iso_month_day_from_fields(VM& vm, Object const& f return ISOMonthDay { .month = result->month, .day = result->day, .reference_iso_year = reference_iso_year }; } -// 12.2.36 ISOYear ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isoyear +// 12.2.37 ISOYear ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isoyear i32 iso_year(Object& temporal_object) { // 1. Assert: temporalObject has an [[ISOYear]] internal slot. @@ -827,7 +841,7 @@ i32 iso_year(Object& temporal_object) VERIFY_NOT_REACHED(); } -// 12.2.37 ISOMonth ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonth +// 12.2.38 ISOMonth ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonth u8 iso_month(Object& temporal_object) { // 1. Assert: temporalObject has an [[ISOMonth]] internal slot. @@ -845,7 +859,7 @@ u8 iso_month(Object& temporal_object) VERIFY_NOT_REACHED(); } -// 12.2.38 ISOMonthCode ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode +// 12.2.39 ISOMonthCode ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode String iso_month_code(Object& temporal_object) { // 1. Assert: temporalObject has an [[ISOMonth]] internal slot. @@ -863,7 +877,7 @@ String iso_month_code(Object& temporal_object) VERIFY_NOT_REACHED(); } -// 12.2.49 ISODay ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode +// 12.2.40 ISODay ( temporalObject ), https://tc39.es/proposal-temporal/#sec-temporal-isomonthcode u8 iso_day(Object& temporal_object) { // 1. Assert: temporalObject has an [[ISODay]] internal slot. @@ -881,7 +895,7 @@ u8 iso_day(Object& temporal_object) VERIFY_NOT_REACHED(); } -// 12.2.40 DefaultMergeCalendarFields ( fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-defaultmergecalendarfields +// 12.2.41 DefaultMergeCalendarFields ( fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal-defaultmergecalendarfields ThrowCompletionOr default_merge_calendar_fields(VM& vm, Object const& fields, Object const& additional_fields) { auto& realm = *vm.current_realm(); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h index 13045fd604..568239b3be 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Calendar.h @@ -59,6 +59,7 @@ ThrowCompletionOr get_temporal_calendar_with_iso_default(VM&, Object&); ThrowCompletionOr calendar_date_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr); ThrowCompletionOr calendar_year_month_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr); ThrowCompletionOr calendar_month_day_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr); +ThrowCompletionOr maybe_format_calendar_annotation(VM&, Value calendar_object, StringView show_calendar); String format_calendar_annotation(StringView id, StringView show_calendar); ThrowCompletionOr calendar_equals(VM&, Object& one, Object& two); ThrowCompletionOr consolidate_calendars(VM&, Object& one, Object& two); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp index 946690dc4e..b54133dd20 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp @@ -427,13 +427,10 @@ ThrowCompletionOr temporal_date_to_string(VM& vm, PlainDate& temporal_da // 5. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2). auto day = String::formatted("{:02}", temporal_date.iso_day()); - // 6. Let calendarID be ? ToString(temporalDate.[[Calendar]]). - auto calendar_id = TRY(Value(&temporal_date.calendar()).to_string(vm)); + // 6. Let calendar be ? MaybeFormatCalendarAnnotation(temporalDate.[[Calendar]], showCalendar). + auto calendar = TRY(maybe_format_calendar_annotation(vm, &temporal_date.calendar(), show_calendar)); - // 7. Let calendar be ! FormatCalendarAnnotation(calendarID, showCalendar). - auto calendar = format_calendar_annotation(calendar_id, show_calendar); - - // 8. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar. + // 7. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar. return String::formatted("{}-{}-{}{}", year, month, day, calendar); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp index ae43066cc8..91d6be9a3b 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp @@ -281,13 +281,10 @@ ThrowCompletionOr temporal_date_time_to_string(VM& vm, i32 iso_year, u8 // 7. Let seconds be ! FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision). auto seconds = format_seconds_string_part(second, millisecond, microsecond, nanosecond, precision); - // 8. Let calendarID be ? ToString(calendar). - auto calendar_id = TRY(calendar.to_string(vm)); + // 8. Let calendarString be ? MaybeFormatCalendarAnnotation(calendar, showCalendar). + auto calendar_string = TRY(maybe_format_calendar_annotation(vm, calendar, show_calendar)); - // 9. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar). - auto calendar_string = format_calendar_annotation(calendar_id, show_calendar); - - // 10. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, 0x0054 (LATIN CAPITAL LETTER T), hour, the code unit 0x003A (COLON), minute, seconds, and calendarString. + // 9. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, 0x0054 (LATIN CAPITAL LETTER T), hour, the code unit 0x003A (COLON), minute, seconds, and calendarString. return String::formatted("{}-{:02}-{:02}T{:02}:{:02}{}{}", pad_iso_year(iso_year), iso_month, iso_day, hour, minute, seconds, calendar_string); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp index 96a9acd1bf..c1d90b1d59 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp @@ -351,13 +351,10 @@ ThrowCompletionOr temporal_zoned_date_time_to_string(VM& vm, ZonedDateTi time_zone_string = String::formatted("[{}]", time_zone_id); } - // 14. Let calendarID be ? ToString(zonedDateTime.[[Calendar]]). - auto calendar_id = TRY(Value(&zoned_date_time.calendar()).to_string(vm)); + // 14. Let calendarString be ? MaybeFormatCalendarAnnotation(zonedDateTime.[[Calendar]], showCalendar). + auto calendar_string = TRY(maybe_format_calendar_annotation(vm, &zoned_date_time.calendar(), show_calendar)); - // 15. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar). - auto calendar_string = format_calendar_annotation(calendar_id, show_calendar); - - // 16. Return the string-concatenation of dateTimeString, offsetString, timeZoneString, and calendarString. + // 15. Return the string-concatenation of dateTimeString, offsetString, timeZoneString, and calendarString. return String::formatted("{}{}{}{}", date_time_string, offset_string, time_zone_string, calendar_string); } diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.toString.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.toString.js index 3faad7c0a8..eabe334c48 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.toString.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.toString.js @@ -36,6 +36,23 @@ describe("correct behavior", () => { plainDate = new Temporal.PlainDate(-12345, 1, 1); expect(plainDate.toString()).toBe("-012345-01-01"); }); + + test("doesn't call ToString on calendar if calenderName option is 'never'", () => { + let calledToString = false; + const calendar = { + toString() { + calledToString = true; + return "nocall"; + }, + }; + + const plainDate = new Temporal.PlainDate(2022, 8, 8, calendar); + const options = { + calendarName: "never", + }; + expect(plainDate.toString(options)).toBe("2022-08-08"); + expect(calledToString).toBeFalse(); + }); }); describe("errors", () => { diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDateTime/PlainDateTime.prototype.toString.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDateTime/PlainDateTime.prototype.toString.js index 29461d4022..2ee72db75b 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDateTime/PlainDateTime.prototype.toString.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/PlainDateTime/PlainDateTime.prototype.toString.js @@ -52,6 +52,34 @@ describe("correct behavior", () => { expect(plainDateTime.toString(pluralOptions)).toBe(expected); } }); + + test("doesn't call ToString on calendar if calenderName option is 'never'", () => { + let calledToString = false; + const calendar = { + toString() { + calledToString = true; + return "nocall"; + }, + }; + + const plainDateTime = new Temporal.PlainDateTime( + 2022, + 8, + 8, + 14, + 38, + 40, + 100, + 200, + 300, + calendar + ); + const options = { + calendarName: "never", + }; + expect(plainDateTime.toString(options)).toBe("2022-08-08T14:38:40.1002003"); + expect(calledToString).toBeFalse(); + }); }); describe("errors", () => { diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.toString.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.toString.js index cde4759c06..d16b05defa 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.toString.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/ZonedDateTime/ZonedDateTime.prototype.toString.js @@ -94,6 +94,37 @@ describe("correct behavior", () => { expect(zonedDateTime.toString(options)).toBe(expected); } }); + + test("doesn't call ToString on calendar if calenderName option is 'never'", () => { + let calledToString = false; + const calendar = { + toString() { + calledToString = true; + return "nocall"; + }, + }; + + const plainDateTime = new Temporal.PlainDateTime( + 2022, + 8, + 8, + 14, + 38, + 40, + 100, + 200, + 300, + calendar + ); + const timeZone = new Temporal.TimeZone("UTC"); + const zonedDateTime = plainDateTime.toZonedDateTime(timeZone); + + const options = { + calendarName: "never", + }; + expect(zonedDateTime.toString(options)).toBe("2022-08-08T14:38:40.1002003+00:00[UTC]"); + expect(calledToString).toBeFalse(); + }); }); describe("errors", () => {