1
0
Fork 0
mirror of https://github.com/LadybirdBrowser/ladybird.git synced 2025-06-10 01:51:03 +09:00

LibJS: Implement the Temporal.PlainDateTime constructor

And the simple Temporal.PlainDateTime.prototype getters, so that the
constructed Temporal.PlainDateTime may actually be validated.
This commit is contained in:
Timothy Flynn 2024-11-23 18:00:05 -05:00 committed by Andreas Kling
parent a61cdeba24
commit 029b6ad1a8
Notes: github-actions[bot] 2024-11-24 10:45:50 +00:00
41 changed files with 1210 additions and 13 deletions

View file

@ -216,6 +216,8 @@ set(SOURCES
Runtime/Temporal/PlainDateConstructor.cpp
Runtime/Temporal/PlainDatePrototype.cpp
Runtime/Temporal/PlainDateTime.cpp
Runtime/Temporal/PlainDateTimeConstructor.cpp
Runtime/Temporal/PlainDateTimePrototype.cpp
Runtime/Temporal/PlainMonthDay.cpp
Runtime/Temporal/PlainMonthDayConstructor.cpp
Runtime/Temporal/PlainMonthDayPrototype.cpp

View file

@ -90,6 +90,7 @@
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
__JS_ENUMERATE(PlainDateTime, plain_date_time, PlainDateTimePrototype, PlainDateTimeConstructor) \
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \
__JS_ENUMERATE(PlainTime, plain_time, PlainTimePrototype, PlainTimeConstructor) \
__JS_ENUMERATE(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor)

View file

@ -49,6 +49,7 @@
#include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
@ -848,6 +849,15 @@ ErrorOr<void> print_temporal_plain_date(JS::PrintContext& print_context, JS::Tem
return {};
}
ErrorOr<void> print_temporal_plain_date_time(JS::PrintContext& print_context, JS::Temporal::PlainDateTime const& plain_date_time, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.PlainDateTime"sv));
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}{:03}{:03}\033[0m", plain_date_time.iso_date_time().iso_date.year, plain_date_time.iso_date_time().iso_date.month, plain_date_time.iso_date_time().iso_date.day, plain_date_time.iso_date_time().time.hour, plain_date_time.iso_date_time().time.minute, plain_date_time.iso_date_time().time.second, plain_date_time.iso_date_time().time.millisecond, plain_date_time.iso_date_time().time.microsecond, plain_date_time.iso_date_time().time.nanosecond));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, JS::PrimitiveString::create(plain_date_time.vm(), plain_date_time.calendar()), seen_objects));
return {};
}
ErrorOr<void> print_temporal_plain_month_day(JS::PrintContext& print_context, JS::Temporal::PlainMonthDay const& plain_month_day, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.PlainMonthDay"sv));
@ -992,6 +1002,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::PlainDate>(object))
return print_temporal_plain_date(print_context, static_cast<JS::Temporal::PlainDate&>(object), seen_objects);
if (is<JS::Temporal::PlainDateTime>(object))
return print_temporal_plain_date_time(print_context, static_cast<JS::Temporal::PlainDateTime&>(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::PlainTime>(object))

View file

@ -103,6 +103,8 @@
#include <LibJS/Runtime/Temporal/DurationPrototype.h>
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDateTimePrototype.h>
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
#include <LibJS/Runtime/Temporal/PlainMonthDayPrototype.h>
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>

View file

@ -431,11 +431,18 @@ ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM& vm, Object con
return RelativeTo { .plain_relative_to = static_cast<PlainDate&>(object), .zoned_relative_to = {} };
}
// FIXME: c. If value has an [[InitializedTemporalDateTime]] internal slot, then
// FIXME: i. Let plainDate be ! CreateTemporalDate(value.[[ISODateTime]].[[ISODate]], value.[[Calendar]]).
// FIXME: ii. Return the Record { [[PlainRelativeTo]]: plainDate, [[ZonedRelativeTo]]: undefined }.
// c. If value has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(object)) {
auto const& plain_date_time = static_cast<PlainDateTime const&>(object);
// FIXME: d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(value).
// i. Let plainDate be ! CreateTemporalDate(value.[[ISODateTime]].[[ISODate]], value.[[Calendar]]).
auto plain_date = MUST(create_temporal_date(vm, plain_date_time.iso_date_time().iso_date, plain_date_time.calendar()));
// ii. Return the Record { [[PlainRelativeTo]]: plainDate, [[ZonedRelativeTo]]: undefined }.
return RelativeTo { .plain_relative_to = plain_date, .zoned_relative_to = {} };
}
// d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(value).
calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object));
// e. Let fields be ? PrepareCalendarFields(calendar, value, « YEAR, MONTH, MONTH-CODE, DAY », « HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND, OFFSET, TIME-ZONE », «»).
@ -626,6 +633,8 @@ ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
// FIXME: Add the other types as we define them.
if (is<PlainDate>(object))
return false;
if (is<PlainDateTime>(object))
return false;
if (is<PlainMonthDay>(object))
return false;
if (is<PlainTime>(object))

