mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-30 12:49:19 +00:00
LibJS: Implement the Temporal.PlainYearMonth constructor
And the simple Temporal.PlainYearMonth.prototype getters, so that the constructed Temporal.PlainYearMonth may actually be validated.
This commit is contained in:
parent
1c733993e0
commit
b68d67693e
Notes:
github-actions[bot]
2024-11-22 18:56:56 +00:00
Author: https://github.com/trflynn89
Commit: b68d67693e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2496
27 changed files with 926 additions and 3 deletions
|
@ -217,6 +217,9 @@ set(SOURCES
|
|||
Runtime/Temporal/PlainMonthDay.cpp
|
||||
Runtime/Temporal/PlainMonthDayConstructor.cpp
|
||||
Runtime/Temporal/PlainMonthDayPrototype.cpp
|
||||
Runtime/Temporal/PlainYearMonth.cpp
|
||||
Runtime/Temporal/PlainYearMonthConstructor.cpp
|
||||
Runtime/Temporal/PlainYearMonthPrototype.cpp
|
||||
Runtime/Temporal/PlainTime.cpp
|
||||
Runtime/Temporal/Temporal.cpp
|
||||
Runtime/Temporal/TimeZone.cpp
|
||||
|
|
|
@ -87,9 +87,10 @@
|
|||
__JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \
|
||||
__JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor)
|
||||
|
||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
||||
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor)
|
||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
||||
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \
|
||||
__JS_ENUMERATE(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor)
|
||||
|
||||
#define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \
|
||||
__JS_ENUMERATE(AtomicsObject, atomics) \
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/WeakMap.h>
|
||||
|
@ -845,6 +846,15 @@ ErrorOr<void> print_temporal_plain_month_day(JS::PrintContext& print_context, JS
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_temporal_plain_year_month(JS::PrintContext& print_context, JS::Temporal::PlainYearMonth const& plain_year_month, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Temporal.PlainYearMonth"sv));
|
||||
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}\033[0m", plain_year_month.iso_date().year, plain_year_month.iso_date().month));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, JS::PrimitiveString::create(plain_year_month.vm(), plain_year_month.calendar()), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_boolean_object(JS::PrintContext& print_context, JS::BooleanObject const& boolean_object, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Boolean"sv));
|
||||
|
@ -964,6 +974,8 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, Hash
|
|||
return print_temporal_duration(print_context, static_cast<JS::Temporal::Duration&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainMonthDay>(object))
|
||||
return print_temporal_plain_month_day(print_context, static_cast<JS::Temporal::PlainMonthDay&>(object), seen_objects);
|
||||
if (is<JS::Temporal::PlainYearMonth>(object))
|
||||
return print_temporal_plain_year_month(print_context, static_cast<JS::Temporal::PlainYearMonth&>(object), seen_objects);
|
||||
return print_object(print_context, object, seen_objects);
|
||||
}
|
||||
|
||||
|
|
|
@ -103,6 +103,8 @@
|
|||
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/TypedArrayConstructor.h>
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
@ -467,6 +468,8 @@ ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
|
|||
// FIXME: Add the other types as we define them.
|
||||
if (is<PlainMonthDay>(object))
|
||||
return false;
|
||||
if (is<PlainYearMonth>(object))
|
||||
return false;
|
||||
|
||||
// 3. Let calendarProperty be ? Get(value, "calendar").
|
||||
auto calendar_property = TRY(object.get(vm.names.calendar));
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibJS/Runtime/Temporal/ISO8601.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibUnicode/Locale.h>
|
||||
|
@ -324,6 +325,8 @@ ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal
|
|||
// FIXME: Add the other calendar-holding types as we define them.
|
||||
if (is<PlainMonthDay>(temporal_calendar_object))
|
||||
return static_cast<PlainMonthDay const&>(temporal_calendar_object).calendar();
|
||||
if (is<PlainYearMonth>(temporal_calendar_object))
|
||||
return static_cast<PlainYearMonth const&>(temporal_calendar_object).calendar();
|
||||
}
|
||||
|
||||
// 2. If temporalCalendarLike is not a String, throw a TypeError exception.
|
||||
|
@ -346,6 +349,8 @@ ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&
|
|||
// FIXME: Add the other calendar-holding types as we define them.
|
||||
if (is<PlainMonthDay>(item))
|
||||
return static_cast<PlainMonthDay const&>(item).calendar();
|
||||
if (is<PlainYearMonth>(item))
|
||||
return static_cast<PlainYearMonth const&>(item).calendar();
|
||||
|
||||
// 2. Let calendarLike be ? Get(item, "calendar").
|
||||
auto calendar_like = TRY(item.get(vm.names.calendar));
|
||||
|
@ -360,6 +365,30 @@ ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&
|
|||
return TRY(to_temporal_calendar_identifier(vm, calendar_like));
|
||||
}
|
||||
|
||||
// 12.2.11 CalendarYearMonthFromFields ( calendar, fields, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-calendaryearmonthfromfields
|
||||
ThrowCompletionOr<ISODate> calendar_year_month_from_fields(VM& vm, StringView calendar, CalendarFields fields, Overflow overflow)
|
||||
{
|
||||
// 1. Perform ? CalendarResolveFields(calendar, fields, YEAR-MONTH).
|
||||
TRY(calendar_resolve_fields(vm, calendar, fields, DateType::YearMonth));
|
||||
|
||||
// FIXME: 2. Let firstDayIndex be the 1-based index of the first day of the month described by fields (i.e., 1 unless the
|
||||
// month's first day is skipped by this calendar.)
|
||||
static auto constexpr first_day_index = 1;
|
||||
|
||||
// 3. Set fields.[[Day]] to firstDayIndex.
|
||||
fields.day = first_day_index;
|
||||
|
||||
// 4. Let result be ? CalendarDateToISO(calendar, fields, overflow).
|
||||
auto result = TRY(calendar_date_to_iso(vm, calendar, fields, overflow));
|
||||
|
||||
// 5. If ISOYearMonthWithinLimits(result) is false, throw a RangeError exception.
|
||||
if (!iso_year_month_within_limits(result))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidISODate);
|
||||
|
||||
// 6. Return result.
|
||||
return result;
|
||||
}
|
||||
|
||||
// 12.2.12 CalendarMonthDayFromFields ( calendar, fields, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthdayfromfields
|
||||
ThrowCompletionOr<ISODate> calendar_month_day_from_fields(VM& vm, StringView calendar, CalendarFields fields, Overflow overflow)
|
||||
{
|
||||
|
@ -530,6 +559,25 @@ u8 iso_day_of_week(ISODate const& iso_date)
|
|||
return day_of_week;
|
||||
}
|
||||
|
||||
// 12.2.19 CalendarDateToISO ( calendar, fields, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-calendardatetoiso
|
||||
ThrowCompletionOr<ISODate> calendar_date_to_iso(VM& vm, StringView calendar, CalendarFields const& fields, Overflow overflow)
|
||||
{
|
||||
// 1. If calendar is "iso8601", then
|
||||
if (calendar == "iso8601"sv) {
|
||||
// a. Assert: fields.[[Year]], fields.[[Month]], and fields.[[Day]] are not UNSET.
|
||||
VERIFY(fields.year.has_value());
|
||||
VERIFY(fields.month.has_value());
|
||||
VERIFY(fields.day.has_value());
|
||||
|
||||
// b. Return ? RegulateISODate(fields.[[Year]], fields.[[Month]], fields.[[Day]], overflow).
|
||||
return TRY(regulate_iso_date(vm, *fields.year, *fields.month, *fields.day, overflow));
|
||||
}
|
||||
|
||||
// 2. Return an implementation-defined ISO Date Record, or throw a RangeError exception, as described below.
|
||||
// FIXME: Create an ISODateRecord based on an ISO8601 calendar for now. See also: CalendarResolveFields.
|
||||
return calendar_month_day_to_iso_reference_date(vm, "iso8601"sv, fields, overflow);
|
||||
}
|
||||
|
||||
// 12.2.20 CalendarMonthDayToISOReferenceDate ( calendar, fields, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-calendarmonthdaytoisoreferencedate
|
||||
ThrowCompletionOr<ISODate> calendar_month_day_to_iso_reference_date(VM& vm, StringView calendar, CalendarFields const& fields, Overflow overflow)
|
||||
{
|
||||
|
|
|
@ -99,6 +99,7 @@ using CalendarFieldListOrPartial = Variant<Partial, CalendarFieldList>;
|
|||
ThrowCompletionOr<String> canonicalize_calendar(VM&, StringView id);
|
||||
Vector<String> const& available_calendars();
|
||||
ThrowCompletionOr<CalendarFields> prepare_calendar_fields(VM&, StringView calendar, Object const& fields, CalendarFieldList calendar_field_names, CalendarFieldList non_calendar_field_names, CalendarFieldListOrPartial required_field_names);
|
||||
ThrowCompletionOr<ISODate> calendar_year_month_from_fields(VM&, StringView calendar, CalendarFields, Overflow);
|
||||
ThrowCompletionOr<ISODate> calendar_month_day_from_fields(VM&, StringView calendar, CalendarFields, Overflow);
|
||||
String format_calendar_annotation(StringView id, ShowCalendar);
|
||||
bool calendar_equals(StringView one, StringView two);
|
||||
|
@ -110,6 +111,7 @@ Vector<CalendarField> calendar_field_keys_present(CalendarFields const&);
|
|||
CalendarFields calendar_merge_fields(StringView calendar, CalendarFields const& fields, CalendarFields const& additional_fields);
|
||||
ThrowCompletionOr<String> to_temporal_calendar_identifier(VM&, Value temporal_calendar_like);
|
||||
ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&, Object const& item);
|
||||
ThrowCompletionOr<ISODate> calendar_date_to_iso(VM&, StringView calendar, CalendarFields const&, Overflow);
|
||||
ThrowCompletionOr<ISODate> calendar_month_day_to_iso_reference_date(VM&, StringView calendar, CalendarFields const&, Overflow);
|
||||
CalendarDate calendar_iso_to_date(StringView calendar, ISODate const&);
|
||||
Vector<CalendarField> calendar_extra_fields(StringView calendar, CalendarFieldList);
|
||||
|
|
152
Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp
Normal file
152
Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Intrinsics.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PlainYearMonth);
|
||||
|
||||
// 9 Temporal.PlainYearMonth Objects, https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-objects
|
||||
PlainYearMonth::PlainYearMonth(ISODate iso_date, String calendar, Object& prototype)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||
, m_iso_date(iso_date)
|
||||
, m_calendar(move(calendar))
|
||||
{
|
||||
}
|
||||
|
||||
// 9.5.2 ToTemporalYearMonth ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalyearmonth
|
||||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> to_temporal_year_month(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 [[InitializedTemporalYearMonth]] internal slot, then
|
||||
if (is<PlainYearMonth>(object)) {
|
||||
auto const& plain_year_month = static_cast<PlainYearMonth const&>(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 ! CreateTemporalYearMonth(item.[[ISODate]], item.[[Calendar]]).
|
||||
return MUST(create_temporal_year_month(vm, plain_year_month.iso_date(), plain_year_month.calendar()));
|
||||
}
|
||||
|
||||
// b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).
|
||||
auto calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object));
|
||||
|
||||
// c. Let fields be ? PrepareCalendarFields(calendar, item, « YEAR, MONTH, MONTH-CODE », «», «»).
|
||||
auto fields = TRY(prepare_calendar_fields(vm, calendar, object, { { CalendarField::Year, CalendarField::Month, CalendarField::MonthCode } }, {}, CalendarFieldList {}));
|
||||
|
||||
// d. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, options));
|
||||
|
||||
// e. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
|
||||
auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
|
||||
|
||||
// f. Let isoDate be ? CalendarYearMonthFromFields(calendar, fields, overflow).
|
||||
auto iso_date = TRY(calendar_year_month_from_fields(vm, calendar, move(fields), overflow));
|
||||
|
||||
// g. Return ! CreateTemporalYearMonth(isoDate, calendar).
|
||||
return MUST(create_temporal_year_month(vm, iso_date, move(calendar)));
|
||||
}
|
||||
|
||||
// 3. If item is not a String, throw a TypeError exception.
|
||||
if (!item.is_string())
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainYearMonth);
|
||||
|
||||
// 4. Let result be ? ParseISODateTime(item, « TemporalYearMonthString »).
|
||||
auto parse_result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalYearMonthString } }));
|
||||
|
||||
// 5. Let calendar be result.[[Calendar]].
|
||||
// 6. If calendar is empty, set calendar to "iso8601".
|
||||
auto calendar = parse_result.calendar.value_or("iso8601"_string);
|
||||
|
||||
// 7. Set calendar to ? CanonicalizeCalendar(calendar).
|
||||
calendar = TRY(canonicalize_calendar(vm, calendar));
|
||||
|
||||
// 8. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
|
||||
auto iso_date = create_iso_date_record(*parse_result.year, parse_result.month, parse_result.day);
|
||||
|
||||
// 9. Set result to ISODateToFields(calendar, isoDate, YEAR-MONTH).
|
||||
auto result = iso_date_to_fields(calendar, iso_date, DateType::YearMonth);
|
||||
|
||||
// 10. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, options));
|
||||
|
||||
// 11. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||||
|
||||
// 12. NOTE: The following operation is called with CONSTRAIN regardless of the value of overflow, in order for the
|
||||
// calendar to store a canonical value in the [[Day]] field of the [[ISODate]] internal slot of the result.
|
||||
// 13. Set isoDate to ? CalendarYearMonthFromFields(calendar, result, CONSTRAIN).
|
||||
iso_date = TRY(calendar_year_month_from_fields(vm, calendar, result, Overflow::Constrain));
|
||||
|
||||
// 14. Return ! CreateTemporalYearMonth(isoDate, calendar).
|
||||
return MUST(create_temporal_year_month(vm, iso_date, move(calendar)));
|
||||
}
|
||||
|
||||
// 9.5.3 ISOYearMonthWithinLimits ( isoDate ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthwithinlimits
|
||||
bool iso_year_month_within_limits(ISODate iso_date)
|
||||
{
|
||||
// 1. If isoDate.[[Year]] < -271821 or isoDate.[[Year]] > 275760, then
|
||||
if (iso_date.year < -271821 || iso_date.year > 275760) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2. If isoDate.[[Year]] = -271821 and isoDate.[[Month]] < 4, then
|
||||
if (iso_date.year == -271821 && iso_date.month < 4) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. If isoDate.[[Year]] = 275760 and isoDate.[[Month]] > 9, then
|
||||
if (iso_date.year == 275760 && iso_date.month > 9) {
|
||||
// a. Return false.
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Return true.
|
||||
return true;
|
||||
}
|
||||
|
||||
// 9.5.5 CreateTemporalYearMonth ( isoDate, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalyearmonth
|
||||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> create_temporal_year_month(VM& vm, ISODate iso_date, String calendar, GC::Ptr<FunctionObject> new_target)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. If ISOYearMonthWithinLimits(isoDate) is false, throw a RangeError exception.
|
||||
if (!iso_year_month_within_limits(iso_date))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
|
||||
|
||||
// 2. If newTarget is not present, set newTarget to %Temporal.PlainYearMonth%.
|
||||
if (!new_target)
|
||||
new_target = realm.intrinsics().temporal_plain_year_month_constructor();
|
||||
|
||||
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainYearMonth.prototype%", « [[InitializedTemporalYearMonth]], [[ISODate]], [[Calendar]] »).
|
||||
// 4. Set object.[[ISODate]] to isoDate.
|
||||
// 5. Set object.[[Calendar]] to calendar.
|
||||
auto object = TRY(ordinary_create_from_constructor<PlainYearMonth>(vm, *new_target, &Intrinsics::temporal_plain_year_month_prototype, iso_date, move(calendar)));
|
||||
|
||||
// 6. Return object.
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
39
Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h
Normal file
39
Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainYearMonth final : public Object {
|
||||
JS_OBJECT(PlainYearMonth, Object);
|
||||
GC_DECLARE_ALLOCATOR(PlainYearMonth);
|
||||
|
||||
public:
|
||||
virtual ~PlainYearMonth() override = default;
|
||||
|
||||
[[nodiscard]] ISODate iso_date() const { return m_iso_date; }
|
||||
[[nodiscard]] String const& calendar() const { return m_calendar; }
|
||||
|
||||
private:
|
||||
PlainYearMonth(ISODate, String calendar, Object& prototype);
|
||||
|
||||
ISODate m_iso_date; // [[ISODate]]
|
||||
String m_calendar; // [[Calendar]]
|
||||
};
|
||||
|
||||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> to_temporal_year_month(VM&, Value item, Value options = js_undefined());
|
||||
bool iso_year_month_within_limits(ISODate);
|
||||
ThrowCompletionOr<GC::Ref<PlainYearMonth>> create_temporal_year_month(VM&, ISODate, String calendar, GC::Ptr<FunctionObject> new_target = {});
|
||||
|
||||
}
|
116
Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.cpp
Normal file
116
Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PlainYearMonthConstructor);
|
||||
|
||||
// 9.1 The Temporal.PlainYearMonth Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-constructor
|
||||
PlainYearMonthConstructor::PlainYearMonthConstructor(Realm& realm)
|
||||
: NativeFunction(realm.vm().names.PlainYearMonth.as_string(), realm.intrinsics().function_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void PlainYearMonthConstructor::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 9.2.1 Temporal.PlainYearMonth.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype
|
||||
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_year_month_prototype(), 0);
|
||||
|
||||
define_direct_property(vm.names.length, Value(2), Attribute::Configurable);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 9.1.1 Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendar [ , referenceISODay ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth
|
||||
ThrowCompletionOr<Value> PlainYearMonthConstructor::call()
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 1. If NewTarget is undefined, then
|
||||
// a. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainYearMonth");
|
||||
}
|
||||
|
||||
// 9.1.1 Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendar [ , referenceISODay ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth
|
||||
ThrowCompletionOr<GC::Ref<Object>> PlainYearMonthConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
auto iso_year = vm.argument(0);
|
||||
auto iso_month = vm.argument(1);
|
||||
auto calendar_value = vm.argument(2);
|
||||
auto reference_iso_day = vm.argument(3);
|
||||
|
||||
// 2. If referenceISODay is undefined, then
|
||||
if (reference_iso_day.is_undefined()) {
|
||||
// a. Set referenceISODay to 1𝔽.
|
||||
reference_iso_day = Value { 1 };
|
||||
}
|
||||
|
||||
// 3. Let y be ? ToIntegerWithTruncation(isoYear).
|
||||
auto year = TRY(to_integer_with_truncation(vm, iso_year, ErrorType::TemporalInvalidPlainYearMonth));
|
||||
|
||||
// 4. Let m be ? ToIntegerWithTruncation(isoMonth).
|
||||
auto month = TRY(to_integer_with_truncation(vm, iso_month, ErrorType::TemporalInvalidPlainYearMonth));
|
||||
|
||||
// 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<TypeError>(ErrorType::NotAString, "calendar"sv);
|
||||
|
||||
// 7. Set calendar to ? CanonicalizeCalendar(calendar).
|
||||
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string_view()));
|
||||
|
||||
// 8. Let ref be ? ToIntegerWithTruncation(referenceISODay).
|
||||
auto reference = TRY(to_integer_with_truncation(vm, reference_iso_day, ErrorType::TemporalInvalidPlainYearMonth));
|
||||
|
||||
// 9. If IsValidISODate(y, m, ref) is false, throw a RangeError exception.
|
||||
if (!is_valid_iso_date(year, month, reference))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
|
||||
|
||||
// 10. Let isoDate be CreateISODateRecord(y, m, ref).
|
||||
auto iso_date = create_iso_date_record(year, month, reference);
|
||||
|
||||
// 11. Return ? CreateTemporalYearMonth(isoDate, calendar, NewTarget).
|
||||
return TRY(create_temporal_year_month(vm, iso_date, move(calendar), &new_target));
|
||||
}
|
||||
|
||||
// 9.2.2 Temporal.PlainYearMonth.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.from
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthConstructor::from)
|
||||
{
|
||||
// 1. Return ? ToTemporalYearMonth(item, options).
|
||||
return TRY(to_temporal_year_month(vm, vm.argument(0), vm.argument(1)));
|
||||
}
|
||||
|
||||
// 9.2.3 Temporal.PlainYearMonth.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.compare
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthConstructor::compare)
|
||||
{
|
||||
// 1. Set one to ? ToTemporalYearMonth(one).
|
||||
auto one = TRY(to_temporal_year_month(vm, vm.argument(0)));
|
||||
|
||||
// 2. Set two to ? ToTemporalYearMonth(two).
|
||||
auto two = TRY(to_temporal_year_month(vm, vm.argument(1)));
|
||||
|
||||
// 3. Return 𝔽(CompareISODate(one.[[ISODate]], two.[[ISODate]])).
|
||||
return compare_iso_date(one->iso_date(), two->iso_date());
|
||||
}
|
||||
|
||||
}
|
34
Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.h
Normal file
34
Libraries/LibJS/Runtime/Temporal/PlainYearMonthConstructor.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainYearMonthConstructor final : public NativeFunction {
|
||||
JS_OBJECT(PlainYearMonthConstructor, NativeFunction);
|
||||
GC_DECLARE_ALLOCATOR(PlainYearMonthConstructor);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~PlainYearMonthConstructor() override = default;
|
||||
|
||||
virtual ThrowCompletionOr<Value> call() override;
|
||||
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
|
||||
|
||||
private:
|
||||
explicit PlainYearMonthConstructor(Realm&);
|
||||
|
||||
virtual bool has_constructor() const override { return true; }
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(from);
|
||||
JS_DECLARE_NATIVE_FUNCTION(compare);
|
||||
};
|
||||
|
||||
}
|
126
Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp
Normal file
126
Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(PlainYearMonthPrototype);
|
||||
|
||||
// 9.3 Properties of the Temporal.PlainYearMonth Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plainyearmonth-prototype-object
|
||||
PlainYearMonthPrototype::PlainYearMonthPrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void PlainYearMonthPrototype::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 9.3.2 Temporal.PlainYearMonth.prototype[ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype-%symbol.tostringtag%
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.PlainYearMonth"_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.daysInYear, days_in_year_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.daysInMonth, days_in_month_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);
|
||||
}
|
||||
|
||||
// 9.3.3 get Temporal.PlainYearMonth.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.calendarid
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::calendar_id_getter)
|
||||
{
|
||||
// 1. Let yearMonth be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
||||
auto year_month = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return yearMonth.[[Calendar]].
|
||||
return PrimitiveString::create(vm, year_month->calendar());
|
||||
}
|
||||
|
||||
// 9.3.4 get Temporal.PlainYearMonth.prototype.era, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.era
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::era_getter)
|
||||
{
|
||||
// 1. Let plainYearMonth be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(plainYearMonth, [[InitializedTemporalYearMonth]]).
|
||||
auto year_month = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return CalendarISOToDate(plainYearMonth.[[Calendar]], plainYearMonth.[[ISODate]]).[[Era]].
|
||||
auto result = calendar_iso_to_date(year_month->calendar(), year_month->iso_date()).era;
|
||||
|
||||
if (!result.has_value())
|
||||
return js_undefined();
|
||||
|
||||
return PrimitiveString::create(vm, result.release_value());
|
||||
}
|
||||
|
||||
// 9.3.5 get Temporal.PlainYearMonth.prototype.eraYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.erayear
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::era_year_getter)
|
||||
{
|
||||
// 1. Let plainYearMonth be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(plainYearMonth, [[InitializedTemporalYearMonth]]).
|
||||
auto year_month = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let result be CalendarISOToDate(plainYearMonth.[[Calendar]], plainYearMonth.[[ISODate]]).[[EraYear]].
|
||||
auto result = calendar_iso_to_date(year_month->calendar(), year_month->iso_date()).era_year;
|
||||
|
||||
// 4. If result is undefined, return undefined.
|
||||
if (!result.has_value())
|
||||
return js_undefined();
|
||||
|
||||
// 5. Return 𝔽(result).
|
||||
return *result;
|
||||
}
|
||||
|
||||
#define JS_ENUMERATE_PLAIN_MONTH_YEAR_SIMPLE_FIELDS \
|
||||
__JS_ENUMERATE(year) \
|
||||
__JS_ENUMERATE(month) \
|
||||
__JS_ENUMERATE(days_in_year) \
|
||||
__JS_ENUMERATE(days_in_month) \
|
||||
__JS_ENUMERATE(months_in_year) \
|
||||
__JS_ENUMERATE(in_leap_year)
|
||||
|
||||
// 9.3.6 get Temporal.PlainYearMonth.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.year
|
||||
// 9.3.7 get Temporal.PlainYearMonth.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.month
|
||||
// 9.3.9 get Temporal.PlainYearMonth.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.daysinyear
|
||||
// 9.3.10 get Temporal.PlainYearMonth.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.daysinmonth
|
||||
// 9.3.11 get Temporal.PlainYearMonth.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.monthsinyear
|
||||
// 9.3.12 get Temporal.PlainYearMonth.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.inleapyear
|
||||
#define __JS_ENUMERATE(field) \
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::field##_getter) \
|
||||
{ \
|
||||
/* 1. Let yearMonth be the this value. */ \
|
||||
/* 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]). */ \
|
||||
auto year_month = TRY(typed_this_object(vm)); \
|
||||
\
|
||||
/* 3. Return CalendarISOToDate(yearMonth.[[Calendar]], yearMonth.[[ISODate]]).[[<field>]]. */ \
|
||||
return calendar_iso_to_date(year_month->calendar(), year_month->iso_date()).field; \
|
||||
}
|
||||
JS_ENUMERATE_PLAIN_MONTH_YEAR_SIMPLE_FIELDS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
// 9.3.8 get Temporal.PlainYearMonth.prototype.monthCode, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.monthcode
|
||||
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::month_code_getter)
|
||||
{
|
||||
// 1. Let yearMonth be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
|
||||
auto year_month = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return CalendarISOToDate(yearMonth.[[Calendar]], yearMonth.[[ISODate]]).[[MonthCode]].
|
||||
auto month_code = calendar_iso_to_date(year_month->calendar(), year_month->iso_date()).month_code;
|
||||
return PrimitiveString::create(vm, move(month_code));
|
||||
}
|
||||
|
||||
}
|
38
Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.h
Normal file
38
Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/PrototypeObject.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class PlainYearMonthPrototype final : public PrototypeObject<PlainYearMonthPrototype, PlainYearMonth> {
|
||||
JS_PROTOTYPE_OBJECT(PlainYearMonthPrototype, PlainYearMonth, Temporal.PlainYearMonth);
|
||||
GC_DECLARE_ALLOCATOR(PlainYearMonthPrototype);
|
||||
|
||||
public:
|
||||
virtual void initialize(Realm&) override;
|
||||
virtual ~PlainYearMonthPrototype() override = default;
|
||||
|
||||
private:
|
||||
explicit PlainYearMonthPrototype(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(days_in_year_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(days_in_month_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(months_in_year_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(in_leap_year_getter);
|
||||
};
|
||||
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
@ -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.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(); });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 2", () => {
|
||||
expect(Temporal.PlainYearMonth.compare).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth1 = new Temporal.PlainYearMonth(2021, 8);
|
||||
expect(Temporal.PlainYearMonth.compare(plainYearMonth1, plainYearMonth1)).toBe(0);
|
||||
const plainYearMonth2 = new Temporal.PlainYearMonth(2021, 9);
|
||||
expect(Temporal.PlainYearMonth.compare(plainYearMonth2, plainYearMonth2)).toBe(0);
|
||||
expect(Temporal.PlainYearMonth.compare(plainYearMonth1, plainYearMonth2)).toBe(-1);
|
||||
expect(Temporal.PlainYearMonth.compare(plainYearMonth2, plainYearMonth1)).toBe(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,130 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Temporal.PlainYearMonth.from).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("PlainYearMonth instance argument", () => {
|
||||
const plainYearMonth_ = new Temporal.PlainYearMonth(2021, 7);
|
||||
const plainYearMonth = Temporal.PlainYearMonth.from(plainYearMonth_);
|
||||
expect(plainYearMonth.year).toBe(2021);
|
||||
expect(plainYearMonth.month).toBe(7);
|
||||
expect(plainYearMonth.monthCode).toBe("M07");
|
||||
expect(plainYearMonth.daysInYear).toBe(365);
|
||||
expect(plainYearMonth.daysInMonth).toBe(31);
|
||||
expect(plainYearMonth.monthsInYear).toBe(12);
|
||||
expect(plainYearMonth.inLeapYear).toBeFalse();
|
||||
});
|
||||
|
||||
test("fields object argument", () => {
|
||||
const object = {
|
||||
year: 2021,
|
||||
month: 7,
|
||||
};
|
||||
const plainYearMonth = Temporal.PlainYearMonth.from(object);
|
||||
expect(plainYearMonth.year).toBe(2021);
|
||||
expect(plainYearMonth.month).toBe(7);
|
||||
expect(plainYearMonth.monthCode).toBe("M07");
|
||||
expect(plainYearMonth.daysInYear).toBe(365);
|
||||
expect(plainYearMonth.daysInMonth).toBe(31);
|
||||
expect(plainYearMonth.monthsInYear).toBe(12);
|
||||
expect(plainYearMonth.inLeapYear).toBeFalse();
|
||||
});
|
||||
|
||||
test("from year month string", () => {
|
||||
const plainYearMonth = Temporal.PlainYearMonth.from("2021-07");
|
||||
expect(plainYearMonth.year).toBe(2021);
|
||||
expect(plainYearMonth.month).toBe(7);
|
||||
expect(plainYearMonth.monthCode).toBe("M07");
|
||||
});
|
||||
|
||||
test("from date time string", () => {
|
||||
const plainYearMonth = Temporal.PlainYearMonth.from("2021-07-06T23:42:01");
|
||||
expect(plainYearMonth.year).toBe(2021);
|
||||
expect(plainYearMonth.month).toBe(7);
|
||||
expect(plainYearMonth.monthCode).toBe("M07");
|
||||
expect(plainYearMonth.daysInYear).toBe(365);
|
||||
expect(plainYearMonth.daysInMonth).toBe(31);
|
||||
expect(plainYearMonth.monthsInYear).toBe(12);
|
||||
expect(plainYearMonth.inLeapYear).toBeFalse();
|
||||
});
|
||||
|
||||
test("compares calendar name in year month string in lowercase", () => {
|
||||
const values = [
|
||||
"2023-02[u-ca=iso8601]",
|
||||
"2023-02[u-ca=isO8601]",
|
||||
"2023-02[u-ca=iSo8601]",
|
||||
"2023-02[u-ca=iSO8601]",
|
||||
"2023-02[u-ca=Iso8601]",
|
||||
"2023-02[u-ca=IsO8601]",
|
||||
"2023-02[u-ca=ISo8601]",
|
||||
"2023-02[u-ca=ISO8601]",
|
||||
];
|
||||
|
||||
for (const value of values) {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from(value);
|
||||
}).not.toThrowWithMessage(
|
||||
RangeError,
|
||||
"YYYY-MM string format can only be used with the iso8601 calendar"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("missing fields", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from({});
|
||||
}).toThrowWithMessage(TypeError, "Required property year is missing or undefined");
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from({ year: 0 });
|
||||
}).toThrowWithMessage(TypeError, "Required property month is missing or undefined");
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from({ month: 1 });
|
||||
}).toThrowWithMessage(TypeError, "Required property year is missing or undefined");
|
||||
});
|
||||
|
||||
test("invalid year month string", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from("foo");
|
||||
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
|
||||
});
|
||||
|
||||
test("string must not contain a UTC designator", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from("2021-07-06T23:42:01Z");
|
||||
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
|
||||
});
|
||||
|
||||
test("extended year must not be negative zero", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from("-000000-01");
|
||||
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from("−000000-01"); // U+2212
|
||||
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
|
||||
});
|
||||
|
||||
test("can only use iso8601 calendar with year month strings", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from("2023-02[u-ca=iso8602]");
|
||||
}).toThrowWithMessage(RangeError, "Invalid calendar identifier 'iso8602'");
|
||||
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth.from("2023-02[u-ca=ladybird]");
|
||||
}).toThrowWithMessage(RangeError, "Invalid calendar identifier 'ladybird'");
|
||||
});
|
||||
|
||||
test("doesn't throw non-iso8601 calendar error when using a superset format string such as DateTime", () => {
|
||||
// NOTE: This will still throw, but only because "ladybird" is not a recognised calendar, not because of the string format restriction.
|
||||
try {
|
||||
Temporal.PlainYearMonth.from("2023-02-10T22:57[u-ca=ladybird]");
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(RangeError);
|
||||
expect(e.message).not.toBe(
|
||||
"MM-DD string format can only be used with the iso8601 calendar"
|
||||
);
|
||||
expect(e.message).toBe("Invalid calendar identifier 'ladybird'");
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,60 @@
|
|||
describe("errors", () => {
|
||||
test("called without new", () => {
|
||||
expect(() => {
|
||||
Temporal.PlainYearMonth();
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
"Temporal.PlainYearMonth constructor must be called with 'new'"
|
||||
);
|
||||
});
|
||||
|
||||
test("cannot pass Infinity", () => {
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(0, Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(0, 1, undefined, Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(-Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(0, -Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(0, 1, undefined, -Infinity);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
});
|
||||
|
||||
test("cannot pass invalid ISO month/day", () => {
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(0, 0);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
expect(() => {
|
||||
new Temporal.PlainYearMonth(0, 1, undefined, 0);
|
||||
}).toThrowWithMessage(RangeError, "Invalid plain year month");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("length is 2", () => {
|
||||
expect(Temporal.PlainYearMonth).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(typeof plainYearMonth).toBe("object");
|
||||
expect(plainYearMonth).toBeInstanceOf(Temporal.PlainYearMonth);
|
||||
expect(Object.getPrototypeOf(plainYearMonth)).toBe(Temporal.PlainYearMonth.prototype);
|
||||
});
|
||||
|
||||
// FIXME: Re-implement this test with Temporal.PlainYearMonth.prototype.toString({ calendarName: "always" }).
|
||||
// test("default reference day is 1", () => {
|
||||
// const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
// const fields = plainYearMonth.getISOFields();
|
||||
// expect(fields.isoDay).toBe(1);
|
||||
// });
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("calendarId basic functionality", () => {
|
||||
const calendar = "iso8601";
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2000, 5, calendar);
|
||||
expect(plainYearMonth.calendarId).toBe("iso8601");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "calendarId", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.daysInMonth).toBe(31);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "daysInMonth", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.daysInYear).toBe(365);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "daysInYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.era).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "era", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.eraYear).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "eraYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.inLeapYear).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "inLeapYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.month).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "month", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.monthCode).toBe("M07");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "monthCode", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.monthsInYear).toBe(12);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "monthsInYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const plainYearMonth = new Temporal.PlainYearMonth(2021, 7);
|
||||
expect(plainYearMonth.year).toBe(2021);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.PlainYearMonth object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.PlainYearMonth.prototype, "year", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainYearMonth");
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue