diff --git a/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.cpp b/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.cpp index 325dc5e7745..97d4b327e1e 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.cpp @@ -41,6 +41,7 @@ void PlainMonthDayPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr); define_native_function(realm, vm.names.toJSON, to_json, 0, attr); define_native_function(realm, vm.names.valueOf, value_of, 0, attr); + define_native_function(realm, vm.names.toPlainDate, to_plain_date, 1, attr); } // 10.3.3 get Temporal.PlainMonthDay.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.prototype.calendarid @@ -180,4 +181,38 @@ JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::value_of) return vm.throw_completion(ErrorType::Convert, "Temporal.PlainMonthDay", "a primitive value"); } +// 10.3.12 Temporal.PlainMonthDay.prototype.toPlainDate ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.toplaindate +JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_plain_date) +{ + auto item = vm.argument(0); + + // 1. Let monthDay be the this value. + // 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]). + auto month_day = TRY(typed_this_object(vm)); + + // 3. If item is not an Object, then + if (!item.is_object()) { + // a. Throw a TypeError exception. + return vm.throw_completion(ErrorType::NotAnObject, item); + } + + // 4. Let calendar be monthDay.[[Calendar]]. + auto const& calendar = month_day->calendar(); + + // 5. Let fields be ISODateToFields(calendar, monthDay.[[ISODate]], MONTH-DAY). + auto fields = iso_date_to_fields(calendar, month_day->iso_date(), DateType::MonthDay); + + // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « YEAR », « », « »). + auto input_fields = TRY(prepare_calendar_fields(vm, calendar, item.as_object(), { { CalendarField::Year } }, {}, CalendarFieldList {})); + + // 7. Let mergedFields be CalendarMergeFields(calendar, fields, inputFields). + auto merged_fields = calendar_merge_fields(calendar, fields, input_fields); + + // 8. Let isoDate be ? CalendarDateFromFields(calendar, mergedFields, CONSTRAIN). + auto iso_date = TRY(calendar_date_from_fields(vm, calendar, merged_fields, Overflow::Constrain)); + + // 9. Return ! CreateTemporalDate(isoDate, calendar). + return MUST(create_temporal_date(vm, iso_date, calendar)); +} + } diff --git a/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.h b/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.h index 2780f77aa8c..ef74ed35113 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.h +++ b/Libraries/LibJS/Runtime/Temporal/PlainMonthDayPrototype.h @@ -32,6 +32,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(to_locale_string); JS_DECLARE_NATIVE_FUNCTION(to_json); JS_DECLARE_NATIVE_FUNCTION(value_of); + JS_DECLARE_NATIVE_FUNCTION(to_plain_date); }; } diff --git a/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp b/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp index d1130e1f962..7f818b1c0f4 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include namespace JS::Temporal { @@ -51,6 +52,7 @@ void PlainYearMonthPrototype::initialize(Realm& realm) define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr); define_native_function(realm, vm.names.toJSON, to_json, 0, attr); define_native_function(realm, vm.names.valueOf, value_of, 0, attr); + define_native_function(realm, vm.names.toPlainDate, to_plain_date, 1, attr); } // 9.3.3 get Temporal.PlainYearMonth.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.calendarid @@ -297,4 +299,38 @@ JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::value_of) return vm.throw_completion(ErrorType::Convert, "Temporal.PlainYearMonth", "a primitive value"); } +// 9.3.23 Temporal.PlainYearMonth.prototype.toPlainDate ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.toplaindate +JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::to_plain_date) +{ + auto item = vm.argument(0); + + // 1. Let yearMonth be the this value. + // 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]). + auto year_month = TRY(typed_this_object(vm)); + + // 3. If item is not an Object, then + if (!item.is_object()) { + // a. Throw a TypeError exception. + return vm.throw_completion(ErrorType::NotAnObject, item); + } + + // 4. Let calendar be yearMonth.[[Calendar]]. + auto const& calendar = year_month->calendar(); + + // 5. Let fields be ISODateToFields(calendar, yearMonth.[[ISODate]], YEAR-MONTH). + auto fields = iso_date_to_fields(calendar, year_month->iso_date(), DateType::YearMonth); + + // 6. Let inputFields be ? PrepareCalendarFields(calendar, item, « DAY », « », « »). + auto input_fields = TRY(prepare_calendar_fields(vm, calendar, item.as_object(), { { CalendarField::Day } }, {}, CalendarFieldList {})); + + // 7. Let mergedFields be CalendarMergeFields(calendar, fields, inputFields). + auto merged_fields = calendar_merge_fields(calendar, fields, input_fields); + + // 8. Let isoDate be ? CalendarDateFromFields(calendar, mergedFields, CONSTRAIN). + auto iso_date = TRY(calendar_date_from_fields(vm, calendar, merged_fields, Overflow::Constrain)); + + // 9. Return ! CreateTemporalDate(isoDate, calendar). + return MUST(create_temporal_date(vm, iso_date, calendar)); +} + } diff --git a/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.h b/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.h index 30a9fd1ddbe..af3e72cd7f5 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.h +++ b/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.h @@ -43,6 +43,7 @@ private: JS_DECLARE_NATIVE_FUNCTION(to_locale_string); JS_DECLARE_NATIVE_FUNCTION(to_json); JS_DECLARE_NATIVE_FUNCTION(value_of); + JS_DECLARE_NATIVE_FUNCTION(to_plain_date); }; } diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainMonthDay/PlainMonthDay.prototype.toPlainDate.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainMonthDay/PlainMonthDay.prototype.toPlainDate.js new file mode 100644 index 00000000000..eb4942a9041 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainMonthDay/PlainMonthDay.prototype.toPlainDate.js @@ -0,0 +1,27 @@ +describe("normal behavior", () => { + test("length is 1", () => { + expect(Temporal.PlainMonthDay.prototype.toPlainDate).toHaveLength(1); + }); + + test("basic functionality", () => { + const plainMonthDay = new Temporal.PlainMonthDay(7, 6); + const plainDate = plainMonthDay.toPlainDate({ year: 2021 }); + expect(plainDate.equals(new Temporal.PlainDate(2021, 7, 6))).toBeTrue(); + }); +}); + +describe("errors", () => { + test("argument must be an object", () => { + const plainMonthDay = new Temporal.PlainMonthDay(7, 6); + expect(() => { + plainMonthDay.toPlainDate(42); + }).toThrowWithMessage(TypeError, "42 is not an object"); + }); + + test("year field is required", () => { + const plainMonthDay = new Temporal.PlainMonthDay(7, 6); + expect(() => { + plainMonthDay.toPlainDate({}); + }).toThrowWithMessage(TypeError, "Required property year is missing or undefined"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.prototype.toPlainDate.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.prototype.toPlainDate.js new file mode 100644 index 00000000000..cf0f945da7b --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainYearMonth/PlainYearMonth.prototype.toPlainDate.js @@ -0,0 +1,27 @@ +describe("normal behavior", () => { + test("length is 1", () => { + expect(Temporal.PlainYearMonth.prototype.toPlainDate).toHaveLength(1); + }); + + test("basic functionality", () => { + const plainYearMonth = new Temporal.PlainYearMonth(2021, 7); + const plainDate = plainYearMonth.toPlainDate({ day: 6 }); + expect(plainDate.equals(new Temporal.PlainDate(2021, 7, 6))).toBeTrue(); + }); +}); + +describe("errors", () => { + test("argument must be an object", () => { + const plainYearMonth = new Temporal.PlainYearMonth(2021, 7); + expect(() => { + plainYearMonth.toPlainDate(42); + }).toThrowWithMessage(TypeError, "42 is not an object"); + }); + + test("day field is required", () => { + const plainYearMonth = new Temporal.PlainYearMonth(2021, 7); + expect(() => { + plainYearMonth.toPlainDate({}); + }).toThrowWithMessage(TypeError, "Required property day is missing or undefined"); + }); +});