View file

@ -15,6 +15,7 @@
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>
#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>
@ -454,6 +455,8 @@ ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal
// FIXME: Add the other calendar-holding types as we define them.
if (is<PlainDate>(temporal_calendar_object))
return static_cast<PlainDate const&>(temporal_calendar_object).calendar();
if (is<PlainDateTime>(temporal_calendar_object))
return static_cast<PlainDateTime const&>(temporal_calendar_object).calendar();
if (is<PlainMonthDay>(temporal_calendar_object))
return static_cast<PlainMonthDay const&>(temporal_calendar_object).calendar();
if (is<PlainYearMonth>(temporal_calendar_object))
@ -480,6 +483,8 @@ ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&
// FIXME: Add the other calendar-holding types as we define them.
if (is<PlainDate>(item))
return static_cast<PlainDate const&>(item).calendar();
if (is<PlainDateTime>(item))
return static_cast<PlainDateTime const&>(item).calendar();
if (is<PlainMonthDay>(item))
return static_cast<PlainMonthDay const&>(item).calendar();
if (is<PlainYearMonth>(item))

View file

@ -83,15 +83,26 @@ ThrowCompletionOr<GC::Ref<PlainDate>> to_temporal_date(VM& vm, Value item, Value
// 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]]).
// c. If item has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(object)) {
auto const& plain_date_time = static_cast<PlainDateTime 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 ! CreateTemporalDate(item.[[ISODateTime]].[[ISODate]], item.[[Calendar]]).
return MUST(create_temporal_date(vm, plain_date_time.iso_date_time().iso_date, plain_date_time.calendar()));
}
// d. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).
auto calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object));

View file

