diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt index b5e21ade214..d8df7e09d43 100644 --- a/Libraries/LibJS/CMakeLists.txt +++ b/Libraries/LibJS/CMakeLists.txt @@ -213,6 +213,8 @@ set(SOURCES Runtime/Temporal/Instant.cpp Runtime/Temporal/ISO8601.cpp Runtime/Temporal/PlainDate.cpp + Runtime/Temporal/PlainDateConstructor.cpp + Runtime/Temporal/PlainDatePrototype.cpp Runtime/Temporal/PlainDateTime.cpp Runtime/Temporal/PlainMonthDay.cpp Runtime/Temporal/PlainMonthDayConstructor.cpp diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index af73e54a3fe..6c71b28cede 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -89,6 +89,7 @@ #define JS_ENUMERATE_TEMPORAL_OBJECTS \ __JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \ + __JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \ __JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \ __JS_ENUMERATE(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor) diff --git a/Libraries/LibJS/Print.cpp b/Libraries/LibJS/Print.cpp index 6ac16a8804b..3ee982851b0 100644 --- a/Libraries/LibJS/Print.cpp +++ b/Libraries/LibJS/Print.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -837,6 +838,15 @@ ErrorOr print_temporal_duration(JS::PrintContext& print_context, JS::Tempo return {}; } +ErrorOr print_temporal_plain_date(JS::PrintContext& print_context, JS::Temporal::PlainDate const& plain_date, HashTable& seen_objects) +{ + TRY(print_type(print_context, "Temporal.PlainDate"sv)); + TRY(js_out(print_context, " \033[34;1m{:04}-{:02}-{:02}\033[0m", plain_date.iso_date().year, plain_date.iso_date().month, plain_date.iso_date().day)); + TRY(js_out(print_context, "\n calendar: ")); + TRY(print_value(print_context, JS::PrimitiveString::create(plain_date.vm(), plain_date.calendar()), seen_objects)); + return {}; +} + ErrorOr print_temporal_plain_month_day(JS::PrintContext& print_context, JS::Temporal::PlainMonthDay const& plain_month_day, HashTable& seen_objects) { TRY(print_type(print_context, "Temporal.PlainMonthDay"sv)); @@ -972,6 +982,8 @@ ErrorOr print_value(JS::PrintContext& print_context, JS::Value value, Hash return print_intl_duration_format(print_context, static_cast(object), seen_objects); if (is(object)) return print_temporal_duration(print_context, static_cast(object), seen_objects); + if (is(object)) + return print_temporal_plain_date(print_context, static_cast(object), seen_objects); if (is(object)) return print_temporal_plain_month_day(print_context, static_cast(object), seen_objects); if (is(object)) diff --git a/Libraries/LibJS/Runtime/Intrinsics.cpp b/Libraries/LibJS/Runtime/Intrinsics.cpp index 45f352355e3..1ad8621efd9 100644 --- a/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -101,6 +101,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp index 09d3ef01f27..9df0c3af469 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp +++ b/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp @@ -8,14 +8,26 @@ */ #include +#include #include #include #include +#include #include #include namespace JS::Temporal { +GC_DEFINE_ALLOCATOR(PlainDate); + +// 3 Temporal.PlainDate Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects +PlainDate::PlainDate(ISODate iso_date, String calendar, Object& prototype) + : Object(ConstructWithPrototypeTag::Tag, prototype) + , m_iso_date(iso_date) + , m_calendar(move(calendar)) +{ +} + // 3.5.2 CreateISODateRecord ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-create-iso-date-record ISODate create_iso_date_record(double year, double month, double day) { @@ -26,6 +38,106 @@ ISODate create_iso_date_record(double year, double month, double day) return { .year = static_cast(year), .month = static_cast(month), .day = static_cast(day) }; } +// 3.5.3 CreateTemporalDate ( isoDate, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldate +ThrowCompletionOr> create_temporal_date(VM& vm, ISODate iso_date, String calendar, GC::Ptr new_target) +{ + auto& realm = *vm.current_realm(); + + // 1. If ISODateWithinLimits(isoDate) is false, throw a RangeError exception. + if (!iso_date_within_limits(iso_date)) + return vm.throw_completion(ErrorType::TemporalInvalidPlainDate); + + // 2. If newTarget is not present, set newTarget to %Temporal.PlainDate%. + if (!new_target) + new_target = realm.intrinsics().temporal_plain_date_constructor(); + + // 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDate.prototype%", « [[InitializedTemporalDate]], [[ISODate]], [[Calendar]] »). + // 4. Set object.[[ISODate]] to isoDate. + // 5. Set object.[[Calendar]] to calendar. + auto object = TRY(ordinary_create_from_constructor(vm, *new_target, &Intrinsics::temporal_plain_date_prototype, iso_date, move(calendar))); + + // 6. Return object. + return object; +} + +// 3.5.4 ToTemporalDate ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldate +ThrowCompletionOr> to_temporal_date(VM& vm, Value item, Value options) +{ + // 1. If options is not present, set options to undefined. + + // 2. If item is an Object, then + if (item.is_object()) { + auto const& object = item.as_object(); + + // a. If item has an [[InitializedTemporalDate]] internal slot, then + if (is(object)) { + auto const& plain_date = static_cast(object); + + // i. Let resolvedOptions be ? GetOptionsObject(options). + auto resolved_options = TRY(get_options_object(vm, options)); + + // ii. Perform ? GetTemporalOverflowOption(resolvedOptions). + TRY(get_temporal_overflow_option(vm, resolved_options)); + + // iii. Return ! CreateTemporalDate(item.[[ISODate]], item.[[Calendar]]). + return MUST(create_temporal_date(vm, plain_date.iso_date(), plain_date.calendar())); + } + // FIXME: b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then + // FIXME: i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], item.[[EpochNanoseconds]]). + // FIXME: ii. Let resolvedOptions be ? GetOptionsObject(options). + // FIXME: iii. Perform ? GetTemporalOverflowOption(resolvedOptions). + // FIXME: iv. Return ! CreateTemporalDate(isoDateTime.[[ISODate]], item.[[Calendar]]). + // FIXME: c. If item has an [[InitializedTemporalDateTime]] internal slot, then + // FIXME: i. Let resolvedOptions be ? GetOptionsObject(options). + // FIXME: ii. Perform ? GetTemporalOverflowOption(resolvedOptions). + // FIXME: iii. Return ! CreateTemporalDate(item.[[ISODateTime]].[[ISODate]], item.[[Calendar]]). + + // d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item). + auto calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object)); + + // e. Let fields be ? PrepareCalendarFields(calendar, item, « YEAR, MONTH, MONTH-CODE, DAY », «», «»). + auto fields = TRY(prepare_calendar_fields(vm, calendar, object, { { CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day } }, {}, CalendarFieldList {})); + + // f. Let resolvedOptions be ? GetOptionsObject(options). + auto resolved_options = TRY(get_options_object(vm, options)); + + // g. Let overflow be ? GetTemporalOverflowOption(resolvedOptions). + auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options)); + + // h. Let isoDate be ? CalendarDateFromFields(calendar, fields, overflow). + auto iso_date = TRY(calendar_date_from_fields(vm, calendar, move(fields), overflow)); + + // i. Return ! CreateTemporalDate(isoDate, calendar). + return MUST(create_temporal_date(vm, iso_date, move(calendar))); + } + + // 3. If item is not a String, throw a TypeError exception. + if (!item.is_string()) + return vm.throw_completion(ErrorType::TemporalInvalidPlainDate); + + // 4. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[~Zoned] »). + auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalDateTimeString } })); + + // 5. Let calendar be result.[[Calendar]]. + // 6. If calendar is empty, set calendar to "iso8601". + auto calendar = result.calendar.value_or("iso8601"_string); + + // 7. Set calendar to ? CanonicalizeCalendar(calendar). + calendar = TRY(canonicalize_calendar(vm, calendar)); + + // 8. Let resolvedOptions be ? GetOptionsObject(options). + auto resolved_options = TRY(get_options_object(vm, options)); + + // 9. Perform ? GetTemporalOverflowOption(resolvedOptions). + TRY(get_temporal_overflow_option(vm, resolved_options)); + + // 10. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]). + auto iso_date = create_iso_date_record(*result.year, result.month, result.day); + + // 11. Return ? CreateTemporalDate(isoDate, calendar). + return TRY(create_temporal_date(vm, iso_date, move(calendar))); +} + // 3.5.5 ISODateSurpasses ( sign, y1, m1, d1, isoDate2 ), https://tc39.es/proposal-temporal/#sec-temporal-isodatesurpasses bool iso_date_surpasses(i8 sign, double year1, double month1, double day1, ISODate iso_date2) { diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDate.h b/Libraries/LibJS/Runtime/Temporal/PlainDate.h index f4bb8067022..b365b58714c 100644 --- a/Libraries/LibJS/Runtime/Temporal/PlainDate.h +++ b/Libraries/LibJS/Runtime/Temporal/PlainDate.h @@ -11,6 +11,7 @@ #include #include +#include #include namespace JS::Temporal { @@ -22,7 +23,26 @@ struct ISODate { u8 day { 0 }; }; +class PlainDate final : public Object { + JS_OBJECT(PlainDate, Object); + GC_DECLARE_ALLOCATOR(PlainDate); + +public: + virtual ~PlainDate() override = default; + + [[nodiscard]] ISODate iso_date() const { return m_iso_date; } + [[nodiscard]] String const& calendar() const { return m_calendar; } + +private: + PlainDate(ISODate, String calendar, Object& prototype); + + ISODate m_iso_date; // [[ISODate]] + String m_calendar; // [[Calendar]] +}; + ISODate create_iso_date_record(double year, double month, double day); +ThrowCompletionOr> to_temporal_date(VM& vm, Value item, Value options = js_undefined()); +ThrowCompletionOr> create_temporal_date(VM&, ISODate, String calendar, GC::Ptr new_target = {}); bool iso_date_surpasses(i8 sign, double year1, double month1, double day1, ISODate iso_date2); ThrowCompletionOr regulate_iso_date(VM& vm, double year, double month, double day, Overflow overflow); bool is_valid_iso_date(double year, double month, double day); diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp new file mode 100644 index 00000000000..8a2791b79ac --- /dev/null +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace JS::Temporal { + +GC_DEFINE_ALLOCATOR(PlainDateConstructor); + +// 3.1 The Temporal.PlainDate Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-constructor +PlainDateConstructor::PlainDateConstructor(Realm& realm) + : NativeFunction(realm.vm().names.PlainDate.as_string(), realm.intrinsics().function_prototype()) +{ +} + +void PlainDateConstructor::initialize(Realm& realm) +{ + Base::initialize(realm); + + auto& vm = this->vm(); + + // 3.2.1 Temporal.PlainDate.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype + define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_date_prototype(), 0); + + u8 attr = Attribute::Writable | Attribute::Configurable; + define_native_function(realm, vm.names.from, from, 1, attr); + define_native_function(realm, vm.names.compare, compare, 2, attr); + + define_direct_property(vm.names.length, Value(3), Attribute::Configurable); +} + +// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendar ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate +ThrowCompletionOr PlainDateConstructor::call() +{ + auto& vm = this->vm(); + + // 1. If NewTarget is undefined, throw a TypeError exception. + return vm.throw_completion(ErrorType::ConstructorWithoutNew, "Temporal.PlainDate"); +} + +// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendar ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate +ThrowCompletionOr> PlainDateConstructor::construct(FunctionObject& new_target) +{ + auto& vm = this->vm(); + + auto iso_year = vm.argument(0); + auto iso_month = vm.argument(1); + auto iso_day = vm.argument(2); + auto calendar_value = vm.argument(3); + + // 2. Let y be ? ToIntegerWithTruncation(isoYear). + auto year = TRY(to_integer_with_truncation(vm, iso_year, ErrorType::TemporalInvalidPlainDate)); + + // 3. Let m be ? ToIntegerWithTruncation(isoMonth). + auto month = TRY(to_integer_with_truncation(vm, iso_month, ErrorType::TemporalInvalidPlainDate)); + + // 4. Let d be ? ToIntegerWithTruncation(isoDay). + auto day = TRY(to_integer_with_truncation(vm, iso_day, ErrorType::TemporalInvalidPlainDate)); + + // 5. If calendar is undefined, set calendar to "iso8601". + if (calendar_value.is_undefined()) + calendar_value = PrimitiveString::create(vm, "iso8601"_string); + + // 6. If calendar is not a String, throw a TypeError exception. + if (!calendar_value.is_string()) + return vm.throw_completion(ErrorType::NotAString, "calendar"sv); + + // 7. Set calendar to ? CanonicalizeCalendar(calendar). + auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string_view())); + + // 8. If IsValidISODate(y, m, d) is false, throw a RangeError exception. + if (!is_valid_iso_date(year, month, day)) + return vm.throw_completion(ErrorType::TemporalInvalidPlainDate); + + // 9. Let isoDate be CreateISODateRecord(y, m, d). + auto iso_date = create_iso_date_record(year, month, day); + + // 10. Return ? CreateTemporalDate(isoDate, calendar, NewTarget). + return TRY(create_temporal_date(vm, iso_date, move(calendar), &new_target)); +} + +// 3.2.2 3.2.2 Temporal.PlainDate.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.from +JS_DEFINE_NATIVE_FUNCTION(PlainDateConstructor::from) +{ + // 1. Return ? ToTemporalDate(item, options). + return TRY(to_temporal_date(vm, vm.argument(0), vm.argument(1))); +} + +// 3.2.3 Temporal.PlainDate.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.compare +JS_DEFINE_NATIVE_FUNCTION(PlainDateConstructor::compare) +{ + // 1. Set one to ? ToTemporalDate(one). + auto one = TRY(to_temporal_date(vm, vm.argument(0))); + + // 2. Set two to ? ToTemporalDate(two). + auto two = TRY(to_temporal_date(vm, vm.argument(1))); + + // 3. Return 𝔽(CompareISODate(one.[[ISODate]], two.[[ISODate]])). + return compare_iso_date(one->iso_date(), two->iso_date()); +} + +} diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h b/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h new file mode 100644 index 00000000000..367f0252677 --- /dev/null +++ b/Libraries/LibJS/Runtime/Temporal/PlainDateConstructor.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS::Temporal { + +class PlainDateConstructor final : public NativeFunction { + JS_OBJECT(PlainDateConstructor, NativeFunction); + GC_DECLARE_ALLOCATOR(PlainDateConstructor); + +public: + virtual void initialize(Realm&) override; + virtual ~PlainDateConstructor() override = default; + + virtual ThrowCompletionOr call() override; + virtual ThrowCompletionOr> construct(FunctionObject& new_target) override; + +private: + explicit PlainDateConstructor(Realm&); + + virtual bool has_constructor() const override { return true; } + + JS_DECLARE_NATIVE_FUNCTION(from); + JS_DECLARE_NATIVE_FUNCTION(compare); +}; + +} diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp b/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp new file mode 100644 index 00000000000..773bb83e315 --- /dev/null +++ b/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * Copyright (c) 2021-2023, Linus Groh + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JS::Temporal { + +GC_DEFINE_ALLOCATOR(PlainDatePrototype); + +// 3.3 Properties of the Temporal.PlainDate Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plaindate-prototype-object +PlainDatePrototype::PlainDatePrototype(Realm& realm) + : PrototypeObject(realm.intrinsics().object_prototype()) +{ +} + +void PlainDatePrototype::initialize(Realm& realm) +{ + Base::initialize(realm); + + auto& vm = this->vm(); + + // 3.3.2 Temporal.PlainDate.prototype[ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype-%symbol.tostringtag% + define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.PlainDate"_string), Attribute::Configurable); + + define_native_accessor(realm, vm.names.calendarId, calendar_id_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.era, era_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.eraYear, era_year_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.year, year_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.month, month_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.monthCode, month_code_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.day, day_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.dayOfWeek, day_of_week_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.dayOfYear, day_of_year_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.weekOfYear, week_of_year_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.yearOfWeek, year_of_week_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.daysInWeek, days_in_week_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.daysInMonth, days_in_month_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.daysInYear, days_in_year_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.monthsInYear, months_in_year_getter, {}, Attribute::Configurable); + define_native_accessor(realm, vm.names.inLeapYear, in_leap_year_getter, {}, Attribute::Configurable); +} + +// 3.3.3 get Temporal.PlainDate.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.calendarid +JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::calendar_id_getter) +{ + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + auto temporal_date = TRY(typed_this_object(vm)); + + // 3. Return temporalDate.[[Calendar]]. + return PrimitiveString::create(vm, temporal_date->calendar()); +} + +// 3.3.4 get Temporal.PlainDate.prototype.era, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.era +JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::era_getter) +{ + // 1. Let plainDate be the this value. + // 2. Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]). + auto plain_date = TRY(typed_this_object(vm)); + + // 3. Return CalendarISOToDate(plainDate.[[Calendar]], plainDate.[[ISODate]]).[[Era]]. + auto result = calendar_iso_to_date(plain_date->calendar(), plain_date->iso_date()).era; + + if (!result.has_value()) + return js_undefined(); + + return PrimitiveString::create(vm, result.release_value()); +} + +// 3.3.5 get Temporal.PlainDate.prototype.eraYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.erayear +JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::era_year_getter) +{ + // 1. Let plainDate be the this value. + // 2. Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]). + auto plain_date = TRY(typed_this_object(vm)); + + // 3. Let result be CalendarISOToDate(plainDate.[[Calendar]], plainDate.[[ISODate]]).[[EraYear]]. + auto result = calendar_iso_to_date(plain_date->calendar(), plain_date->iso_date()).era_year; + + // 4. If result is undefined, return undefined. + if (!result.has_value()) + return js_undefined(); + + // 5. Return 𝔽(result). + return *result; +} + +// 3.3.6 get Temporal.PlainDate.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.year +// 3.3.7 get Temporal.PlainDate.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.month +// 3.3.9 get Temporal.PlainDate.prototype.day, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.day +// 3.3.10 get Temporal.PlainDate.prototype.dayOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.dayofweek +// 3.3.11 get Temporal.PlainDate.prototype.dayOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.dayofyear +// 3.3.14 get Temporal.PlainDate.prototype.daysInWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinweek +// 3.3.15 get Temporal.PlainDate.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinmonth +// 3.3.16 get Temporal.PlainDate.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinyear +// 3.3.17 get Temporal.PlainDate.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.monthsinyear +// 3.3.18 get Temporal.PlainDate.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.inleapyear +#define JS_ENUMERATE_PLAIN_DATE_SIMPLE_FIELDS \ + __JS_ENUMERATE(year) \ + __JS_ENUMERATE(month) \ + __JS_ENUMERATE(day) \ + __JS_ENUMERATE(day_of_week) \ + __JS_ENUMERATE(day_of_year) \ + __JS_ENUMERATE(days_in_week) \ + __JS_ENUMERATE(days_in_month) \ + __JS_ENUMERATE(days_in_year) \ + __JS_ENUMERATE(months_in_year) \ + __JS_ENUMERATE(in_leap_year) + +#define __JS_ENUMERATE(field) \ + JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::field##_getter) \ + { \ + /* 1. Let temporalDate be the this value. */ \ + /* 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). */ \ + auto temporal_date = TRY(typed_this_object(vm)); \ + \ + /* 3. Return CalendarISOToDate(temporalDate.[[Calendar]], temporalDate.[[ISODate]]).[[]]. */ \ + return calendar_iso_to_date(temporal_date->calendar(), temporal_date->iso_date()).field; \ + } +JS_ENUMERATE_PLAIN_DATE_SIMPLE_FIELDS +#undef __JS_ENUMERATE + +// 3.3.8 get Temporal.PlainDate.prototype.monthCode, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.monthcode +JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::month_code_getter) +{ + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + auto temporal_date = TRY(typed_this_object(vm)); + + // 3. Return CalendarISOToDate(temporalDate.[[Calendar]], temporalDate.[[ISODate]]).[[MonthCode]]. + auto month_code = calendar_iso_to_date(temporal_date->calendar(), temporal_date->iso_date()).month_code; + return PrimitiveString::create(vm, move(month_code)); +} + +// 3.3.12 get Temporal.PlainDate.prototype.weekOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.weekofyear +JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::week_of_year_getter) +{ + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + auto temporal_date = TRY(typed_this_object(vm)); + + // 3. Let result be CalendarISOToDate(temporalDate.[[Calendar]], temporalDate.[[ISODate]]).[[WeekOfYear]].[[Week]]. + auto result = calendar_iso_to_date(temporal_date->calendar(), temporal_date->iso_date()).week_of_year.week; + + // 4. If result is undefined, return undefined. + if (!result.has_value()) + return js_undefined(); + + // 5. Return 𝔽(result). + return *result; +} + +// 3.3.13 get Temporal.PlainDate.prototype.yearOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.yearofweek +JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::year_of_week_getter) +{ + // 1. Let temporalDate be the this value. + // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). + auto temporal_date = TRY(typed_this_object(vm)); + + // 3. Let result be CalendarISOToDate(temporalDate.[[Calendar]], temporalDate.[[ISODate]]).[[WeekOfYear]].[[Year]]. + auto result = calendar_iso_to_date(temporal_date->calendar(), temporal_date->iso_date()).week_of_year.year; + + // 4. If result is undefined, return undefined. + if (!result.has_value()) + return js_undefined(); + + // 5. Return 𝔽(result). + return *result; +} + +} diff --git a/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h b/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h new file mode 100644 index 00000000000..7a79fafffc0 --- /dev/null +++ b/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2021, Idan Horowitz + * Copyright (c) 2024, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace JS::Temporal { + +class PlainDatePrototype final : public PrototypeObject { + JS_PROTOTYPE_OBJECT(PlainDatePrototype, PlainDate, Temporal.PlainDate); + GC_DECLARE_ALLOCATOR(PlainDatePrototype); + +public: + virtual void initialize(Realm&) override; + virtual ~PlainDatePrototype() override = default; + +private: + explicit PlainDatePrototype(Realm&); + + JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter); + JS_DECLARE_NATIVE_FUNCTION(era_getter); + JS_DECLARE_NATIVE_FUNCTION(era_year_getter); + JS_DECLARE_NATIVE_FUNCTION(year_getter); + JS_DECLARE_NATIVE_FUNCTION(month_getter); + JS_DECLARE_NATIVE_FUNCTION(month_code_getter); + JS_DECLARE_NATIVE_FUNCTION(day_getter); + JS_DECLARE_NATIVE_FUNCTION(day_of_week_getter); + JS_DECLARE_NATIVE_FUNCTION(day_of_year_getter); + JS_DECLARE_NATIVE_FUNCTION(week_of_year_getter); + JS_DECLARE_NATIVE_FUNCTION(year_of_week_getter); + JS_DECLARE_NATIVE_FUNCTION(days_in_week_getter); + JS_DECLARE_NATIVE_FUNCTION(days_in_month_getter); + JS_DECLARE_NATIVE_FUNCTION(days_in_year_getter); + JS_DECLARE_NATIVE_FUNCTION(months_in_year_getter); + JS_DECLARE_NATIVE_FUNCTION(in_leap_year_getter); +}; + +} diff --git a/Libraries/LibJS/Runtime/Temporal/Temporal.cpp b/Libraries/LibJS/Runtime/Temporal/Temporal.cpp index 190620a905c..d42829445ea 100644 --- a/Libraries/LibJS/Runtime/Temporal/Temporal.cpp +++ b/Libraries/LibJS/Runtime/Temporal/Temporal.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ void Temporal::initialize(Realm& realm) u8 attr = Attribute::Writable | Attribute::Configurable; define_intrinsic_accessor(vm.names.Duration, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_duration_constructor(); }); + define_intrinsic_accessor(vm.names.PlainDate, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_date_constructor(); }); define_intrinsic_accessor(vm.names.PlainMonthDay, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_month_day_constructor(); }); define_intrinsic_accessor(vm.names.PlainYearMonth, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_year_month_constructor(); }); } diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.compare.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.compare.js new file mode 100644 index 00000000000..a6a92799a3e --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.compare.js @@ -0,0 +1,13 @@ +describe("correct behavior", () => { + test("length is 2", () => { + expect(Temporal.PlainDate.compare).toHaveLength(2); + }); + + test("basic functionality", () => { + const plainDate1 = new Temporal.PlainDate(2021, 7, 26); + expect(Temporal.PlainDate.compare(plainDate1, plainDate1)).toBe(0); + const plainDate2 = new Temporal.PlainDate(2021, 7, 27); + expect(Temporal.PlainDate.compare(plainDate1, plainDate2)).toBe(-1); + expect(Temporal.PlainDate.compare(plainDate2, plainDate1)).toBe(1); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.from.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.from.js new file mode 100644 index 00000000000..abb79f6538c --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.from.js @@ -0,0 +1,49 @@ +describe("correct behavior", () => { + test("length is 1", () => { + expect(Temporal.PlainDate.from).toHaveLength(1); + }); + + test("PlainDate instance argument", () => { + const plainDate = new Temporal.PlainDate(2021, 7, 26); + const createdPlainDate = Temporal.PlainDate.from(plainDate); + expect(createdPlainDate.year).toBe(2021); + expect(createdPlainDate.month).toBe(7); + expect(createdPlainDate.day).toBe(26); + }); + + test("PlainDate string argument", () => { + const createdPlainDate = Temporal.PlainDate.from("2021-07-26"); + expect(createdPlainDate.year).toBe(2021); + expect(createdPlainDate.month).toBe(7); + expect(createdPlainDate.day).toBe(26); + }); +}); + +describe("errors", () => { + test("missing fields", () => { + expect(() => { + Temporal.PlainDate.from({}); + }).toThrowWithMessage(TypeError, "Required property year is missing or undefined"); + expect(() => { + Temporal.PlainDate.from({ year: 0 }); + }).toThrowWithMessage(TypeError, "Required property day is missing or undefined"); + expect(() => { + Temporal.PlainDate.from({ month: 1 }); + }).toThrowWithMessage(TypeError, "Required property year is missing or undefined"); + }); + + test("invalid date time string", () => { + expect(() => { + Temporal.PlainDate.from("foo"); + }).toThrowWithMessage(RangeError, "Invalid ISO date time"); + }); + + test("extended year must not be negative zero", () => { + expect(() => { + Temporal.PlainDate.from("-000000-01-01"); + }).toThrowWithMessage(RangeError, "Invalid ISO date time"); + expect(() => { + Temporal.PlainDate.from("−000000-01-01"); // U+2212 + }).toThrowWithMessage(RangeError, "Invalid ISO date time"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js new file mode 100644 index 00000000000..8273a1bdba5 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.js @@ -0,0 +1,53 @@ +describe("errors", () => { + test("called without new", () => { + expect(() => { + Temporal.PlainDate(); + }).toThrowWithMessage( + TypeError, + "Temporal.PlainDate constructor must be called with 'new'" + ); + }); + + test("cannot pass Infinity", () => { + expect(() => { + new Temporal.PlainDate(Infinity); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + expect(() => { + new Temporal.PlainDate(0, Infinity); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + expect(() => { + new Temporal.PlainDate(0, 0, Infinity); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + expect(() => { + new Temporal.PlainDate(-Infinity); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + expect(() => { + new Temporal.PlainDate(0, -Infinity); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + expect(() => { + new Temporal.PlainDate(0, 0, -Infinity); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + }); + + test("cannot pass invalid ISO month/day", () => { + expect(() => { + new Temporal.PlainDate(0, 0, 1); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + expect(() => { + new Temporal.PlainDate(0, 1, 0); + }).toThrowWithMessage(RangeError, "Invalid plain date"); + }); +}); + +describe("normal behavior", () => { + test("length is 3", () => { + expect(Temporal.PlainDate).toHaveLength(3); + }); + + test("basic functionality", () => { + const plainDate = new Temporal.PlainDate(2021, 7, 19); + expect(typeof plainDate).toBe("object"); + expect(plainDate).toBeInstanceOf(Temporal.PlainDate); + expect(Object.getPrototypeOf(plainDate)).toBe(Temporal.PlainDate.prototype); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.calendarId.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.calendarId.js new file mode 100644 index 00000000000..ae43185fd4f --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.calendarId.js @@ -0,0 +1,15 @@ +describe("correct behavior", () => { + test("calendarId basic functionality", () => { + const calendar = "iso8601"; + const plainDate = new Temporal.PlainDate(2000, 5, 1, calendar); + expect(plainDate.calendarId).toBe("iso8601"); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "calendarId", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.day.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.day.js new file mode 100644 index 00000000000..576e8275cc5 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.day.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.day).toBe(23); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "day", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.dayOfWeek.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.dayOfWeek.js new file mode 100644 index 00000000000..436b8b6e850 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.dayOfWeek.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.dayOfWeek).toBe(5); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "dayOfWeek", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.dayOfYear.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.dayOfYear.js new file mode 100644 index 00000000000..15d50c8e5c0 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.dayOfYear.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.dayOfYear).toBe(204); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "dayOfYear", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInMonth.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInMonth.js new file mode 100644 index 00000000000..9d02718ded0 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInMonth.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.daysInMonth).toBe(31); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "daysInMonth", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInWeek.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInWeek.js new file mode 100644 index 00000000000..1e9a8883493 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInWeek.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.daysInWeek).toBe(7); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "daysInWeek", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInYear.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInYear.js new file mode 100644 index 00000000000..90eb232fd04 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.daysInYear.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.daysInYear).toBe(365); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "daysInYear", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.era.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.era.js new file mode 100644 index 00000000000..7aebdab846e --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.era.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const plainDate = new Temporal.PlainDate(2021, 7, 6); + expect(plainDate.era).toBeUndefined(); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "era", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.eraYear.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.eraYear.js new file mode 100644 index 00000000000..4b85648e7ae --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.eraYear.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const plainDate = new Temporal.PlainDate(2021, 7, 6); + expect(plainDate.eraYear).toBeUndefined(); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "eraYear", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.inLeapYear.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.inLeapYear.js new file mode 100644 index 00000000000..94810afc29a --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.inLeapYear.js @@ -0,0 +1,16 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.inLeapYear).toBeFalse(); + const leapDate = new Temporal.PlainDate(2020, 7, 23); + expect(leapDate.inLeapYear).toBeTrue(); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "inLeapYear", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.month.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.month.js new file mode 100644 index 00000000000..4e771f92727 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.month.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.month).toBe(7); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "month", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.monthCode.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.monthCode.js new file mode 100644 index 00000000000..e5c6a293a82 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.monthCode.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.monthCode).toBe("M07"); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "monthCode", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.monthsInYear.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.monthsInYear.js new file mode 100644 index 00000000000..44b3e3566c1 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.monthsInYear.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.monthsInYear).toBe(12); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "monthsInYear", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.weekOfYear.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.weekOfYear.js new file mode 100644 index 00000000000..ef9e8552ea9 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.weekOfYear.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.weekOfYear).toBe(29); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "weekOfYear", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.year.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.year.js new file mode 100644 index 00000000000..04ed036a3f5 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.year.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2021, 7, 23); + expect(date.year).toBe(2021); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "year", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +}); diff --git a/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.yearOfWeek.js b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.yearOfWeek.js new file mode 100644 index 00000000000..ab42d277914 --- /dev/null +++ b/Libraries/LibJS/Tests/builtins/Temporal/PlainDate/PlainDate.prototype.yearOfWeek.js @@ -0,0 +1,14 @@ +describe("correct behavior", () => { + test("basic functionality", () => { + const date = new Temporal.PlainDate(2023, 1, 1); + expect(date.yearOfWeek).toBe(2022); + }); +}); + +describe("errors", () => { + test("this value must be a Temporal.PlainDate object", () => { + expect(() => { + Reflect.get(Temporal.PlainDate.prototype, "yearOfWeek", "foo"); + }).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDate"); + }); +});