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

LibJS: Avoid calling ToString on calendar when calendarName is "never"

This is a normative change in the Temporal spec.

See:
- 6122f4e
- cf586bc
This commit is contained in:
Luke Wilde 2022-08-08 14:48:48 +01:00 committed by Linus Groh
parent 0d7b634313
commit 54bb6bf2c0
8 changed files with 115 additions and 33 deletions

View file

@ -502,7 +502,21 @@ ThrowCompletionOr<PlainMonthDay*> calendar_month_day_from_fields(VM& vm, Object&
return static_cast<PlainMonthDay*>(month_day_object); return static_cast<PlainMonthDay*>(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<String> 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) String format_calendar_annotation(StringView id, StringView show_calendar)
{ {
// 1. Assert: showCalendar is "auto", "always", or "never". // 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); 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<bool> calendar_equals(VM& vm, Object& one, Object& two) ThrowCompletionOr<bool> calendar_equals(VM& vm, Object& one, Object& two)
{ {
// 1. If one and two are the same Object value, return true. // 1. If one and two are the same Object value, return true.
@ -541,7 +555,7 @@ ThrowCompletionOr<bool> calendar_equals(VM& vm, Object& one, Object& two)
return false; 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<Object*> consolidate_calendars(VM& vm, Object& one, Object& two) ThrowCompletionOr<Object*> consolidate_calendars(VM& vm, Object& one, Object& two)
{ {
// 1. If one and two are the same Object value, return two. // 1. If one and two are the same Object value, return two.
@ -570,7 +584,7 @@ ThrowCompletionOr<Object*> consolidate_calendars(VM& vm, Object& one, Object& tw
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendar); return vm.throw_completion<RangeError>(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) u8 iso_days_in_month(i32 year, u8 month)
{ {
// 1. Assert: year is an integer. // 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)); 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) u8 to_iso_week_of_year(i32 year, u8 month, u8 day)
{ {
// 1. Assert: year is an integer. // 1. Assert: year is an integer.
@ -625,7 +639,7 @@ u8 to_iso_week_of_year(i32 year, u8 month, u8 day)
return week; 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) String build_iso_month_code(u8 month)
{ {
// 1. Let numberPart be ToZeroPaddedDecimalString(month, 2). // 1. Let numberPart be ToZeroPaddedDecimalString(month, 2).
@ -633,7 +647,7 @@ String build_iso_month_code(u8 month)
return String::formatted("M{:02}", 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<double> resolve_iso_month(VM& vm, Object const& fields) ThrowCompletionOr<double> 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. // 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<double> resolve_iso_month(VM& vm, Object const& fields)
return number_part_integer; 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<ISODateRecord> iso_date_from_fields(VM& vm, Object const& fields, Object const& options) ThrowCompletionOr<ISODateRecord> iso_date_from_fields(VM& vm, Object const& fields, Object const& options)
{ {
// 1. Assert: Type(fields) is Object. // 1. Assert: Type(fields) is Object.
@ -724,7 +738,7 @@ ThrowCompletionOr<ISODateRecord> iso_date_from_fields(VM& vm, Object const& fiel
return regulate_iso_date(vm, year.as_double(), month, day.as_double(), overflow); 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<ISOYearMonth> iso_year_month_from_fields(VM& vm, Object const& fields, Object const& options) ThrowCompletionOr<ISOYearMonth> iso_year_month_from_fields(VM& vm, Object const& fields, Object const& options)
{ {
// 1. Assert: Type(fields) is Object. // 1. Assert: Type(fields) is Object.
@ -751,7 +765,7 @@ ThrowCompletionOr<ISOYearMonth> iso_year_month_from_fields(VM& vm, Object const&
return ISOYearMonth { .year = result.year, .month = result.month, .reference_iso_day = 1 }; 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<ISOMonthDay> iso_month_day_from_fields(VM& vm, Object const& fields, Object const& options) ThrowCompletionOr<ISOMonthDay> iso_month_day_from_fields(VM& vm, Object const& fields, Object const& options)
{ {
// 1. Assert: Type(fields) is Object. // 1. Assert: Type(fields) is Object.
@ -809,7 +823,7 @@ ThrowCompletionOr<ISOMonthDay> iso_month_day_from_fields(VM& vm, Object const& f
return ISOMonthDay { .month = result->month, .day = result->day, .reference_iso_year = reference_iso_year }; 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) i32 iso_year(Object& temporal_object)
{ {
// 1. Assert: temporalObject has an [[ISOYear]] internal slot. // 1. Assert: temporalObject has an [[ISOYear]] internal slot.
@ -827,7 +841,7 @@ i32 iso_year(Object& temporal_object)
VERIFY_NOT_REACHED(); 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) u8 iso_month(Object& temporal_object)
{ {
// 1. Assert: temporalObject has an [[ISOMonth]] internal slot. // 1. Assert: temporalObject has an [[ISOMonth]] internal slot.
@ -845,7 +859,7 @@ u8 iso_month(Object& temporal_object)
VERIFY_NOT_REACHED(); 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) String iso_month_code(Object& temporal_object)
{ {
// 1. Assert: temporalObject has an [[ISOMonth]] internal slot. // 1. Assert: temporalObject has an [[ISOMonth]] internal slot.
@ -863,7 +877,7 @@ String iso_month_code(Object& temporal_object)
VERIFY_NOT_REACHED(); 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) u8 iso_day(Object& temporal_object)
{ {
// 1. Assert: temporalObject has an [[ISODay]] internal slot. // 1. Assert: temporalObject has an [[ISODay]] internal slot.
@ -881,7 +895,7 @@ u8 iso_day(Object& temporal_object)
VERIFY_NOT_REACHED(); 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<Object*> default_merge_calendar_fields(VM& vm, Object const& fields, Object const& additional_fields) ThrowCompletionOr<Object*> default_merge_calendar_fields(VM& vm, Object const& fields, Object const& additional_fields)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();

View file

@ -59,6 +59,7 @@ ThrowCompletionOr<Object*> get_temporal_calendar_with_iso_default(VM&, Object&);
ThrowCompletionOr<PlainDate*> calendar_date_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr); ThrowCompletionOr<PlainDate*> calendar_date_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
ThrowCompletionOr<PlainYearMonth*> calendar_year_month_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr); ThrowCompletionOr<PlainYearMonth*> calendar_year_month_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
ThrowCompletionOr<PlainMonthDay*> calendar_month_day_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr); ThrowCompletionOr<PlainMonthDay*> calendar_month_day_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
ThrowCompletionOr<String> maybe_format_calendar_annotation(VM&, Value calendar_object, StringView show_calendar);
String format_calendar_annotation(StringView id, StringView show_calendar); String format_calendar_annotation(StringView id, StringView show_calendar);
ThrowCompletionOr<bool> calendar_equals(VM&, Object& one, Object& two); ThrowCompletionOr<bool> calendar_equals(VM&, Object& one, Object& two);
ThrowCompletionOr<Object*> consolidate_calendars(VM&, Object& one, Object& two); ThrowCompletionOr<Object*> consolidate_calendars(VM&, Object& one, Object& two);

View file

@ -427,13 +427,10 @@ ThrowCompletionOr<String> temporal_date_to_string(VM& vm, PlainDate& temporal_da
// 5. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2). // 5. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2).
auto day = String::formatted("{:02}", temporal_date.iso_day()); auto day = String::formatted("{:02}", temporal_date.iso_day());
// 6. Let calendarID be ? ToString(temporalDate.[[Calendar]]). // 6. Let calendar be ? MaybeFormatCalendarAnnotation(temporalDate.[[Calendar]], showCalendar).
auto calendar_id = TRY(Value(&temporal_date.calendar()).to_string(vm)); auto calendar = TRY(maybe_format_calendar_annotation(vm, &temporal_date.calendar(), show_calendar));
// 7. Let calendar be ! FormatCalendarAnnotation(calendarID, showCalendar). // 7. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar.
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.
return String::formatted("{}-{}-{}{}", year, month, day, calendar); return String::formatted("{}-{}-{}{}", year, month, day, calendar);
} }

View file

@ -281,13 +281,10 @@ ThrowCompletionOr<String> temporal_date_time_to_string(VM& vm, i32 iso_year, u8
// 7. Let seconds be ! FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision). // 7. Let seconds be ! FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision).
auto seconds = format_seconds_string_part(second, millisecond, microsecond, nanosecond, precision); auto seconds = format_seconds_string_part(second, millisecond, microsecond, nanosecond, precision);
// 8. Let calendarID be ? ToString(calendar). // 8. Let calendarString be ? MaybeFormatCalendarAnnotation(calendar, showCalendar).
auto calendar_id = TRY(calendar.to_string(vm)); auto calendar_string = TRY(maybe_format_calendar_annotation(vm, calendar, show_calendar));
// 9. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar). // 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.
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.
return String::formatted("{}-{:02}-{:02}T{:02}:{:02}{}{}", pad_iso_year(iso_year), iso_month, iso_day, hour, minute, seconds, calendar_string); return String::formatted("{}-{:02}-{:02}T{:02}:{:02}{}{}", pad_iso_year(iso_year), iso_month, iso_day, hour, minute, seconds, calendar_string);
} }

View file

@ -351,13 +351,10 @@ ThrowCompletionOr<String> temporal_zoned_date_time_to_string(VM& vm, ZonedDateTi
time_zone_string = String::formatted("[{}]", time_zone_id); time_zone_string = String::formatted("[{}]", time_zone_id);
} }
// 14. Let calendarID be ? ToString(zonedDateTime.[[Calendar]]). // 14. Let calendarString be ? MaybeFormatCalendarAnnotation(zonedDateTime.[[Calendar]], showCalendar).
auto calendar_id = TRY(Value(&zoned_date_time.calendar()).to_string(vm)); auto calendar_string = TRY(maybe_format_calendar_annotation(vm, &zoned_date_time.calendar(), show_calendar));
// 15. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar). // 15. Return the string-concatenation of dateTimeString, offsetString, timeZoneString, and calendarString.
auto calendar_string = format_calendar_annotation(calendar_id, show_calendar);
// 16. Return the string-concatenation of dateTimeString, offsetString, timeZoneString, and calendarString.
return String::formatted("{}{}{}{}", date_time_string, offset_string, time_zone_string, calendar_string); return String::formatted("{}{}{}{}", date_time_string, offset_string, time_zone_string, calendar_string);
} }

View file

@ -36,6 +36,23 @@ describe("correct behavior", () => {
plainDate = new Temporal.PlainDate(-12345, 1, 1); plainDate = new Temporal.PlainDate(-12345, 1, 1);
expect(plainDate.toString()).toBe("-012345-01-01"); 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", () => { describe("errors", () => {

View file

@ -52,6 +52,34 @@ describe("correct behavior", () => {
expect(plainDateTime.toString(pluralOptions)).toBe(expected); 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", () => { describe("errors", () => {

View file

@ -94,6 +94,37 @@ describe("correct behavior", () => {
expect(zonedDateTime.toString(options)).toBe(expected); 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", () => { describe("errors", () => {