@ -6,15 +6,26 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDateTime);
PlainDateTime::PlainDateTime(ISODateTime const& iso_date_time, String calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_iso_date_time(iso_date_time)
, m_calendar(move(calendar))
{
}
// 5.5.3 CombineISODateAndTimeRecord ( isoDate, time ), https://tc39.es/proposal-temporal/#sec-temporal-combineisodateandtimerecord
ISODateTime combine_iso_date_and_time_record(ISODate iso_date, Time time)
{
@ -68,6 +79,106 @@ ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(VM& vm, Strin
return combine_iso_date_and_time_record(iso_date, time);
}
// 5.5.6 ToTemporalDateTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldatetime
ThrowCompletionOr<GC::Ref<PlainDateTime>> to_temporal_date_time(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 [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(object)) {
auto const& plain_date_time = static_cast<PlainDateTime 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 ! CreateTemporalDateTime(item.[[ISODateTime]], item.[[Calendar]]).
return MUST(create_temporal_date_time(vm, plain_date_time.iso_date_time(), plain_date_time.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 ! CreateTemporalDateTime(isoDateTime, item.[[Calendar]]).
// c. If item has an [[InitializedTemporalDate]] internal slot, then
if (is<PlainDate>(object)) {
auto const& plain_date = static_cast<PlainDate 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. Let isoDateTime be CombineISODateAndTimeRecord(item.[[ISODate]], MidnightTimeRecord()).
auto iso_date_time = combine_iso_date_and_time_record(plain_date.iso_date(), midnight_time_record());
// iv. Return ? CreateTemporalDateTime(isoDateTime, item.[[Calendar]]).
return TRY(create_temporal_date_time(vm, iso_date_time, plain_date.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 », « HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND », «»).
static constexpr auto calendar_field_names = to_array({ CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day });
static constexpr auto non_calendar_field_names = to_array({ CalendarField::Hour, CalendarField::Minute, CalendarField::Second, CalendarField::Millisecond, CalendarField::Microsecond, CalendarField::Nanosecond });
auto fields = TRY(prepare_calendar_fields(vm, calendar, object, calendar_field_names, non_calendar_field_names, 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 result be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
auto result = TRY(interpret_temporal_date_time_fields(vm, calendar, fields, overflow));
// i. Return ? CreateTemporalDateTime(result, calendar).
return TRY(create_temporal_date_time(vm, result, move(calendar)));
}
// 3. If item is not a String, throw a TypeError exception.
if (!item.is_string())
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidPlainDateTime);
// 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. If result.[[Time]] is START-OF-DAY, let time be MidnightTimeRecord(); else let time be result.[[Time]].
auto time = result.time.has<ParsedISODateTime::StartOfDay>() ? midnight_time_record() : result.time.get<Time>();
// 6. Let calendar be result.[[Calendar]].
// 7. If calendar is empty, set calendar to "iso8601".
auto calendar = result.calendar.value_or("iso8601"_string);
// 8. Set calendar to ? CanonicalizeCalendar(calendar).
calendar = TRY(canonicalize_calendar(vm, calendar));
// 9. Let resolvedOptions be ? GetOptionsObject(options).
auto resolved_options = TRY(get_options_object(vm, options));
// 10. Perform ? GetTemporalOverflowOption(resolvedOptions).
TRY(get_temporal_overflow_option(vm, resolved_options));
// 11. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
auto iso_date = create_iso_date_record(*result.year, result.month, result.day);
// 12. Let isoDateTime be CombineISODateAndTimeRecord(isoDate, time).
auto iso_date_time = combine_iso_date_and_time_record(iso_date, time);
// 13. Return ? CreateTemporalDateTime(isoDateTime, calendar).
return TRY(create_temporal_date_time(vm, iso_date_time, move(calendar)));
}
// 5.5.7 BalanceISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodatetime
ISODateTime balance_iso_date_time(double year, double month, double day, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
{
@ -81,6 +192,30 @@ ISODateTime balance_iso_date_time(double year, double month, double day, double
return combine_iso_date_and_time_record(balanced_date, balanced_time);
}
// 5.5.8 CreateTemporalDateTime ( isoDateTime, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldatetime
ThrowCompletionOr<GC::Ref<PlainDateTime>> create_temporal_date_time(VM& vm, ISODateTime const& iso_date_time, String calendar, GC::Ptr<FunctionObject> new_target)
{
auto& realm = *vm.current_realm();
// 1. If ISODateTimeWithinLimits(isoDateTime) is false, then
if (!iso_date_time_within_limits(iso_date_time)) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDateTime);
}
// 2. If newTarget is not present, set newTarget to %Temporal.PlainDateTime%.
if (!new_target)
new_target = realm.intrinsics().temporal_plain_date_time_constructor();
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDateTime.prototype%", « [[InitializedTemporalDateTime]], [[ISODateTime]], [[Calendar]] »).
// 4. Set object.[[ISODateTime]] to isoDateTime.
// 5. Set object.[[Calendar]] to calendar.
auto object = TRY(ordinary_create_from_constructor<PlainDateTime>(vm, *new_target, &Intrinsics::temporal_plain_date_time_prototype, iso_date_time, move(calendar)));
// 6. Return object.
return object;
}
// 5.5.10 CompareISODateTime ( isoDateTime1, isoDateTime2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodatetime
i8 compare_iso_date_time(ISODateTime const& iso_date_time1, ISODateTime const& iso_date_time2)
{

View file

@ -9,15 +9,35 @@
#pragma once
#include <LibCrypto/BigFraction/BigFraction.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/ISORecords.h>
namespace JS::Temporal {
class PlainDateTime final : public Object {
JS_OBJECT(PlainDateTime, Object);
GC_DECLARE_ALLOCATOR(PlainDateTime);
public:
virtual ~PlainDateTime() override = default;
[[nodiscard]] ISODateTime iso_date_time() const { return m_iso_date_time; }
[[nodiscard]] String const& calendar() const { return m_calendar; }
private:
PlainDateTime(ISODateTime const&, String calendar, Object& prototype);
ISODateTime m_iso_date_time; // [[ISODateTime]]
String m_calendar; // [[Calendar]]
};
ISODateTime combine_iso_date_and_time_record(ISODate, Time);
bool iso_date_time_within_limits(ISODateTime);
ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(VM&, StringView calendar, CalendarFields&, Overflow);
ThrowCompletionOr<GC::Ref<PlainDateTime>> to_temporal_date_time(VM&, Value item, Value options = js_undefined());
ISODateTime balance_iso_date_time(double year, double month, double day, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
ThrowCompletionOr<GC::Ref<PlainDateTime>> create_temporal_date_time(VM&, ISODateTime const&, String calendar, GC::Ptr<FunctionObject> new_target = {});
i8 compare_iso_date_time(ISODateTime const&, ISODateTime const&);
ThrowCompletionOr<InternalDuration> difference_iso_date_time(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit largest_unit);
ThrowCompletionOr<InternalDuration> difference_plain_date_time_with_rounding(VM&, ISODateTime const&, ISODateTime const&, StringView calendar, Unit largest_unit, u64 rounding_increment, Unit smallest_unit, RoundingMode);

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Checked.h>
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDateTimeConstructor);
// 5.1 The Temporal.PlainDateTime Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-constructor
PlainDateTimeConstructor::PlainDateTimeConstructor(Realm& realm)
: NativeFunction(realm.vm().names.PlainDateTime.as_string(), realm.intrinsics().function_prototype())
{
}
void PlainDateTimeConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 5.2.1 Temporal.PlainDateTime.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_date_time_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);
}
// 5.1.1 Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ , second [ , millisecond [ , microsecond [ , nanosecond [ , calendar ] ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime
ThrowCompletionOr<Value> PlainDateTimeConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainDateTime");
}
// 5.1.1 Temporal.PlainDateTime ( isoYear, isoMonth, isoDay [ , hour [ , minute [ , second [ , millisecond [ , microsecond [ , nanosecond [ , calendar ] ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime
ThrowCompletionOr<GC::Ref<Object>> PlainDateTimeConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
auto calendar_value = vm.argument(9);
auto next_integer_argument = [&, index = 0](Optional<double> fallback) mutable -> ThrowCompletionOr<double> {
auto value = vm.argument(index++);
if (value.is_undefined() && fallback.has_value())
return *fallback;
return to_integer_with_truncation(vm, value, ErrorType::TemporalInvalidPlainDateTime);
};
// 2. Set isoYear to ? ToIntegerWithTruncation(isoYear).
auto iso_year = TRY(next_integer_argument({}));
// 3. Set isoMonth to ? ToIntegerWithTruncation(isoMonth).
auto iso_month = TRY(next_integer_argument({}));
// 4. Set isoDay to ? ToIntegerWithTruncation(isoDay).
auto iso_day = TRY(next_integer_argument({}));
// 5. If hour is undefined, set hour to 0; else set hour to ? ToIntegerWithTruncation(hour).
auto hour = TRY(next_integer_argument(0));
// 6. If minute is undefined, set minute to 0; else set minute to ? ToIntegerWithTruncation(minute).
auto minute = TRY(next_integer_argument(0));
// 7. If second is undefined, set second to 0; else set second to ? ToIntegerWithTruncation(second).
auto second = TRY(next_integer_argument(0));
// 8. If millisecond is undefined, set millisecond to 0; else set millisecond to ? ToIntegerWithTruncation(millisecond).
auto millisecond = TRY(next_integer_argument(0));
// 9. If microsecond is undefined, set microsecond to 0; else set microsecond to ? ToIntegerWithTruncation(microsecond).
auto microsecond = TRY(next_integer_argument(0));
// 10. If nanosecond is undefined, set nanosecond to 0; else set nanosecond to ? ToIntegerWithTruncation(nanosecond).
auto nanosecond = TRY(next_integer_argument(0));
// 11. If calendar is undefined, set calendar to "iso8601".
if (calendar_value.is_undefined())
calendar_value = PrimitiveString::create(vm, "iso8601"_string);
// 12. If calendar is not a String, throw a TypeError exception.
if (!calendar_value.is_string())
return vm.throw_completion<TypeError>(ErrorType::NotAString, "calendar"sv);
// 13. Set calendar to ? CanonicalizeCalendar(calendar).
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string_view()));
// 14. If IsValidISODate(isoYear, isoMonth, isoDay) is false, throw a RangeError exception.
if (!is_valid_iso_date(iso_year, iso_month, iso_day))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDateTime);
// 15. Let isoDate be CreateISODateRecord(isoYear, isoMonth, isoDay).
auto iso_date = create_iso_date_record(iso_year, iso_month, iso_day);
// 16. If IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is false, throw a RangeError exception.
if (!is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDateTime);
// 17. Let time be CreateTimeRecord(hour, minute, second, millisecond, microsecond, nanosecond).
auto time = create_time_record(hour, minute, second, millisecond, microsecond, nanosecond);
// 18. Let isoDateTime be CombineISODateAndTimeRecord(isoDate, time).
auto iso_date_time = combine_iso_date_and_time_record(iso_date, time);
// 19. Return ? CreateTemporalDateTime(isoDateTime, calendar, NewTarget).
return TRY(create_temporal_date_time(vm, iso_date_time, move(calendar), new_target));
}
// 5.2.2 Temporal.PlainDateTime.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.from
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimeConstructor::from)
{
// 1. Return ? ToTemporalDateTime(item, options).
return TRY(to_temporal_date_time(vm, vm.argument(0), vm.argument(1)));
}
// 5.2.3 Temporal.PlainDateTime.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.compare
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimeConstructor::compare)
{
// 1. Set one to ? ToTemporalDateTime(one).
auto one = TRY(to_temporal_date_time(vm, vm.argument(0)));
// 2. Set two to ? ToTemporalDateTime(two).
auto two = TRY(to_temporal_date_time(vm, vm.argument(1)));
// 3. Return 𝔽(CompareISODateTime(one.[[ISODateTime]], two.[[ISODateTime]])).
return compare_iso_date_time(one->iso_date_time(), two->iso_date_time());
}
}

View 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 PlainDateTimeConstructor final : public NativeFunction {
JS_OBJECT(PlainDateTimeConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(PlainDateTimeConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~PlainDateTimeConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit PlainDateTimeConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
JS_DECLARE_NATIVE_FUNCTION(compare);
};
}

View file

@ -0,0 +1,210 @@
/*
* 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/PlainDateTimePrototype.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDateTimePrototype);
// 5.3 Properties of the Temporal.PlainDateTime Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plaindatetime-prototype-object
PlainDateTimePrototype::PlainDateTimePrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void PlainDateTimePrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 5.3.2 Temporal.PlainDateTime.prototype[ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.PlainDateTime"_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.hour, hour_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.minute, minute_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.second, second_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.millisecond, millisecond_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.microsecond, microsecond_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.nanosecond, nanosecond_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);
}
// 5.3.3 get Temporal.PlainDateTime.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.calendarid
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::calendar_id_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return dateTime.[[Calendar]].
return PrimitiveString::create(vm, date_time->calendar());
}
// 5.3.4 get Temporal.PlainDateTime.prototype.era, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.era
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::era_getter)
{
// 1. Let plainDateTime be the this value.
// 2. Perform ? RequireInternalSlot(plainDateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return CalendarISOToDate(plainDateTime.[[Calendar]], plainDateTime.[[ISODateTime]].[[ISODate]]).[[Era]].
auto result = calendar_iso_to_date(date_time->calendar(), date_time->iso_date_time().iso_date).era;
if (!result.has_value())
return js_undefined();
return PrimitiveString::create(vm, result.release_value());
}
// 5.3.5 get Temporal.PlainDateTime.prototype.eraYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.erayear
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::era_year_getter)
{
// 1. Let plainDateTime be the this value.
// 2. Perform ? RequireInternalSlot(plainDateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let result be CalendarISOToDate(plainDateTime.[[Calendar]], plainDateTime.[[ISODateTime]].[[ISODate]]).[[EraYear]].
auto result = calendar_iso_to_date(date_time->calendar(), date_time->iso_date_time().iso_date).era_year;
// 4. If result is undefined, return undefined.
if (!result.has_value())
return js_undefined();
// 5. Return 𝔽(result).
return *result;
}
// 5.3.6 get Temporal.PlainDateTime.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.year
// 5.3.7 get Temporal.PlainDateTime.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.month
// 5.3.9 get Temporal.PlainDateTime.prototype.day, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthcode
// 5.3.16 get Temporal.PlainDateTime.prototype.dayOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofweek
// 5.3.17 get Temporal.PlainDateTime.prototype.dayOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofyear
// 5.3.20 get Temporal.PlainDateTime.prototype.daysInWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinweek
// 5.3.21 get Temporal.PlainDateTime.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinmonth
// 5.3.22 get Temporal.PlainDateTime.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinyear
// 5.3.23 get Temporal.PlainDateTime.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthsinyear
// 5.3.24 get Temporal.PlainDateTime.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.inleapyear
#define JS_ENUMERATE_PLAIN_DATE_TIME_SIMPLE_DATE_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(PlainDateTimePrototype::field##_getter) \
{ \
/* 1. Let dateTime be the this value. */ \
/* 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). */ \
auto date_time = TRY(typed_this_object(vm)); \
\
/* 3. Return 𝔽(CalendarISOToDate(dateTime.[[Calendar]], dateTime.[[ISODateTime]].[[ISODate]]).[[<field>]]). */ \
return calendar_iso_to_date(date_time->calendar(), date_time->iso_date_time().iso_date).field; \
}
JS_ENUMERATE_PLAIN_DATE_TIME_SIMPLE_DATE_FIELDS
#undef __JS_ENUMERATE
// 5.3.8 get Temporal.PlainDateTime.prototype.monthCode, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthcode
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::month_code_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return CalendarISOToDate(dateTime.[[Calendar]], dateTime.[[ISODateTime]].[[ISODate]]).[[MonthCode]].
auto month_code = calendar_iso_to_date(date_time->calendar(), date_time->iso_date_time().iso_date).month_code;
return PrimitiveString::create(vm, move(month_code));
}
// 5.3.10 get Temporal.PlainDateTime.prototype.hour, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.hour
// 5.3.11 get Temporal.PlainDateTime.prototype.minute, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.minute
// 5.3.12 get Temporal.PlainDateTime.prototype.second, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.second
// 5.3.13 get Temporal.PlainDateTime.prototype.millisecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.millisecond
// 5.3.14 get Temporal.PlainDateTime.prototype.microsecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.microsecond
// 5.3.15 get Temporal.PlainDateTime.prototype.nanosecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.nanosecond
#define JS_ENUMERATE_PLAIN_DATE_TIME_TIME_FIELDS \
__JS_ENUMERATE(hour) \
__JS_ENUMERATE(minute) \
__JS_ENUMERATE(second) \
__JS_ENUMERATE(millisecond) \
__JS_ENUMERATE(microsecond) \
__JS_ENUMERATE(nanosecond)
#define __JS_ENUMERATE(field) \
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::field##_getter) \
{ \
/* 1. Let dateTime be the this value. */ \
/* 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). */ \
auto date_time = TRY(typed_this_object(vm)); \
\
/* 3. Return 𝔽(dateTime.[[ISODateTime]].[[Time]].[[<field>]]). */ \
return date_time->iso_date_time().time.field; \
}
JS_ENUMERATE_PLAIN_DATE_TIME_TIME_FIELDS
#undef __JS_ENUMERATE
// 5.3.18 get Temporal.PlainDateTime.prototype.weekOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.weekofyear
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::week_of_year_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let result be CalendarISOToDate(dateTime.[[Calendar]], dateTime.[[ISODateTime]].[[ISODate]]).[[WeekOfYear]].[[Week]].
auto result = calendar_iso_to_date(date_time->calendar(), date_time->iso_date_time().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;
}
// 5.3.19 get Temporal.PlainDateTime.prototype.yearOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.yearofweek
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::year_of_week_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let result be CalendarISOToDate(dateTime.[[Calendar]], dateTime.[[ISODateTime]].[[ISODate]]).[[WeekOfYear]].[[Year]].
auto result = calendar_iso_to_date(date_time->calendar(), date_time->iso_date_time().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;
}
}

View file

@ -0,0 +1,50 @@
/*
* 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/PlainDateTime.h>
namespace JS::Temporal {
class PlainDateTimePrototype final : public PrototypeObject<PlainDateTimePrototype, PlainDateTime> {
JS_PROTOTYPE_OBJECT(PlainDateTimePrototype, PlainDateTime, Temporal.PlainDateTime);
GC_DECLARE_ALLOCATOR(PlainDateTimePrototype);
public:
virtual void initialize(Realm&) override;
virtual ~PlainDateTimePrototype() override = default;
private:
explicit PlainDateTimePrototype(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(hour_getter);
JS_DECLARE_NATIVE_FUNCTION(minute_getter);
JS_DECLARE_NATIVE_FUNCTION(second_getter);
JS_DECLARE_NATIVE_FUNCTION(millisecond_getter);
JS_DECLARE_NATIVE_FUNCTION(microsecond_getter);
JS_DECLARE_NATIVE_FUNCTION(nanosecond_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);
};
}

View file

@ -10,6 +10,7 @@
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
#include <math.h>
@ -128,10 +129,19 @@ ThrowCompletionOr<GC::Ref<PlainTime>> to_temporal_time(VM& vm, Value item, Value
return MUST(create_temporal_time(vm, plain_time.time()));
}
// FIXME: b. If item has an [[InitializedTemporalDateTime]] internal slot, then
// FIXME: i. Let resolvedOptions be ? GetOptionsObject(options).
// FIXME: ii. Perform ? GetTemporalOverflowOption(resolvedOptions).
// FIXME: iii. Return ! CreateTemporalTime(item.[[ISODateTime]].[[Time]]).
// b. If item has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(object)) {
auto const& plain_date_time = static_cast<PlainDateTime 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 ! CreateTemporalTime(item.[[ISODateTime]].[[Time]]).
return MUST(create_temporal_time(vm, plain_date_time.iso_date_time().time));
}
// FIXME: c. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
// FIXME: i. Let isoDateTime be GetISODateTimeFor(item.[[TimeZone]], item.[[EpochNanoseconds]]).

View file

@ -8,6 +8,7 @@
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDateConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
@ -35,6 +36,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.PlainDateTime, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_date_time_constructor(); });
define_intrinsic_accessor(vm.names.PlainMonthDay, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_month_day_constructor(); });
define_intrinsic_accessor(vm.names.PlainTime, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_time_constructor(); });
define_intrinsic_accessor(vm.names.PlainYearMonth, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_plain_year_month_constructor(); });

View file

@ -0,0 +1,13 @@
describe("correct behavior", () => {
test("length is 2", () => {
expect(Temporal.PlainDateTime.compare).toHaveLength(2);
});
test("basic functionality", () => {
const plainDateTime1 = new Temporal.PlainDateTime(2021, 8, 27, 16, 38, 40, 1, 2, 3);
expect(Temporal.PlainDateTime.compare(plainDateTime1, plainDateTime1)).toBe(0);
const plainDateTime2 = new Temporal.PlainDateTime(2021, 8, 27, 16, 39, 5, 0, 1, 2);
expect(Temporal.PlainDateTime.compare(plainDateTime1, plainDateTime2)).toBe(-1);
expect(Temporal.PlainDateTime.compare(plainDateTime2, plainDateTime1)).toBe(1);
});
});

View file

@ -0,0 +1,171 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.PlainDateTime.from).toHaveLength(1);
});
test("PlainDate instance argument", () => {
const plainDate = new Temporal.PlainDate(2021, 7, 6);
const plainDateTime = Temporal.PlainDateTime.from(plainDate);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(0);
expect(plainDateTime.minute).toBe(0);
expect(plainDateTime.second).toBe(0);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("PlainDateTime instance argument", () => {
const plainDateTime_ = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
const plainDateTime = Temporal.PlainDateTime.from(plainDateTime_);
expect(plainDateTime).not.toBe(plainDateTime_);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(18);
expect(plainDateTime.minute).toBe(14);
expect(plainDateTime.second).toBe(47);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("fields object argument", () => {
const object = {
year: 2021,
month: 7,
day: 6,
hour: 23,
minute: 42,
second: 1,
millisecond: 0,
microsecond: 0,
nanosecond: 0,
};
const plainDateTime = Temporal.PlainDateTime.from(object);
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(42);
expect(plainDateTime.second).toBe(1);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
test("with 'constrain' overflow option", () => {
const object = {
year: 0,
month: 1,
day: 1,
hour: 24,
minute: 60,
second: 60,
millisecond: 1000,
microsecond: 1000,
nanosecond: 1000,
};
const plainDateTime = Temporal.PlainDateTime.from(object, { overflow: "constrain" });
expect(plainDateTime.year).toBe(0);
expect(plainDateTime.month).toBe(1);
expect(plainDateTime.day).toBe(1);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(59);
expect(plainDateTime.second).toBe(59);
expect(plainDateTime.millisecond).toBe(999);
expect(plainDateTime.microsecond).toBe(999);
expect(plainDateTime.nanosecond).toBe(999);
});
test("PlainDateTime string argument", () => {
const plainDateTime = Temporal.PlainDateTime.from("2021-07-06T23:42:01");
expect(plainDateTime.year).toBe(2021);
expect(plainDateTime.month).toBe(7);
expect(plainDateTime.day).toBe(6);
expect(plainDateTime.hour).toBe(23);
expect(plainDateTime.minute).toBe(42);
expect(plainDateTime.second).toBe(1);
expect(plainDateTime.millisecond).toBe(0);
expect(plainDateTime.microsecond).toBe(0);
expect(plainDateTime.nanosecond).toBe(0);
});
});
describe("errors", () => {
test("missing fields", () => {
expect(() => {
Temporal.PlainDateTime.from({ year: 0, month: 1 });
}).toThrowWithMessage(TypeError, "Required property day is missing or undefined");
expect(() => {
Temporal.PlainDateTime.from({ year: 0, day: 1 });
}).toThrowWithMessage(TypeError, "Required property month is missing or undefined");
expect(() => {
Temporal.PlainDateTime.from({ month: 1, day: 1 });
}).toThrowWithMessage(TypeError, "Required property year is missing or undefined");
});
test("with 'reject' overflow option", () => {
const values = [
[{ year: 1234567, month: 1, day: 1 }, "Invalid ISO date"],
[{ year: 0, month: 13, day: 1 }, "Invalid ISO date"],
[{ year: 0, month: 1, day: 32 }, "Invalid ISO date"],
[{ year: 0, month: 1, day: 1, hour: 24 }, "Invalid plain time"],
[{ year: 0, month: 1, day: 1, hour: 0, minute: 60 }, "Invalid plain time"],
[{ year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 60 }, "Invalid plain time"],
[
{ year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0, millisecond: 1000 },
"Invalid plain time",
],
[
{
year: 0,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 1000,
},
"Invalid plain time",
],
[
{
year: 0,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
millisecond: 0,
microsecond: 0,
nanosecond: 1000,
},
"Invalid plain time",
],
];
for (const [object, error] of values) {
expect(() => {
Temporal.PlainDateTime.from(object, { overflow: "reject" });
}).toThrowWithMessage(RangeError, error);
}
});
test("string must not contain a UTC designator", () => {
expect(() => {
Temporal.PlainDateTime.from("2021-07-06T23:42:01Z");
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
});
test("extended year must not be negative zero", () => {
expect(() => {
Temporal.PlainDateTime.from("-000000-01-01");
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
expect(() => {
Temporal.PlainDateTime.from("000000-01-01"); // U+2212
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
});
});

View file

@ -0,0 +1,53 @@
describe("errors", () => {
test("called without new", () => {
expect(() => {
Temporal.PlainDateTime();
}).toThrowWithMessage(
TypeError,
"Temporal.PlainDateTime constructor must be called with 'new'"
);
});
test("cannot pass Infinity", () => {
for (let i = 0; i < 9; ++i) {
const args = Array(9).fill(0);
args[i] = Infinity;
expect(() => {
new Temporal.PlainDateTime(...args);
}).toThrowWithMessage(RangeError, "Invalid plain date time");
args[i] = -Infinity;
expect(() => {
new Temporal.PlainDateTime(...args);
}).toThrowWithMessage(RangeError, "Invalid plain date time");
}
});
test("cannot pass invalid ISO date or time", () => {
// NOTE: The year max value is 3 more than in the polyfill, but they incorrectly seem to
// use ISOYearMonthWithinLimits, which AFAICT isn't used for PlainDate{,Time} in the spec.
// ¯\_(ツ)_/¯
const badValues = [275764, 0, 0, 24, 60, 60, 1000, 1000, 1000];
for (let i = 0; i < 9; ++i) {
const args = [0, 1, 1, 0, 0, 0, 0, 0, 0];
args[i] = badValues[i];
expect(() => {
new Temporal.PlainDateTime(...args);
}).toThrowWithMessage(RangeError, "Invalid plain date time");
}
});
});
describe("normal behavior", () => {
test("length is 3", () => {
expect(Temporal.PlainDateTime).toHaveLength(3);
});
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 22, 19, 46, 32, 123, 456, 789);
expect(typeof plainDateTime).toBe("object");
expect(plainDateTime).toBeInstanceOf(Temporal.PlainDateTime);
expect(Object.getPrototypeOf(plainDateTime)).toBe(Temporal.PlainDateTime.prototype);
});
});

View file

@ -0,0 +1,15 @@
describe("correct behavior", () => {
test("calendarId basic functionality", () => {
const calendar = "gregory";
const plainDateTime = new Temporal.PlainDateTime(2000, 5, 1, 12, 30, 0, 0, 0, 0, calendar);
expect(plainDateTime.calendarId).toBe(calendar);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "calendarId", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 29);
expect(plainDateTime.day).toBe(29);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "day", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.dayOfWeek).toBe(5);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "dayOfWeek", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.dayOfYear).toBe(211);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "dayOfYear", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.daysInMonth).toBe(31);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "daysInMonth", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.daysInWeek).toBe(7);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "daysInWeek", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.daysInYear).toBe(365);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "daysInYear", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
expect(plainDateTime.era).toBeUndefined();
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "era", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 6, 18, 14, 47);
expect(plainDateTime.eraYear).toBeUndefined();
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "eraYear", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30, 1);
expect(plainDateTime.hour).toBe(1);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "hour", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.inLeapYear).toBeFalse();
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "inLeapYear", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30, 1, 4, 32, 111, 420);
expect(plainDateTime.microsecond).toBe(420);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "microsecond", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30, 1, 4, 32, 111);
expect(plainDateTime.millisecond).toBe(111);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "millisecond", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30, 1, 4);
expect(plainDateTime.minute).toBe(4);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "minute", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 29);
expect(plainDateTime.month).toBe(7);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "month", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 29);
expect(plainDateTime.monthCode).toBe("M07");
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "monthCode", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.monthsInYear).toBe(12);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "monthsInYear", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30, 1, 4, 32, 111, 420, 963);
expect(plainDateTime.nanosecond).toBe(963);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "nanosecond", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30, 1, 4, 32);
expect(plainDateTime.second).toBe(32);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "second", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 30);
expect(plainDateTime.weekOfYear).toBe(30);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "weekOfYear", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2021, 7, 29);
expect(plainDateTime.year).toBe(2021);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "year", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -0,0 +1,14 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const plainDateTime = new Temporal.PlainDateTime(2023, 1, 1);
expect(plainDateTime.yearOfWeek).toBe(2022);
});
});
describe("errors", () => {
test("this value must be a Temporal.PlainDateTime object", () => {
expect(() => {
Reflect.get(Temporal.PlainDateTime.prototype, "yearOfWeek", "foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.PlainDateTime");
});
});

View file

@ -9,7 +9,7 @@ const PLAIN_TIME_PROPERTIES = [
const REJECTED_CALENDAR_TYPES_THREE_ARGUMENTS = [
Temporal.PlainDate,
// Temporal.PlainDateTime,
Temporal.PlainDateTime,
Temporal.PlainTime,
];