Compare commits

...

24 commits

Author SHA1 Message Date
Chase Knowlden
b4e7e27dae
Merge b17e627f06 into d6bcd3fb0b 2024-11-20 20:01:46 -05:00
Shannon Booth
d6bcd3fb0b LibWeb: Make CallbackType take a realm instead of settings object
Some checks are pending
CI / Lagom (false, FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, macos-15, macOS, Clang) (push) Waiting to run
CI / Lagom (false, NO_FUZZ, ubuntu-24.04, Linux, GNU) (push) Waiting to run
CI / Lagom (true, NO_FUZZ, ubuntu-24.04, Linux, Clang) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (macos-14, macOS, macOS-universal2) (push) Waiting to run
Package the js repl as a binary artifact / build-and-package (ubuntu-24.04, Linux, Linux-x86_64) (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Push notes / build (push) Waiting to run
In line with the ShadowRealm proposal changes in the WebIDL spec:
webidl#1437 and supporting changes in HTML spec.

This is required for ShadowRealms as they have no relevant settings
object on the shadow realm, so fixes a crash in the QueueingStrategy
test in this commit.
2024-11-20 18:01:21 -07:00
Shannon Booth
d527c5df5d LibWeb: Allow using queuing strategies on globals other than Window
These interfaces are exposed on *, meaning it should work for workers
and our newly added shadow realm global object by being stored on the
universal global scope mixin.
2024-11-20 18:01:21 -07:00
rmg-x
13f349aea2 LibWeb/Fetch: Implement blob range section of scheme fetch specification 2024-11-21 00:26:58 +00:00
rmg-x
84f673515b LibWeb/XHR: Use normalized header value when validating contents
This was not matching specification and caused valid requests to be
rejected.
2024-11-21 00:26:58 +00:00
rmg-x
4e48298414 LibWeb/Fetch: Implement build_content_range(start, end, full_length) 2024-11-21 00:26:58 +00:00
rmg-x
bf5cf720b5 LibWeb/Fetch: Bring parse_single_range_header_value() up to spec
The previous implementation wasn't using the latest specification steps.
2024-11-21 00:26:58 +00:00
Timothy Flynn
488034477a Revert "LibWeb: Set doctype node immediately while parsing XML document"
This reverts commit cd446e5e9c.

This broke about 20k WPT subtests, all related to XML parsing. See:
https://wpt.fyi/results/html/the-xhtml-syntax/parsing-xhtml-documents?diff=&filter=ADC&run_id=5154815472828416&run_id=5090731742199808
2024-11-20 19:11:56 -05:00
Timothy Flynn
f57ff63432 LibJS: Implement Temporal.Duration.prototype.valueOf 2024-11-20 19:04:30 -05:00
Timothy Flynn
c715711f88 LibJS: Implement Temporal.Duration.prototype.total
Until we have re-implemented Temporal.PlainDate/ZonedDateTime, some of
Temporal.Duration.prototype.total (and its invoked AOs) are left
unimplemented.
2024-11-20 19:04:30 -05:00
Timothy Flynn
5689621c2b LibJS: Implement Temporal.Duration.prototype.round
Until we have re-implemented Temporal.PlainDate/ZonedDateTime, some of
Temporal.Duration.prototype.round (and its invoked AOs) are left
unimplemented.
2024-11-20 19:04:30 -05:00
Timothy Flynn
4742775262 LibJS: Implement stringification Temporal.Duration prototypes 2024-11-20 19:04:30 -05:00
Timothy Flynn
a80523be18 LibJS: Implement mathematical Temporal.Duration prototypes
Includes:
Temporal.Duration.prototype.negated
Temporal.Duration.prototype.abs
Temporal.Duration.prototype.add
Temporal.Duration.prototype.subtract
2024-11-20 19:04:30 -05:00
Timothy Flynn
55c81482b0 LibJS: Implement Temporal.Duration.prototype.with 2024-11-20 19:04:30 -05:00
Timothy Flynn
dfaa3bf649 LibJS: Implement Temporal.Duration.prototype.sign/blank 2024-11-20 19:04:30 -05:00
Timothy Flynn
5fe0d3352d LibJS: Implement the Temporal.Duration constructor
This also includes a stubbed Temporal.Duration.prototype.

Until we have re-implemented Temporal.PlainDate/ZonedDateTime, some of
Temporal.Duration.compare (and its invoked AOs) are left unimplemented.
2024-11-20 19:04:30 -05:00
Timothy Flynn
eca378a7a3 LibJS: Restore some Temporal numeric constants
And add few ad-hoc constants for convenience.
2024-11-20 19:04:30 -05:00
Timothy Flynn
e4e05837e1 LibJS: Return a GC::Ref from Temporal::get_options_object
The Object returned here is always non-null.
2024-11-20 19:04:30 -05:00
Timothy Flynn
c8d2404230 LibJS: Update spec steps for the few remaining Temporal AOs 2024-11-20 19:04:30 -05:00
Timothy Flynn
d368fcadac LibJS: Update spec link for Temporal [ %Symbol.toStringTag% ] 2024-11-20 19:04:30 -05:00
Timothy Flynn
f7517c5b8d LibJS: Remove our existing Temporal implementation
Our Temporal implementation is woefully out of date. The spec has been
so vastly rewritten that it is unfortunately not practical to update our
implementation in-place. Even just removing Temporal objects that were
removed from the spec, or updating any of the simpler remaining objects,
has proven to be a mess in previous attempts.

So, this removes our Temporal implementation. AOs used by other specs
are left intact.
2024-11-20 19:04:30 -05:00
Timothy Flynn
b94307583b LibCrypto: Add user-defined literals to convert numbers to a BigInt
It is much more convenient to define constants with:

    1000_bigint

Than with:

    Crypto::UnsignedBigInteger { 1000 }
2024-11-20 19:04:30 -05:00
Timothy Flynn
e236f1d2ae LibCrypto: Define UnsignedBigInteger::operator<=
We have all comparison operators except less-than-or-equal already.
2024-11-20 19:04:30 -05:00
Chase Knowlden
b17e627f06 LibWeb: Implement more of the download attribute 2024-11-18 16:55:08 -05:00
413 changed files with 2934 additions and 30932 deletions

View file

@ -601,6 +601,11 @@ bool UnsignedBigInteger::operator<(UnsignedBigInteger const& other) const
return false; return false;
} }
bool UnsignedBigInteger::operator<=(UnsignedBigInteger const& other) const
{
return *this < other || *this == other;
}
bool UnsignedBigInteger::operator>(UnsignedBigInteger const& other) const bool UnsignedBigInteger::operator>(UnsignedBigInteger const& other) const
{ {
return *this != other && !(*this < other); return *this != other && !(*this < other);

View file

@ -128,6 +128,7 @@ public:
[[nodiscard]] bool operator==(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator==(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator!=(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator<(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator<(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator<=(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator>(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator>(UnsignedBigInteger const& other) const;
[[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const; [[nodiscard]] bool operator>=(UnsignedBigInteger const& other) const;
@ -171,8 +172,26 @@ struct AK::Formatter<Crypto::UnsignedBigInteger> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder&, Crypto::UnsignedBigInteger const&); ErrorOr<void> format(FormatBuilder&, Crypto::UnsignedBigInteger const&);
}; };
inline Crypto::UnsignedBigInteger inline Crypto::UnsignedBigInteger operator""_bigint(char const* string, size_t length)
operator""_bigint(char const* string, size_t length)
{ {
return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length })); return MUST(Crypto::UnsignedBigInteger::from_base(10, { string, length }));
} }
inline Crypto::UnsignedBigInteger operator""_bigint(unsigned long long value)
{
auto result = Crypto::UnsignedBigInteger { static_cast<u64>(value) };
VERIFY(!result.is_invalid());
return result;
}
inline Crypto::UnsignedBigInteger operator""_bigint(long double value)
{
VERIFY(value >= 0);
VERIFY(value < static_cast<long double>(NumericLimits<double>::max()));
auto result = Crypto::UnsignedBigInteger { static_cast<double>(value) };
VERIFY(!result.is_invalid());
return result;
}

View file

@ -205,40 +205,12 @@ set(SOURCES
Runtime/SymbolObject.cpp Runtime/SymbolObject.cpp
Runtime/SymbolPrototype.cpp Runtime/SymbolPrototype.cpp
Runtime/Temporal/AbstractOperations.cpp Runtime/Temporal/AbstractOperations.cpp
Runtime/Temporal/Calendar.cpp
Runtime/Temporal/CalendarConstructor.cpp
Runtime/Temporal/CalendarPrototype.cpp
Runtime/Temporal/Duration.cpp Runtime/Temporal/Duration.cpp
Runtime/Temporal/DurationConstructor.cpp Runtime/Temporal/DurationConstructor.cpp
Runtime/Temporal/DurationPrototype.cpp Runtime/Temporal/DurationPrototype.cpp
Runtime/Temporal/Instant.cpp Runtime/Temporal/Instant.cpp
Runtime/Temporal/InstantConstructor.cpp
Runtime/Temporal/InstantPrototype.cpp
Runtime/Temporal/ISO8601.cpp Runtime/Temporal/ISO8601.cpp
Runtime/Temporal/Now.cpp
Runtime/Temporal/PlainDate.cpp
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
Runtime/Temporal/PlainTime.cpp
Runtime/Temporal/PlainTimeConstructor.cpp
Runtime/Temporal/PlainTimePrototype.cpp
Runtime/Temporal/PlainYearMonth.cpp
Runtime/Temporal/PlainYearMonthConstructor.cpp
Runtime/Temporal/PlainYearMonthPrototype.cpp
Runtime/Temporal/Temporal.cpp Runtime/Temporal/Temporal.cpp
Runtime/Temporal/TimeZone.cpp
Runtime/Temporal/TimeZoneConstructor.cpp
Runtime/Temporal/TimeZoneMethods.cpp
Runtime/Temporal/TimeZonePrototype.cpp
Runtime/Temporal/ZonedDateTime.cpp
Runtime/Temporal/ZonedDateTimeConstructor.cpp
Runtime/Temporal/ZonedDateTimePrototype.cpp
Runtime/TypedArray.cpp Runtime/TypedArray.cpp
Runtime/TypedArrayConstructor.cpp Runtime/TypedArrayConstructor.cpp
Runtime/TypedArrayPrototype.cpp Runtime/TypedArrayPrototype.cpp

View file

@ -87,17 +87,8 @@
__JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \ __JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \
__JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor) __JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor)
#define JS_ENUMERATE_TEMPORAL_OBJECTS \ #define JS_ENUMERATE_TEMPORAL_OBJECTS \
__JS_ENUMERATE(Calendar, calendar, CalendarPrototype, CalendarConstructor) \ __JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor)
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
__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) \
__JS_ENUMERATE(TimeZone, time_zone, TimeZonePrototype, TimeZoneConstructor) \
__JS_ENUMERATE(ZonedDateTime, zoned_date_time, ZonedDateTimePrototype, ZonedDateTimeConstructor)
#define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \ #define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \
__JS_ENUMERATE(AtomicsObject, atomics) \ __JS_ENUMERATE(AtomicsObject, atomics) \
@ -284,13 +275,8 @@ namespace Temporal {
class PrototypeName; class PrototypeName;
JS_ENUMERATE_TEMPORAL_OBJECTS JS_ENUMERATE_TEMPORAL_OBJECTS
#undef __JS_ENUMERATE #undef __JS_ENUMERATE
class Temporal; class Temporal;
struct CalendarMethods;
struct DurationRecord;
struct DateDurationRecord;
struct TimeDurationRecord;
struct TimeZoneMethods;
struct PartialDurationRecord;
}; };
template<typename T> template<typename T>

View file

@ -47,16 +47,7 @@
#include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/StringObject.h> #include <LibJS/Runtime/StringObject.h>
#include <LibJS/Runtime/StringPrototype.h> #include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.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/PlainMonthDay.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/WeakMap.h> #include <LibJS/Runtime/WeakMap.h>
@ -516,101 +507,6 @@ ErrorOr<void> print_data_view(JS::PrintContext& print_context, JS::DataView cons
return {}; return {};
} }
ErrorOr<void> print_temporal_calendar(JS::PrintContext& print_context, JS::Temporal::Calendar const& calendar, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.Calendar"sv));
TRY(js_out(print_context, " "));
TRY(print_value(print_context, JS::PrimitiveString::create(calendar.vm(), calendar.identifier()), seen_objects));
return {};
}
ErrorOr<void> print_temporal_duration(JS::PrintContext& print_context, JS::Temporal::Duration const& duration, HashTable<JS::Object*>&)
{
TRY(print_type(print_context, "Temporal.Duration"sv));
TRY(js_out(print_context, " \033[34;1m{} y, {} M, {} w, {} d, {} h, {} m, {} s, {} ms, {} us, {} ns\033[0m", duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds()));
return {};
}
ErrorOr<void> print_temporal_instant(JS::PrintContext& print_context, JS::Temporal::Instant const& instant, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.Instant"sv));
TRY(js_out(print_context, " "));
// FIXME: Print human readable date and time, like in print_date(print_context, ) - ideally handling arbitrarily large values since we get a bigint.
TRY(print_value(print_context, &instant.nanoseconds(), seen_objects));
return {};
}
ErrorOr<void> print_temporal_plain_date(JS::PrintContext& print_context, JS::Temporal::PlainDate const& plain_date, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.PlainDate"sv));
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}-{:02}\033[0m", plain_date.iso_year(), plain_date.iso_month(), plain_date.iso_day()));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, &plain_date.calendar(), seen_objects));
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_year(), plain_date_time.iso_month(), plain_date_time.iso_day(), plain_date_time.iso_hour(), plain_date_time.iso_minute(), plain_date_time.iso_second(), plain_date_time.iso_millisecond(), plain_date_time.iso_microsecond(), plain_date_time.iso_nanosecond()));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, &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));
// Also has an [[ISOYear]] internal slot, but showing that here seems rather unexpected.
TRY(js_out(print_context, " \033[34;1m{:02}-{:02}\033[0m", plain_month_day.iso_month(), plain_month_day.iso_day()));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, &plain_month_day.calendar(), seen_objects));
return {};
}
ErrorOr<void> print_temporal_plain_time(JS::PrintContext& print_context, JS::Temporal::PlainTime const& plain_time, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.PlainTime"sv));
TRY(js_out(print_context, " \033[34;1m{:02}:{:02}:{:02}.{:03}{:03}{:03}\033[0m", plain_time.iso_hour(), plain_time.iso_minute(), plain_time.iso_second(), plain_time.iso_millisecond(), plain_time.iso_microsecond(), plain_time.iso_nanosecond()));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, &plain_time.calendar(), seen_objects));
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));
// Also has an [[ISODay]] internal slot, but showing that here seems rather unexpected.
TRY(js_out(print_context, " \033[34;1m{:04}-{:02}\033[0m", plain_year_month.iso_year(), plain_year_month.iso_month()));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, &plain_year_month.calendar(), seen_objects));
return {};
}
ErrorOr<void> print_temporal_time_zone(JS::PrintContext& print_context, JS::Temporal::TimeZone const& time_zone, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.TimeZone"sv));
TRY(js_out(print_context, " "));
TRY(print_value(print_context, JS::PrimitiveString::create(time_zone.vm(), time_zone.identifier()), seen_objects));
if (time_zone.offset_nanoseconds().has_value()) {
TRY(js_out(print_context, "\n offset (ns): "));
TRY(print_value(print_context, JS::Value(*time_zone.offset_nanoseconds()), seen_objects));
}
return {};
}
ErrorOr<void> print_temporal_zoned_date_time(JS::PrintContext& print_context, JS::Temporal::ZonedDateTime const& zoned_date_time, HashTable<JS::Object*>& seen_objects)
{
TRY(print_type(print_context, "Temporal.ZonedDateTime"sv));
TRY(js_out(print_context, "\n epochNanoseconds: "));
TRY(print_value(print_context, &zoned_date_time.nanoseconds(), seen_objects));
TRY(js_out(print_context, "\n timeZone: "));
TRY(print_value(print_context, &zoned_date_time.time_zone(), seen_objects));
TRY(js_out(print_context, "\n calendar: "));
TRY(print_value(print_context, &zoned_date_time.calendar(), seen_objects));
return {};
}
ErrorOr<void> print_intl_display_names(JS::PrintContext& print_context, JS::Intl::DisplayNames const& display_names, HashTable<JS::Object*>& seen_objects) ErrorOr<void> print_intl_display_names(JS::PrintContext& print_context, JS::Intl::DisplayNames const& display_names, HashTable<JS::Object*>& seen_objects)
{ {
TRY(print_type(print_context, "Intl.DisplayNames"sv)); TRY(print_type(print_context, "Intl.DisplayNames"sv));
@ -932,6 +828,13 @@ ErrorOr<void> print_intl_duration_format(JS::PrintContext& print_context, JS::In
return {}; return {};
} }
ErrorOr<void> print_temporal_duration(JS::PrintContext& print_context, JS::Temporal::Duration const& duration, HashTable<JS::Object*>&)
{
TRY(print_type(print_context, "Temporal.Duration"sv));
TRY(js_out(print_context, " \033[34;1m{} y, {} M, {} w, {} d, {} h, {} m, {} s, {} ms, {} us, {} ns\033[0m", duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds()));
return {};
}
ErrorOr<void> print_boolean_object(JS::PrintContext& print_context, JS::BooleanObject const& boolean_object, HashTable<JS::Object*>& seen_objects) 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)); TRY(print_type(print_context, "Boolean"sv));
@ -1025,26 +928,6 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, Hash
return print_number_object(print_context, static_cast<JS::NumberObject&>(object), seen_objects); return print_number_object(print_context, static_cast<JS::NumberObject&>(object), seen_objects);
if (is<JS::StringObject>(object)) if (is<JS::StringObject>(object))
return print_string_object(print_context, static_cast<JS::StringObject&>(object), seen_objects); return print_string_object(print_context, static_cast<JS::StringObject&>(object), seen_objects);
if (is<JS::Temporal::Calendar>(object))
return print_temporal_calendar(print_context, static_cast<JS::Temporal::Calendar&>(object), seen_objects);
if (is<JS::Temporal::Duration>(object))
return print_temporal_duration(print_context, static_cast<JS::Temporal::Duration&>(object), seen_objects);
if (is<JS::Temporal::Instant>(object))
return print_temporal_instant(print_context, static_cast<JS::Temporal::Instant&>(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))
return print_temporal_plain_time(print_context, static_cast<JS::Temporal::PlainTime&>(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);
if (is<JS::Temporal::TimeZone>(object))
return print_temporal_time_zone(print_context, static_cast<JS::Temporal::TimeZone&>(object), seen_objects);
if (is<JS::Temporal::ZonedDateTime>(object))
return print_temporal_zoned_date_time(print_context, static_cast<JS::Temporal::ZonedDateTime&>(object), seen_objects);
if (is<JS::Intl::DisplayNames>(object)) if (is<JS::Intl::DisplayNames>(object))
return print_intl_display_names(print_context, static_cast<JS::Intl::DisplayNames&>(object), seen_objects); return print_intl_display_names(print_context, static_cast<JS::Intl::DisplayNames&>(object), seen_objects);
if (is<JS::Intl::Locale>(object)) if (is<JS::Intl::Locale>(object))
@ -1067,6 +950,8 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, Hash
return print_intl_segments(print_context, static_cast<JS::Intl::Segments&>(object), seen_objects); return print_intl_segments(print_context, static_cast<JS::Intl::Segments&>(object), seen_objects);
if (is<JS::Intl::DurationFormat>(object)) if (is<JS::Intl::DurationFormat>(object))
return print_intl_duration_format(print_context, static_cast<JS::Intl::DurationFormat&>(object), seen_objects); return print_intl_duration_format(print_context, static_cast<JS::Intl::DurationFormat&>(object), seen_objects);
if (is<JS::Temporal::Duration>(object))
return print_temporal_duration(print_context, static_cast<JS::Temporal::Duration&>(object), seen_objects);
return print_object(print_context, object, seen_objects); return print_object(print_context, object, seen_objects);
} }

View file

@ -21,7 +21,6 @@
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Intl/DateTimeFormat.h> #include <LibJS/Runtime/Intl/DateTimeFormat.h>
#include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h> #include <LibJS/Runtime/Intl/DateTimeFormatConstructor.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
#include <LibUnicode/DisplayNames.h> #include <LibUnicode/DisplayNames.h>
@ -82,7 +81,6 @@ void DatePrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr); define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toLocaleTimeString, to_locale_time_string, 0, attr); define_native_function(realm, vm.names.toLocaleTimeString, to_locale_time_string, 0, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr); define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toTemporalInstant, to_temporal_instant, 0, attr);
define_native_function(realm, vm.names.toTimeString, to_time_string, 0, attr); define_native_function(realm, vm.names.toTimeString, to_time_string, 0, attr);
define_native_function(realm, vm.names.toUTCString, to_utc_string, 0, attr); define_native_function(realm, vm.names.toUTCString, to_utc_string, 0, attr);
@ -1179,20 +1177,6 @@ ByteString to_date_string(double time)
return ByteString::formatted("{} {}{}", date_string(time), time_string(time), time_zone_string(time)); return ByteString::formatted("{} {}{}", date_string(time), time_string(time), time_zone_string(time));
} }
// 14.1.1 Date.prototype.toTemporalInstant ( ), https://tc39.es/proposal-temporal/#sec-date.prototype.totemporalinstant
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_temporal_instant)
{
// 1. Let t be ? thisTimeValue(this value).
auto t = TRY(this_time_value(vm, vm.this_value()));
// 2. Let ns be ? NumberToBigInt(t) × (10^6).
auto* ns = TRY(number_to_bigint(vm, Value(t)));
ns = BigInt::create(vm, ns->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }));
// 3. Return ! CreateTemporalInstant(ns).
return MUST(Temporal::create_temporal_instant(vm, *ns));
}
// 21.4.4.42 Date.prototype.toTimeString ( ), https://tc39.es/ecma262/#sec-date.prototype.totimestring // 21.4.4.42 Date.prototype.toTimeString ( ), https://tc39.es/ecma262/#sec-date.prototype.totimestring
JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string) JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string)
{ {

View file

@ -62,7 +62,6 @@ private:
JS_DECLARE_NATIVE_FUNCTION(to_locale_string); JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_time_string); JS_DECLARE_NATIVE_FUNCTION(to_locale_time_string);
JS_DECLARE_NATIVE_FUNCTION(to_string); JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_temporal_instant);
JS_DECLARE_NATIVE_FUNCTION(to_time_string); JS_DECLARE_NATIVE_FUNCTION(to_time_string);
JS_DECLARE_NATIVE_FUNCTION(to_utc_string); JS_DECLARE_NATIVE_FUNCTION(to_utc_string);

View file

@ -261,6 +261,7 @@
M(TemporalInvalidInstantString, "Invalid instant string '{}'") \ M(TemporalInvalidInstantString, "Invalid instant string '{}'") \
M(TemporalInvalidISODate, "Invalid ISO date") \ M(TemporalInvalidISODate, "Invalid ISO date") \
M(TemporalInvalidISODateTime, "Invalid ISO date time") \ M(TemporalInvalidISODateTime, "Invalid ISO date time") \
M(TemporalInvalidLargestUnit, "Largest unit must not be {}") \
M(TemporalInvalidMonthCode, "Invalid month code") \ M(TemporalInvalidMonthCode, "Invalid month code") \
M(TemporalInvalidMonthDayString, "Invalid month day string '{}'") \ M(TemporalInvalidMonthDayString, "Invalid month day string '{}'") \
M(TemporalInvalidMonthDayStringUTCDesignator, "Invalid month day string '{}': must not contain a UTC designator") \ M(TemporalInvalidMonthDayStringUTCDesignator, "Invalid month day string '{}': must not contain a UTC designator") \
@ -286,7 +287,7 @@
M(TemporalOnlyISO8601WithMonthDayString, "MM-DD string format can only be used with the iso8601 calendar") \ M(TemporalOnlyISO8601WithMonthDayString, "MM-DD string format can only be used with the iso8601 calendar") \
M(TemporalOnlyISO8601WithYearMonthString, "YYYY-MM string format can only be used with the iso8601 calendar") \ M(TemporalOnlyISO8601WithYearMonthString, "YYYY-MM string format can only be used with the iso8601 calendar") \
M(TemporalMissingOptionsObject, "Required options object is missing or undefined") \ M(TemporalMissingOptionsObject, "Required options object is missing or undefined") \
M(TemporalMissingStartingPoint, "A starting point is required for balancing {}") \ M(TemporalMissingStartingPoint, "A starting point is required for comparing {}") \
M(TemporalMissingUnits, "One or both of smallestUnit or largestUnit is required") \ M(TemporalMissingUnits, "One or both of smallestUnit or largestUnit is required") \
M(TemporalNanosecondsConvertedToDaysWithOppositeSign, "Time zone or calendar converted nanoseconds into a number of days with " \ M(TemporalNanosecondsConvertedToDaysWithOppositeSign, "Time zone or calendar converted nanoseconds into a number of days with " \
"the opposite sign") \ "the opposite sign") \

View file

@ -67,17 +67,7 @@
#include <LibJS/Runtime/StringPrototype.h> #include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SuppressedErrorConstructor.h> #include <LibJS/Runtime/SuppressedErrorConstructor.h>
#include <LibJS/Runtime/SymbolConstructor.h> #include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
#include <LibJS/Runtime/Temporal/DurationConstructor.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.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>
#include <LibJS/Runtime/Temporal/Temporal.h> #include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/Value.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>

View file

@ -64,7 +64,7 @@ ThrowCompletionOr<GC::Ref<Object>> DisplayNamesConstructor::construct(FunctionOb
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "options"sv); return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "options"sv);
// 5. Set options to ? GetOptionsObject(options). // 5. Set options to ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 6. Let opt be a new Record. // 6. Let opt be a new Record.
LocaleOptions opt {}; LocaleOptions opt {};

View file

@ -59,7 +59,7 @@ ThrowCompletionOr<GC::Ref<Object>> DurationFormatConstructor::construct(Function
auto requested_locales = TRY(canonicalize_locale_list(vm, locales)); auto requested_locales = TRY(canonicalize_locale_list(vm, locales));
// 4. Let options be ? GetOptionsObject(options). // 4. Let options be ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 5. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit"). // 5. Let matcher be ? GetOption(options, "localeMatcher", string, « "lookup", "best fit" », "best fit").
auto matcher = TRY(get_option(vm, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv)); auto matcher = TRY(get_option(vm, *options, vm.names.localeMatcher, OptionType::String, { "lookup"sv, "best fit"sv }, "best fit"sv));

View file

@ -59,7 +59,7 @@ ThrowCompletionOr<GC::Ref<Object>> ListFormatConstructor::construct(FunctionObje
auto requested_locales = TRY(canonicalize_locale_list(vm, locale_value)); auto requested_locales = TRY(canonicalize_locale_list(vm, locale_value));
// 4. Set options to ? GetOptionsObject(options). // 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 5. Let opt be a new Record. // 5. Let opt be a new Record.
LocaleOptions opt {}; LocaleOptions opt {};

View file

@ -60,7 +60,7 @@ ThrowCompletionOr<GC::Ref<Object>> SegmenterConstructor::construct(FunctionObjec
auto requested_locales = TRY(canonicalize_locale_list(vm, locales)); auto requested_locales = TRY(canonicalize_locale_list(vm, locales));
// 5. Set options to ? GetOptionsObject(options). // 5. Set options to ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 6. Let opt be a new Record. // 6. Let opt be a new Record.
LocaleOptions opt {}; LocaleOptions opt {};

View file

@ -99,27 +99,9 @@
#include <LibJS/Runtime/SuppressedErrorPrototype.h> #include <LibJS/Runtime/SuppressedErrorPrototype.h>
#include <LibJS/Runtime/SymbolConstructor.h> #include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h> #include <LibJS/Runtime/SymbolPrototype.h>
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
#include <LibJS/Runtime/Temporal/CalendarPrototype.h>
#include <LibJS/Runtime/Temporal/DurationConstructor.h> #include <LibJS/Runtime/Temporal/DurationConstructor.h>
#include <LibJS/Runtime/Temporal/DurationPrototype.h> #include <LibJS/Runtime/Temporal/DurationPrototype.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.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>
#include <LibJS/Runtime/Temporal/PlainTimePrototype.h>
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
#include <LibJS/Runtime/Temporal/Temporal.h> #include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
#include <LibJS/Runtime/TypedArray.h> #include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/TypedArrayConstructor.h> #include <LibJS/Runtime/TypedArrayConstructor.h>
#include <LibJS/Runtime/TypedArrayPrototype.h> #include <LibJS/Runtime/TypedArrayPrototype.h>

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,22 @@
/* /*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/Forward.h>
#include <AK/Variant.h> #include <AK/Variant.h>
#include <LibCrypto/BigInt/SignedBigInteger.h> #include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibGC/Ptr.h>
#include <LibJS/Forward.h> #include <LibJS/Forward.h>
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/VM.h>
#include <LibJS/Runtime/Temporal/ISO8601.h>
#include <LibJS/Runtime/Temporal/TimeZoneMethods.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
#include <math.h>
namespace JS::Temporal { namespace JS::Temporal {
@ -24,11 +25,48 @@ enum class ArithmeticOperation {
Subtract, Subtract,
}; };
enum class DifferenceOperation { // https://tc39.es/proposal-temporal/#sec-temporal-units
Since, enum class Unit {
Until, Year,
Month,
Week,
Day,
Hour,
Minute,
Second,
Millisecond,
Microsecond,
Nanosecond,
};
StringView temporal_unit_to_string(Unit);
// https://tc39.es/proposal-temporal/#sec-temporal-units
enum class UnitCategory {
Date,
Time,
}; };
// https://tc39.es/proposal-temporal/#sec-temporal-units
enum class UnitGroup {
Date,
Time,
DateTime,
};
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
enum class RoundingMode {
Ceil,
Floor,
Expand,
Trunc,
HalfCeil,
HalfFloor,
HalfExpand,
HalfTrunc,
HalfEven,
};
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
enum class UnsignedRoundingMode { enum class UnsignedRoundingMode {
HalfEven, HalfEven,
HalfInfinity, HalfInfinity,
@ -37,157 +75,108 @@ enum class UnsignedRoundingMode {
Zero, Zero,
}; };
// https://tc39.es/proposal-temporal/#table-unsigned-rounding-modes
enum class Sign {
Negative,
Positive,
};
struct Auto { };
struct Required { };
struct Unset { };
using Precision = Variant<Auto, u8>;
using RoundingIncrement = Variant<Unset, u64>;
using UnitDefault = Variant<Required, Unset, Auto, Unit>;
using UnitValue = Variant<Unset, Auto, Unit>;
struct SecondsStringPrecision {
struct Minute { };
Variant<Minute, Auto, u8> precision;
Unit unit;
u8 increment { 0 };
};
struct RelativeTo {
// FIXME: Make these objects represent their actual types when we re-implement them.
GC::Ptr<JS::Object> plain_relative_to; // [[PlainRelativeTo]]
GC::Ptr<JS::Object> zoned_relative_to; // [[ZonedRelativeTo]]
};
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM&, u64 increment, u64 dividend, bool inclusive);
ThrowCompletionOr<Precision> get_temporal_fractional_second_digits_option(VM&, Object const& options);
SecondsStringPrecision to_seconds_string_precision_record(UnitValue, Precision);
ThrowCompletionOr<UnitValue> get_temporal_unit_valued_option(VM&, Object const& options, PropertyKey const&, UnitGroup, UnitDefault const&, ReadonlySpan<UnitValue> extra_values = {});
ThrowCompletionOr<RelativeTo> get_temporal_relative_to_option(VM&, Object const& options);
Unit larger_of_two_temporal_units(Unit, Unit);
bool is_calendar_unit(Unit);
UnitCategory temporal_unit_category(Unit);
RoundingIncrement maximum_temporal_duration_rounding_increment(Unit);
Crypto::UnsignedBigInteger const& temporal_unit_length_in_nanoseconds(Unit);
String format_fractional_seconds(u64, Precision);
UnsignedRoundingMode get_unsigned_rounding_mode(RoundingMode, Sign);
double apply_unsigned_rounding_mode(double, double r1, double r2, UnsignedRoundingMode);
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger const& r1, Crypto::SignedBigInteger const& r2, UnsignedRoundingMode, Crypto::UnsignedBigInteger const& increment);
double round_number_to_increment(double, u64 increment, RoundingMode);
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, Crypto::UnsignedBigInteger const& increment, RoundingMode);
ThrowCompletionOr<GC::Ref<Duration>> parse_temporal_duration_string(VM&, StringView iso_string);
// 13.38 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
template<typename... Args>
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, Value argument, ErrorType error_type, Args&&... args)
{
// 1. Let number be ? ToNumber(argument).
auto number = TRY(argument.to_number(vm));
// 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception.
if (number.is_nan() || number.is_infinity())
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
// 3. Return truncate((number)).
return trunc(number.as_double());
}
// 13.38 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation
// AD-HOC: We often need to use this AO when we have a parsed StringView. This overload allows callers to avoid creating
// a PrimitiveString for the primary definition.
template<typename... Args>
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, StringView argument, ErrorType error_type, Args&&... args)
{
// 1. Let number be ? ToNumber(argument).
auto number = string_to_number(argument);
// 2. If number is NaN, +∞𝔽 or -∞𝔽, throw a RangeError exception.
if (isnan(number) || isinf(number))
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
// 3. Return truncate((number)).
return trunc(number);
}
// 13.39 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
template<typename... Args>
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args&&... args)
{
// 1. Let number be ? ToNumber(argument).
auto number = TRY(argument.to_number(vm));
// 2. If number is not an integral Number, throw a RangeError exception.
if (!number.is_integral_number())
return vm.throw_completion<RangeError>(error_type, forward<Args>(args)...);
// 3. Return (number).
return number.as_double();
}
enum class OptionType { enum class OptionType {
Boolean, Boolean,
String, String,
Number
}; };
enum class UnitGroup { using OptionDefault = Variant<Required, Empty, bool, StringView, double>;
Date,
Time,
DateTime,
};
struct TemporalInstant { ThrowCompletionOr<GC::Ref<Object>> get_options_object(VM&, Value options);
i32 year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u16 millisecond;
u16 microsecond;
u16 nanosecond;
Optional<String> time_zone_offset;
};
struct TemporalDate {
i32 year;
u8 month;
u8 day;
Optional<String> calendar;
};
struct TemporalTime {
u8 hour;
u8 minute;
u8 second;
u16 millisecond;
u16 microsecond;
u16 nanosecond;
Optional<String> calendar = {};
};
struct TemporalTimeZone {
bool z;
Optional<String> offset_string;
Optional<String> name;
};
struct TemporalYearMonth {
i32 year;
u8 month;
u8 day;
Optional<String> calendar = {};
};
struct TemporalMonthDay {
Optional<i32> year;
u8 month;
u8 day;
Optional<String> calendar = {};
};
struct ISODateTime {
i32 year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u16 millisecond;
u16 microsecond;
u16 nanosecond;
TemporalTimeZone time_zone { .z = false, .offset_string = {}, .name = {} };
Optional<String> calendar = {};
};
struct SecondsStringPrecision {
Variant<StringView, u8> precision;
StringView unit;
u32 increment;
};
struct DifferenceSettings {
String smallest_unit;
String largest_unit;
String rounding_mode;
u64 rounding_increment;
GC::Ref<Object> options;
};
struct TemporalUnitRequired { };
struct PrepareTemporalFieldsPartial { };
struct GetOptionRequired { };
using OptionDefault = Variant<GetOptionRequired, Empty, bool, StringView, double>;
using TemporalUnitDefault = Variant<TemporalUnitRequired, Optional<StringView>>;
ThrowCompletionOr<GC::MarkedVector<Value>> iterable_to_list_of_type(VM&, Value items, Vector<OptionType> const& element_types);
ThrowCompletionOr<Object*> get_options_object(VM&, Value options);
ThrowCompletionOr<Value> get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const&); ThrowCompletionOr<Value> get_option(VM&, Object const& options, PropertyKey const& property, OptionType type, ReadonlySpan<StringView> values, OptionDefault const&);
ThrowCompletionOr<String> to_temporal_overflow(VM&, Object const* options);
ThrowCompletionOr<String> to_temporal_disambiguation(VM&, Object const* options);
ThrowCompletionOr<String> to_temporal_rounding_mode(VM&, Object const& normalized_options, StringView fallback);
StringView negate_temporal_rounding_mode(StringView rounding_mode);
ThrowCompletionOr<String> to_temporal_offset(VM&, Object const* options, StringView fallback);
ThrowCompletionOr<String> to_calendar_name_option(VM&, Object const& normalized_options);
ThrowCompletionOr<String> to_time_zone_name_option(VM&, Object const& normalized_options);
ThrowCompletionOr<String> to_show_offset_option(VM&, Object const& normalized_options);
ThrowCompletionOr<u64> to_temporal_rounding_increment(VM& vm, Object const& normalized_options);
ThrowCompletionOr<void> validate_temporal_rounding_increment(VM& vm, u64 increment, u64 dividend, bool inclusive);
ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision_record(VM&, Object const& normalized_options);
ThrowCompletionOr<Optional<String>> get_temporal_unit(VM&, Object const& normalized_options, PropertyKey const&, UnitGroup, TemporalUnitDefault const& default_, Vector<StringView> const& extra_values = {});
struct RelativeTo {
GC::Ptr<PlainDate> plain_relative_to; // [[PlainRelativeTo]]
GC::Ptr<ZonedDateTime> zoned_relative_to; // [[ZonedRelativeTo]]
Optional<TimeZoneMethods> time_zone_record; // [[TimeZoneRec]]
};
ThrowCompletionOr<RelativeTo> to_relative_temporal_object(VM&, Object const& options);
Value relative_to_converted_to_value(RelativeTo const&);
StringView larger_of_two_temporal_units(StringView, StringView);
ThrowCompletionOr<Object*> merge_largest_unit_option(VM&, Object const& options, String largest_unit);
Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit);
ThrowCompletionOr<void> reject_object_with_calendar_or_time_zone(VM&, Object&);
ThrowCompletionOr<String> format_seconds_string_part(VM&, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<StringView, u8> const& precision);
double sign(double);
double sign(Crypto::SignedBigInteger const&);
UnsignedRoundingMode get_unsigned_rounding_mode(StringView rounding_mode, bool is_negative);
double apply_unsigned_rounding_mode(double x, double r1, double r2, Optional<UnsignedRoundingMode> const&);
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger const& r1, Crypto::SignedBigInteger const& r2, Optional<UnsignedRoundingMode> const&, Crypto::UnsignedBigInteger const& increment);
double round_number_to_increment(double, u64 increment, StringView rounding_mode);
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode);
Crypto::SignedBigInteger round_number_to_increment_as_if_positive(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode);
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM&, StringView iso_string);
ThrowCompletionOr<ISODateTime> parse_iso_date_time(VM&, ParseResult const& parse_result);
ThrowCompletionOr<TemporalInstant> parse_temporal_instant_string(VM&, StringView iso_string);
ThrowCompletionOr<ISODateTime> parse_temporal_zoned_date_time_string(VM&, StringView iso_string);
ThrowCompletionOr<String> parse_temporal_calendar_string(VM&, StringView iso_string);
ThrowCompletionOr<TemporalDate> parse_temporal_date_string(VM&, StringView iso_string);
ThrowCompletionOr<ISODateTime> parse_temporal_date_time_string(VM&, StringView iso_string);
ThrowCompletionOr<DurationRecord> parse_temporal_duration_string(VM&, StringView iso_string);
ThrowCompletionOr<TemporalMonthDay> parse_temporal_month_day_string(VM&, StringView iso_string);
ThrowCompletionOr<ISODateTime> parse_temporal_relative_to_string(VM&, StringView iso_string);
ThrowCompletionOr<TemporalTime> parse_temporal_time_string(VM&, StringView iso_string);
ThrowCompletionOr<TemporalTimeZone> parse_temporal_time_zone_string(VM&, StringView iso_string);
ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(VM&, StringView iso_string);
ThrowCompletionOr<double> to_positive_integer_with_truncation(VM&, Value argument);
ThrowCompletionOr<Object*> prepare_temporal_fields(VM&, Object const& fields, Vector<String> const& field_names, Variant<PrepareTemporalFieldsPartial, Vector<StringView>> const& required_fields);
ThrowCompletionOr<DifferenceSettings> get_difference_settings(VM&, DifferenceOperation, Value options_value, UnitGroup unit_group, Vector<StringView> const& disallowed_units, TemporalUnitDefault const& fallback_smallest_unit, StringView smallest_largest_default_unit);
template<size_t Size> template<size_t Size>
ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_) ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_)
@ -195,43 +184,7 @@ ThrowCompletionOr<Value> get_option(VM& vm, Object const& options, PropertyKey c
return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_); return get_option(vm, options, property, type, ReadonlySpan<StringView> { values }, default_);
} }
// 13.40 ToIntegerWithTruncation ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerwithtruncation ThrowCompletionOr<RoundingMode> get_rounding_mode_option(VM&, Object const& options, RoundingMode fallback);
template<typename... Args> ThrowCompletionOr<u64> get_rounding_increment_option(VM&, Object const& options);
ThrowCompletionOr<double> to_integer_with_truncation(VM& vm, Value argument, ErrorType error_type, Args... args)
{
// 1. Let number be ? ToIntegerOrInfinity(argument).
auto number = TRY(argument.to_number(vm));
// 2. If number is NaN, return 0.
if (number.is_nan())
return 0;
// 3. If number is +∞𝔽 or -∞𝔽, throw a RangeError exception.
if (Value(number).is_infinity()) {
return vm.template throw_completion<RangeError>(error_type, args...);
}
// 4. Return truncate((number)).
return trunc(number.as_double());
}
// 13.41 ToIntegerIfIntegral ( argument ), https://tc39.es/proposal-temporal/#sec-tointegerifintegral
template<typename... Args>
ThrowCompletionOr<double> to_integer_if_integral(VM& vm, Value argument, ErrorType error_type, Args... args)
{
// 1. Let number be ? ToNumber(argument).
auto number = TRY(argument.to_number(vm));
// 2. If number is NaN, +0𝔽, or -0𝔽, return 0.
if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
return 0;
// 3. If IsIntegralNumber(number) is false, throw a RangeError exception.
if (!number.is_integral_number())
return vm.template throw_completion<RangeError>(error_type, args...);
// 4. Return (number).
return number.as_double();
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,141 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2023-2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
#include <LibJS/Runtime/Value.h>
namespace JS::Temporal {
class Calendar final : public Object {
JS_OBJECT(Calendar, Object);
GC_DECLARE_ALLOCATOR(Calendar);
public:
virtual ~Calendar() override = default;
[[nodiscard]] String const& identifier() const { return m_identifier; }
private:
Calendar(String identifier, Object& prototype);
// 12.5 Properties of Temporal.Calendar Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-calendar-instances
String m_identifier; // [[Identifier]]
};
// 14.2 The Year-Week Record Specification Type, https://tc39.es/proposal-temporal/#sec-year-week-record-specification-type
struct YearWeekRecord {
u8 week { 0 };
i32 year { 0 };
};
bool is_builtin_calendar(StringView identifier);
ReadonlySpan<StringView> available_calendars();
ThrowCompletionOr<Calendar*> create_temporal_calendar(VM&, String const& identifier, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<Calendar*> get_builtin_calendar(VM&, String const& identifier);
Calendar* get_iso8601_calendar(VM&);
ThrowCompletionOr<Vector<String>> calendar_fields(VM&, Object& calendar, Vector<StringView> const& field_names);
ThrowCompletionOr<Object*> calendar_merge_fields(VM&, Object& calendar, Object& fields, Object& additional_fields);
ThrowCompletionOr<PlainDate*> calendar_date_add(VM&, Object& calendar, Value date, Duration&, Object* options = nullptr, FunctionObject* date_add = nullptr);
ThrowCompletionOr<GC::Ref<Duration>> calendar_date_until(VM&, CalendarMethods const&, Value one, Value two, Object const& options);
ThrowCompletionOr<double> calendar_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_month(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<String> calendar_month_code(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_day(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_day_of_week(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_day_of_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_week_of_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_year_of_week(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_days_in_week(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_days_in_month(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_days_in_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<double> calendar_months_in_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<Value> calendar_in_leap_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<Value> calendar_era(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<Value> calendar_era_year(VM&, Object& calendar, Object& date_like);
ThrowCompletionOr<Object*> to_temporal_calendar(VM&, Value);
ThrowCompletionOr<Object*> to_temporal_calendar_with_iso_default(VM&, Value);
ThrowCompletionOr<Object*> get_temporal_calendar_with_iso_default(VM&, Object&);
ThrowCompletionOr<PlainDate*> calendar_date_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
ThrowCompletionOr<PlainYearMonth*> calendar_year_month_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
ThrowCompletionOr<PlainMonthDay*> calendar_month_day_from_fields(VM&, Object& calendar, Object const& fields, Object const* options = nullptr);
ThrowCompletionOr<String> maybe_format_calendar_annotation(VM&, Object const* calendar_object, StringView show_calendar);
ThrowCompletionOr<String> format_calendar_annotation(VM&, StringView id, StringView show_calendar);
ThrowCompletionOr<bool> calendar_equals(VM&, Object& one, Object& two);
ThrowCompletionOr<Object*> consolidate_calendars(VM&, Object& one, Object& two);
u8 iso_days_in_month(i32 year, u8 month);
YearWeekRecord to_iso_week_of_year(i32 year, u8 month, u8 day);
ThrowCompletionOr<String> iso_month_code(VM&, u8 month);
ThrowCompletionOr<double> resolve_iso_month(VM&, Object const& fields);
ThrowCompletionOr<ISODateRecord> iso_date_from_fields(VM&, Object const& fields, Object const& options);
ThrowCompletionOr<ISOYearMonth> iso_year_month_from_fields(VM&, Object const& fields, Object const& options);
ThrowCompletionOr<ISOMonthDay> iso_month_day_from_fields(VM&, Object const& fields, Object const& options);
ThrowCompletionOr<Object*> default_merge_calendar_fields(VM&, Object const& fields, Object const& additional_fields);
u16 to_iso_day_of_year(i32 year, u8 month, u8 day);
u8 to_iso_day_of_week(i32 year, u8 month, u8 day);
// https://tc39.es/proposal-temporal/#table-temporal-calendar-methods-record-fields
struct CalendarMethods {
// The calendar object, or a string indicating a built-in time zone.
Variant<String, GC::Ref<Object>> receiver; // [[Reciever]]
// The calendar's dateAdd method. For a built-in calendar this is always %Temporal.Calendar.prototype.dateAdd%.
GC::Ptr<FunctionObject> date_add; // [[DateAdd]]
// The calendar's dateFromFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.dateFromFields%.
GC::Ptr<FunctionObject> date_from_fields; // [[DateFromFields]]
// The calendar's dateUntil method. For a built-in calendar this is always %Temporal.Calendar.prototype.dateUntil%.
GC::Ptr<FunctionObject> date_until; // [[DateUntil]]
// The calendar's day method. For a built-in calendar this is always %Temporal.Calendar.prototype.day%.
GC::Ptr<FunctionObject> day; // [[Day]]
// The calendar's fields method. For a built-in calendar this is always %Temporal.Calendar.prototype.fields%.
GC::Ptr<FunctionObject> fields; // [[Fields]]
// The calendar's mergeFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.mergeFields%.
GC::Ptr<FunctionObject> merge_fields; // [[MergeFields]]
// The calendar's monthDayFromFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.monthDayFromFields%.
GC::Ptr<FunctionObject> month_day_from_fields; // [[MonthDayFromFields]]
// The calendar's yearMonthFromFields method. For a built-in calendar this is always %Temporal.Calendar.prototype.yearMonthFromFields%.
GC::Ptr<FunctionObject> year_month_from_fields; // [[YearMonthFromFields]]
};
#define JS_ENUMERATE_CALENDAR_METHODS \
__JS_ENUMERATE(DateAdd, dateAdd, date_add) \
__JS_ENUMERATE(DateFromFields, dateFromFields, date_from_fields) \
__JS_ENUMERATE(DateUntil, dateUntil, date_until) \
__JS_ENUMERATE(Day, day, day) \
__JS_ENUMERATE(Fields, fields, fields) \
__JS_ENUMERATE(MergeFields, mergeFields, merge_fields) \
__JS_ENUMERATE(MonthDayFromFields, monthDayFromFields, month_day_from_fields) \
__JS_ENUMERATE(YearMonthFromFields, yearMonthFromFields, year_month_from_fields)
enum class CalendarMethod {
#define __JS_ENUMERATE(PascalName, camelName, snake_name) \
PascalName,
JS_ENUMERATE_CALENDAR_METHODS
#undef __JS_ENUMERATE
};
ThrowCompletionOr<void> calendar_methods_record_lookup(VM&, CalendarMethods&, CalendarMethod);
ThrowCompletionOr<CalendarMethods> create_calendar_methods_record(VM&, Variant<String, GC::Ref<Object>> calendar, ReadonlySpan<CalendarMethod>);
ThrowCompletionOr<Optional<CalendarMethods>> create_calendar_methods_record_from_relative_to(VM&, GC::Ptr<PlainDate>, GC::Ptr<ZonedDateTime>, ReadonlySpan<CalendarMethod>);
bool calendar_methods_record_has_looked_up(CalendarMethods const&, CalendarMethod);
bool calendar_methods_record_is_builtin(CalendarMethods const&);
ThrowCompletionOr<Value> calendar_methods_record_call(VM&, CalendarMethods const&, CalendarMethod, ReadonlySpan<Value> arguments);
}

View file

@ -1,73 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(CalendarConstructor);
// 12.2 The Temporal.Calendar Constructor, https://tc39.es/proposal-temporal/#sec-temporal-calendar-constructor
CalendarConstructor::CalendarConstructor(Realm& realm)
: NativeFunction(realm.vm().names.Calendar.as_string(), realm.intrinsics().function_prototype())
{
}
void CalendarConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 12.3.1 Temporal.Calendar.prototype, https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_calendar_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.from, from, 1, attr);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}
// 12.2.1 Temporal.Calendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal.calendar
ThrowCompletionOr<Value> CalendarConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.Calendar");
}
// 12.2.1 Temporal.Calendar ( id ), https://tc39.es/proposal-temporal/#sec-temporal.calendar
ThrowCompletionOr<GC::Ref<Object>> CalendarConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Set id to ? ToString(id).
auto identifier = TRY(vm.argument(0).to_string(vm));
// 3. If IsBuiltinCalendar(id) is false, then
if (!is_builtin_calendar(identifier)) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, identifier);
}
// 4. Return ? CreateTemporalCalendar(id, NewTarget).
return *TRY(create_temporal_calendar(vm, identifier, &new_target));
}
// 12.3.2 Temporal.Calendar.from ( calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.from
JS_DEFINE_NATIVE_FUNCTION(CalendarConstructor::from)
{
auto calendar_like = vm.argument(0);
// 1. Return ? ToTemporalCalendar(calendarLike).
return TRY(to_temporal_calendar(vm, calendar_like));
}
}

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class CalendarConstructor final : public NativeFunction {
JS_OBJECT(CalendarConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(CalendarConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~CalendarConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit CalendarConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
};
}

View file

@ -1,730 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/CalendarPrototype.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/PlainYearMonth.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(CalendarPrototype);
[[nodiscard]] static i32 iso_year(Object& temporal_object);
[[nodiscard]] static u8 iso_month(Object& temporal_object);
[[nodiscard]] static u8 iso_day(Object& temporal_object);
// 12.4 Properties of the Temporal.Calendar Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-calendar-prototype-object
CalendarPrototype::CalendarPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void CalendarPrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 12.4.2 Temporal.Calendar.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Calendar"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.id, id_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.dateFromFields, date_from_fields, 1, attr);
define_native_function(realm, vm.names.yearMonthFromFields, year_month_from_fields, 1, attr);
define_native_function(realm, vm.names.monthDayFromFields, month_day_from_fields, 1, attr);
define_native_function(realm, vm.names.dateAdd, date_add, 2, attr);
define_native_function(realm, vm.names.dateUntil, date_until, 2, attr);
define_native_function(realm, vm.names.year, year, 1, attr);
define_native_function(realm, vm.names.month, month, 1, attr);
define_native_function(realm, vm.names.monthCode, month_code, 1, attr);
define_native_function(realm, vm.names.day, day, 1, attr);
define_native_function(realm, vm.names.dayOfWeek, day_of_week, 1, attr);
define_native_function(realm, vm.names.dayOfYear, day_of_year, 1, attr);
define_native_function(realm, vm.names.weekOfYear, week_of_year, 1, attr);
define_native_function(realm, vm.names.yearOfWeek, year_of_week, 1, attr);
define_native_function(realm, vm.names.daysInWeek, days_in_week, 1, attr);
define_native_function(realm, vm.names.daysInMonth, days_in_month, 1, attr);
define_native_function(realm, vm.names.daysInYear, days_in_year, 1, attr);
define_native_function(realm, vm.names.monthsInYear, months_in_year, 1, attr);
define_native_function(realm, vm.names.inLeapYear, in_leap_year, 1, attr);
define_native_function(realm, vm.names.fields, fields, 1, attr);
define_native_function(realm, vm.names.mergeFields, merge_fields, 2, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.era, era, 1, attr);
define_native_function(realm, vm.names.eraYear, era_year, 1, attr);
}
// 12.4.3 get Temporal.Calendar.prototype.id, https://tc39.es/proposal-temporal/#sec-get-temporal.calendar.prototype.id
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::id_getter)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Return calendar.[[Identifier]].
return { PrimitiveString::create(vm, calendar->identifier()) };
}
// 12.4.4 Temporal.Calendar.prototype.dateFromFields ( fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.datefromfields
// NOTE: This is the minimum dateFromFields implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_from_fields)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. If Type(fields) is not Object, throw a TypeError exception.
auto fields = vm.argument(0);
if (!fields.is_object())
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, fields.to_string_without_side_effects());
// 5. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
// 6. Let result be ? ISODateFromFields(fields, options).
auto result = TRY(iso_date_from_fields(vm, fields.as_object(), *options));
// 7. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
return TRY(create_temporal_date(vm, result.year, result.month, result.day, calendar));
}
// 12.4.5 Temporal.Calendar.prototype.yearMonthFromFields ( fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.yearmonthfromfields
// NOTE: This is the minimum yearMonthFromFields implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::year_month_from_fields)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. If Type(fields) is not Object, throw a TypeError exception.
auto fields = vm.argument(0);
if (!fields.is_object())
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, fields.to_string_without_side_effects());
// 5. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
// 6. Let result be ? ISOYearMonthFromFields(fields, options).
auto result = TRY(iso_year_month_from_fields(vm, fields.as_object(), *options));
// 7. Return ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[ReferenceISODay]]).
return TRY(create_temporal_year_month(vm, result.year, result.month, calendar, result.reference_iso_day));
}
// 12.4.6 Temporal.Calendar.prototype.monthDayFromFields ( fields [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthdayfromfields
// NOTE: This is the minimum monthDayFromFields implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::month_day_from_fields)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. If Type(fields) is not Object, throw a TypeError exception.
auto fields = vm.argument(0);
if (!fields.is_object())
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, fields.to_string_without_side_effects());
// 5. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
// 6. Let result be ? ISOMonthDayFromFields(fields, options).
auto result = TRY(iso_month_day_from_fields(vm, fields.as_object(), *options));
// 7. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, result.[[ReferenceISOYear]]).
return TRY(create_temporal_month_day(vm, result.month, result.day, calendar, result.reference_iso_year));
}
// 12.4.7 Temporal.Calendar.prototype.dateAdd ( date, duration [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dateadd
// NOTE: This is the minimum dateAdd implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_add)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Set date to ? ToTemporalDate(date).
auto* date = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Set duration to ? ToTemporalDuration(duration).
auto duration = TRY(to_temporal_duration(vm, vm.argument(1)));
// 6. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(2)));
// 7. Let overflow be ? ToTemporalOverflow(options).
auto overflow = TRY(to_temporal_overflow(vm, options));
// 8. Let balanceResult be ? BalanceDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").
auto balance_result = TRY(balance_duration(vm, duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), Crypto::SignedBigInteger { duration->nanoseconds() }, "day"sv));
// 9. Let result be ? AddISODate(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]], duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], overflow).
auto result = TRY(add_iso_date(vm, date->iso_year(), date->iso_month(), date->iso_day(), duration->years(), duration->months(), duration->weeks(), balance_result.days, overflow));
// 10. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
return TRY(create_temporal_date(vm, result.year, result.month, result.day, calendar));
}
// 12.4.8 Temporal.Calendar.prototype.dateUntil ( one, two [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dateuntil
// NOTE: This is the minimum dateUntil implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::date_until)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Set one to ? ToTemporalDate(one).
auto* one = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Set two to ? ToTemporalDate(two).
auto* two = TRY(to_temporal_date(vm, vm.argument(1)));
// 6. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(2)));
// 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
auto largest_unit = TRY(get_temporal_unit(vm, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
// 8. If largestUnit is "auto", set largestUnit to "day".
if (largest_unit == "auto")
largest_unit = "day"_string;
// 9. Let result be DifferenceISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], largestUnit).
auto result = difference_iso_date(vm, one->iso_year(), one->iso_month(), one->iso_day(), two->iso_year(), two->iso_month(), two->iso_day(), *largest_unit);
// 10. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0));
}
// 12.4.9 Temporal.Calendar.prototype.year ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.year
// NOTE: This is the minimum year implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::year)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. Assert: temporalDateLike has an [[ISOYear]] internal slot.
// NOTE: The assertion happens in iso_year() call.
// 6. Return 𝔽(temporalDateLike.[[ISOYear]]).
return Value(iso_year(temporal_date_like.as_object()));
}
// 12.4.10 Temporal.Calendar.prototype.month ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.month
// NOTE: This is the minimum month implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::month)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is Object and temporalDateLike has an [[InitializedTemporalMonthDay]] internal slot, then
if (temporal_date_like.is_object() && is<PlainMonthDay>(temporal_date_like.as_object())) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::TemporalAmbiguousMonthOfPlainMonthDay);
}
// 5. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 6. Assert: temporalDateLike has an [[ISOMonth]] internal slot.
// NOTE: The assertion happens in iso_month() call.
// 7. Return 𝔽(temporalDateLike.[[ISOMonth]]).
return Value(iso_month(temporal_date_like.as_object()));
}
// 12.4.11 Temporal.Calendar.prototype.monthCode ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthcode
// NOTE: This is the minimum monthCode implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::month_code)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainMonthDay>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. Assert: temporalDateLike has an [[ISOMonth]] internal slot.
// NOTE: The assertion happens in iso_month() call.
// 6. Return ISOMonthCode(temporalDateLike.[[ISOMonth]]).
return PrimitiveString::create(vm, TRY(iso_month_code(vm, iso_month(temporal_date_like.as_object()))));
}
// 12.4.12 Temporal.Calendar.prototype.day ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.day
// NOTE: This is the minimum day implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::day)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]] or [[InitializedTemporalMonthDay]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainMonthDay>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. Assert: temporalDateLike has an [[ISODay]] internal slot.
// NOTE: The assertion happens in iso_day() call.
// 6. Return 𝔽(temporalDateLike.[[ISODay]]).
return Value(iso_day(temporal_date_like.as_object()));
}
// 12.4.13 Temporal.Calendar.prototype.dayOfWeek ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dayofweek
// NOTE: This is the minimum dayOfWeek implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::day_of_week)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Return 𝔽(ToISODayOfWeek(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
return Value(to_iso_day_of_week(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()));
}
// 12.4.14 Temporal.Calendar.prototype.dayOfYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.dayofyear
// NOTE: This is the minimum dayOfYear implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::day_of_year)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Return 𝔽(ToISODayOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]])).
return Value(to_iso_day_of_year(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day()));
}
// 12.4.15 Temporal.Calendar.prototype.weekOfYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.weekofyear
// NOTE: This is the minimum weekOfYear implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::week_of_year)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Let isoYearWeek be ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]).
auto iso_year_week = to_iso_week_of_year(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day());
// 6. Return 𝔽(isoYearWeek.[[Week]]).
return Value(iso_year_week.week);
}
// 12.5.16 Temporal.Calendar.prototype.yearOfWeek ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.yearofweek
// NOTE: This is the minimum yearOfWeek implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::year_of_week)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Let isoYearWeek be ToISOWeekOfYear(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]]).
auto iso_year_week = to_iso_week_of_year(temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day());
// 6. Return 𝔽(isoYearWeek.[[Year]]).
return Value(iso_year_week.year);
}
// 12.4.17 Temporal.Calendar.prototype.daysInWeek ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.daysinweek
// NOTE: This is the minimum daysInWeek implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::days_in_week)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Let temporalDate be ? ToTemporalDate(temporalDateLike).
[[maybe_unused]] auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 5. Return 7𝔽.
return Value(7);
}
// 12.4.18 Temporal.Calendar.prototype.daysInMonth ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.daysinweek
// NOTE: This is the minimum daysInMonth implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::days_in_month)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slots, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. Return 𝔽(! ISODaysInMonth(temporalDateLike.[[ISOYear]], temporalDateLike.[[ISOMonth]])).
return Value(iso_days_in_month(iso_year(temporal_date_like.as_object()), iso_month(temporal_date_like.as_object())));
}
// 12.4.19 Temporal.Calendar.prototype.daysInYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.daysinyear
// NOTE: This is the minimum daysInYear implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::days_in_year)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. Return DaysInYear(𝔽(temporalDateLike.[[ISOYear]])).
return Value(JS::days_in_year(iso_year(temporal_date_like.as_object())));
}
// 12.4.20 Temporal.Calendar.prototype.monthsInYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.monthsinyear
// NOTE: This is the minimum monthsInYear implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::months_in_year)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Perform ? ToTemporalDate(temporalDateLike).
(void)TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. Return 12𝔽.
return Value(12);
}
// 12.4.21 Temporal.Calendar.prototype.inLeapYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.inleapyear
// NOTE: This is the minimum inLeapYear implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::in_leap_year)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
auto temporal_date_like = vm.argument(0);
// 4. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 5. If InLeapYear(TimeFromYear(𝔽(temporalDateLike.[[ISOYear]]))) is 1𝔽, return true.
if (JS::in_leap_year(time_from_year(iso_year(temporal_date_like.as_object()))))
return Value(true);
// 6. Return false.
return Value(false);
}
// 12.4.22 Temporal.Calendar.prototype.fields ( fields ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.fields
// NOTE: This is the minimum fields implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::fields)
{
auto& realm = *vm.current_realm();
auto fields = vm.argument(0);
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 4. Let iteratorRecord be ? GetIterator(fields, sync).
auto iterator_record = TRY(get_iterator(vm, fields, IteratorHint::Sync));
// 5. Let fieldNames be a new empty List.
auto field_names = GC::MarkedVector<Value> { vm.heap() };
// 6. Let next be true.
// 7. Repeat, while next is not false,
while (true) {
// a. Set next to ? IteratorStep(iteratorRecord).
auto next = TRY(iterator_step(vm, iterator_record));
// b. If next is not false, then
if (!next)
break;
// i. Let nextValue be ? IteratorValue(next).
auto next_value = TRY(iterator_value(vm, *next));
// ii. If Type(nextValue) is not String, then
if (!next_value.is_string()) {
// 1. Let completion be ThrowCompletion(a newly created TypeError object).
auto completion = vm.throw_completion<TypeError>(ErrorType::TemporalInvalidCalendarFieldValue, next_value.to_string_without_side_effects());
// 2. Return ? IteratorClose(iteratorRecord, completion).
return *TRY(iterator_close(vm, iterator_record, move(completion)));
}
auto next_value_string = next_value.as_string().utf8_string();
// iii. If fieldNames contains nextValue, then
if (field_names.contains_slow(next_value)) {
// 1. Let completion be ThrowCompletion(a newly created RangeError object).
auto completion = vm.throw_completion<RangeError>(ErrorType::TemporalDuplicateCalendarField, next_value_string);
// 2. Return ? IteratorClose(iteratorRecord, completion).
return *TRY(iterator_close(vm, iterator_record, move(completion)));
}
// iv. If nextValue is not one of "year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", then
if (!next_value_string.is_one_of("year"sv, "month"sv, "monthCode"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv)) {
// 1. Let completion be ThrowCompletion(a newly created RangeError object).
auto completion = vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarFieldName, next_value_string);
// 2. Return ? IteratorClose(iteratorRecord, completion).
return *TRY(iterator_close(vm, iterator_record, move(completion)));
}
// v. Append nextValue to the end of the List fieldNames.
field_names.append(next_value);
}
// 8. Return CreateArrayFromList(fieldNames).
return Array::create_from(realm, field_names);
}
// 12.4.23 Temporal.Calendar.prototype.mergeFields ( fields, additionalFields ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.mergefields
// NOTE: This is the minimum mergeFields implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::merge_fields)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Set fields to ? ToObject(fields).
auto fields = TRY(vm.argument(0).to_object(vm));
// 4. Set additionalFields to ? ToObject(additionalFields).
auto additional_fields = TRY(vm.argument(1).to_object(vm));
// 5. Assert: calendar.[[Identifier]] is "iso8601".
VERIFY(calendar->identifier() == "iso8601"sv);
// 6. Return ? DefaultMergeCalendarFields(fields, additionalFields).
return TRY(default_merge_calendar_fields(vm, fields, additional_fields));
}
// 12.4.24 Temporal.Calendar.prototype.toString ( ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::to_string)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Return calendar.[[Identifier]].
return PrimitiveString::create(vm, calendar->identifier());
}
// 12.4.25 Temporal.Calendar.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::to_json)
{
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. Return ? ToString(calendar).
return PrimitiveString::create(vm, TRY(Value(calendar).to_string(vm)));
}
// 15.6.2.6 Temporal.Calendar.prototype.era ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.era
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::era)
{
auto temporal_date_like = vm.argument(0);
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 4. If calendar.[[Identifier]] is "iso8601", then
if (calendar->identifier() == "iso8601"sv) {
// a. Return undefined.
return js_undefined();
}
// 5. Let era be the result of implementation-defined processing of temporalDateLike and calendar.[[Identifier]].
// 6. Return era.
// NOTE: No support for non-iso8601 calendars yet.
VERIFY_NOT_REACHED();
}
// 15.6.2.7 Temporal.Calendar.prototype.eraYear ( temporalDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.calendar.prototype.erayear
JS_DEFINE_NATIVE_FUNCTION(CalendarPrototype::era_year)
{
auto temporal_date_like = vm.argument(0);
// 1. Let calendar be the this value.
// 2. Perform ? RequireInternalSlot(calendar, [[InitializedTemporalCalendar]]).
auto calendar = TRY(typed_this_object(vm));
// 3. If Type(temporalDateLike) is not Object or temporalDateLike does not have an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], or [[InitializedTemporalYearMonth]] internal slot, then
if (!temporal_date_like.is_object() || !(is<PlainDate>(temporal_date_like.as_object()) || is<PlainDateTime>(temporal_date_like.as_object()) || is<PlainYearMonth>(temporal_date_like.as_object()))) {
// a. Set temporalDateLike to ? ToTemporalDate(temporalDateLike).
temporal_date_like = TRY(to_temporal_date(vm, temporal_date_like));
}
// 4. If calendar.[[Identifier]] is "iso8601", then
if (calendar->identifier() == "iso8601"sv) {
// a. Return undefined.
return js_undefined();
}
// 5. Let eraYear be the result of implementation-defined processing of temporalDateLike and calendar.[[Identifier]].
// 6. Return 𝔽(eraYear).
// NOTE: No support for non-iso8601 calendars yet.
VERIFY_NOT_REACHED();
}
static i32 iso_year(Object& temporal_object)
{
if (is<PlainDate>(temporal_object))
return static_cast<PlainDate&>(temporal_object).iso_year();
if (is<PlainDateTime>(temporal_object))
return static_cast<PlainDateTime&>(temporal_object).iso_year();
if (is<PlainYearMonth>(temporal_object))
return static_cast<PlainYearMonth&>(temporal_object).iso_year();
if (is<PlainMonthDay>(temporal_object))
return static_cast<PlainMonthDay&>(temporal_object).iso_year();
VERIFY_NOT_REACHED();
}
static u8 iso_month(Object& temporal_object)
{
if (is<PlainDate>(temporal_object))
return static_cast<PlainDate&>(temporal_object).iso_month();
if (is<PlainDateTime>(temporal_object))
return static_cast<PlainDateTime&>(temporal_object).iso_month();
if (is<PlainYearMonth>(temporal_object))
return static_cast<PlainYearMonth&>(temporal_object).iso_month();
if (is<PlainMonthDay>(temporal_object))
return static_cast<PlainMonthDay&>(temporal_object).iso_month();
VERIFY_NOT_REACHED();
}
static u8 iso_day(Object& temporal_object)
{
if (is<PlainDate>(temporal_object))
return static_cast<PlainDate&>(temporal_object).iso_day();
if (is<PlainDateTime>(temporal_object))
return static_cast<PlainDateTime&>(temporal_object).iso_day();
if (is<PlainYearMonth>(temporal_object))
return static_cast<PlainYearMonth&>(temporal_object).iso_day();
if (is<PlainMonthDay>(temporal_object))
return static_cast<PlainMonthDay&>(temporal_object).iso_day();
VERIFY_NOT_REACHED();
}
}

View file

@ -1,52 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
namespace JS::Temporal {
class CalendarPrototype final : public PrototypeObject<CalendarPrototype, Calendar> {
JS_PROTOTYPE_OBJECT(CalendarPrototype, Calendar, Temporal.Calendar);
GC_DECLARE_ALLOCATOR(CalendarPrototype);
public:
virtual void initialize(Realm&) override;
virtual ~CalendarPrototype() override = default;
private:
explicit CalendarPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(id_getter);
JS_DECLARE_NATIVE_FUNCTION(date_from_fields);
JS_DECLARE_NATIVE_FUNCTION(year_month_from_fields);
JS_DECLARE_NATIVE_FUNCTION(month_day_from_fields);
JS_DECLARE_NATIVE_FUNCTION(date_add);
JS_DECLARE_NATIVE_FUNCTION(date_until);
JS_DECLARE_NATIVE_FUNCTION(year);
JS_DECLARE_NATIVE_FUNCTION(month);
JS_DECLARE_NATIVE_FUNCTION(month_code);
JS_DECLARE_NATIVE_FUNCTION(day);
JS_DECLARE_NATIVE_FUNCTION(day_of_week);
JS_DECLARE_NATIVE_FUNCTION(day_of_year);
JS_DECLARE_NATIVE_FUNCTION(week_of_year);
JS_DECLARE_NATIVE_FUNCTION(year_of_week);
JS_DECLARE_NATIVE_FUNCTION(days_in_week);
JS_DECLARE_NATIVE_FUNCTION(days_in_month);
JS_DECLARE_NATIVE_FUNCTION(days_in_year);
JS_DECLARE_NATIVE_FUNCTION(months_in_year);
JS_DECLARE_NATIVE_FUNCTION(in_leap_year);
JS_DECLARE_NATIVE_FUNCTION(fields);
JS_DECLARE_NATIVE_FUNCTION(merge_fields);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(era);
JS_DECLARE_NATIVE_FUNCTION(era_year);
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
/* /*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org> * Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -8,17 +9,26 @@
#pragma once #pragma once
#include <AK/Optional.h> #include <AK/Optional.h>
#include <LibGC/Root.h> #include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/Object.h> #include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/Calendar.h> #include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/PlainDate.h> #include <LibJS/Runtime/Value.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibJS/Runtime/VM.h>
namespace JS::Temporal { namespace JS::Temporal {
#define JS_ENUMERATE_DURATION_UNITS \
__JS_ENUMERATE(years) \
__JS_ENUMERATE(months) \
__JS_ENUMERATE(weeks) \
__JS_ENUMERATE(days) \
__JS_ENUMERATE(hours) \
__JS_ENUMERATE(minutes) \
__JS_ENUMERATE(seconds) \
__JS_ENUMERATE(milliseconds) \
__JS_ENUMERATE(microseconds) \
__JS_ENUMERATE(nanoseconds)
class Duration final : public Object { class Duration final : public Object {
JS_OBJECT(Duration, Object); JS_OBJECT(Duration, Object);
GC_DECLARE_ALLOCATOR(Duration); GC_DECLARE_ALLOCATOR(Duration);
@ -26,68 +36,46 @@ class Duration final : public Object {
public: public:
virtual ~Duration() override = default; virtual ~Duration() override = default;
[[nodiscard]] double years() const { return m_years; } #define __JS_ENUMERATE(unit) \
[[nodiscard]] double months() const { return m_months; } [[nodiscard]] double unit() const { return m_##unit; }
[[nodiscard]] double weeks() const { return m_weeks; } JS_ENUMERATE_DURATION_UNITS
[[nodiscard]] double days() const { return m_days; } #undef __JS_ENUMERATE
[[nodiscard]] double hours() const { return m_hours; }
[[nodiscard]] double minutes() const { return m_minutes; }
[[nodiscard]] double seconds() const { return m_seconds; }
[[nodiscard]] double milliseconds() const { return m_milliseconds; }
[[nodiscard]] double microseconds() const { return m_microseconds; }
[[nodiscard]] double nanoseconds() const { return m_nanoseconds; }
private: private:
Duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object& prototype); Duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object& prototype);
// 7.4 Properties of Temporal.Duration Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-duration-instances double m_years { 0 }; // [[Years]]
double m_years; // [[Years]] double m_months { 0 }; // [[Months]]
double m_months; // [[Months]] double m_weeks { 0 }; // [[Weeks]]
double m_weeks; // [[Weeks]] double m_days { 0 }; // [[Days]]
double m_days; // [[Days]] double m_hours { 0 }; // [[Hours]]
double m_hours; // [[Hours]] double m_minutes { 0 }; // [[Minutes]]
double m_minutes; // [[Minutes]] double m_seconds { 0 }; // [[Seconds]]
double m_seconds; // [[Seconds]] double m_milliseconds { 0 }; // [[Milliseconds]]
double m_milliseconds; // [[Milliseconds]] double m_microseconds { 0 }; // [[Microseconds]]
double m_microseconds; // [[Microseconds]] double m_nanoseconds { 0 }; // [[Nanoseconds]]
double m_nanoseconds; // [[Nanoseconds]]
}; };
// 7.5.1 Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-duration-records // 7.5.1 Date Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-date-duration-records
struct DurationRecord { struct DateDuration {
double years; double years { 0 };
double months; double months { 0 };
double weeks; double weeks { 0 };
double days; double days { 0 };
double hours;
double minutes;
double seconds;
double milliseconds;
double microseconds;
double nanoseconds;
}; };
// 7.5.2 Date Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-date-duration-records // 7.5.2 Partial Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-partial-duration-records
struct DateDurationRecord { struct PartialDuration {
double years; static PartialDuration zero()
double months; {
double weeks; return { .years = 0, .months = 0, .weeks = 0, .days = 0, .hours = 0, .minutes = 0, .seconds = 0, .milliseconds = 0, .microseconds = 0, .nanoseconds = 0 };
double days; }
};
// 7.5.3 Time Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-time-duration-records bool any_field_defined() const
struct TimeDurationRecord { {
double days; return years.has_value() || months.has_value() || weeks.has_value() || days.has_value() || hours.has_value() || minutes.has_value() || seconds.has_value() || milliseconds.has_value() || microseconds.has_value() || nanoseconds.has_value();
double hours; }
double minutes;
double seconds;
double milliseconds;
double microseconds;
double nanoseconds;
};
// 7.5.4 Partial Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-partial-duration-records
struct PartialDurationRecord {
Optional<double> years; Optional<double> years;
Optional<double> months; Optional<double> months;
Optional<double> weeks; Optional<double> weeks;
@ -100,79 +88,41 @@ struct PartialDurationRecord {
Optional<double> nanoseconds; Optional<double> nanoseconds;
}; };
// Used by MoveRelativeDate to temporarily hold values // A time duration is an integer in the inclusive interval from -maxTimeDuration to maxTimeDuration, where
struct MoveRelativeDateResult { // maxTimeDuration = 2**53 × 10**9 - 1 = 9,007,199,254,740,991,999,999,999. It represents the portion of a
GC::Root<PlainDate> relative_to; // Temporal.Duration object that deals with time units, but as a combined value of total nanoseconds.
double days; using TimeDuration = Crypto::SignedBigInteger;
extern TimeDuration const MAX_TIME_DURATION;
// 7.5.3 Internal Duration Records, https://tc39.es/proposal-temporal/#sec-temporal-internal-duration-records
struct InternalDuration {
DateDuration date;
TimeDuration time;
}; };
// Used by RoundDuration to temporarily hold values DateDuration zero_date_duration(VM&);
struct RoundedDuration { InternalDuration to_internal_duration_record(VM&, Duration const&);
DurationRecord duration_record; InternalDuration to_internal_duration_record_with_24_hour_days(VM&, Duration const&);
double total; ThrowCompletionOr<GC::Ref<Duration>> temporal_duration_from_internal(VM&, InternalDuration const&, Unit largest_unit);
}; ThrowCompletionOr<DateDuration> create_date_duration_record(VM&, double years, double months, double weeks, double days);
ThrowCompletionOr<InternalDuration> combine_date_and_time_duration(VM&, DateDuration, TimeDuration);
// Table 8: Duration Record Fields, https://tc39.es/proposal-temporal/#table-temporal-duration-record-fields ThrowCompletionOr<GC::Ref<Duration>> to_temporal_duration(VM&, Value);
i8 duration_sign(Duration const&);
template<typename StructT, typename ValueT> i8 date_duration_sign(DateDuration const&);
struct TemporalDurationRecordField {
ValueT StructT::*field_name { nullptr };
PropertyKey property_name;
};
DurationRecord create_duration_record(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
ThrowCompletionOr<DurationRecord> create_duration_record(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
DateDurationRecord create_date_duration_record(double years, double months, double weeks, double days);
ThrowCompletionOr<DateDurationRecord> create_date_duration_record(VM&, double years, double months, double weeks, double days);
ThrowCompletionOr<TimeDurationRecord> create_time_duration_record(VM&, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
ThrowCompletionOr<GC::Ref<Duration>> to_temporal_duration(VM&, Value item);
ThrowCompletionOr<DurationRecord> to_temporal_duration_record(VM&, Value temporal_duration_like);
i8 duration_sign(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds); bool is_valid_duration(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
StringView default_temporal_largest_unit(double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds); Unit default_temporal_largest_unit(Duration const&);
ThrowCompletionOr<PartialDurationRecord> to_temporal_partial_duration_record(VM&, Value temporal_duration_like); ThrowCompletionOr<PartialDuration> to_temporal_partial_duration_record(VM&, Value temporal_duration_like);
ThrowCompletionOr<GC::Ref<Duration>> create_temporal_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, FunctionObject const* new_target = nullptr); ThrowCompletionOr<GC::Ref<Duration>> create_temporal_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, GC::Ptr<FunctionObject> new_target = {});
GC::Ref<Duration> create_negated_temporal_duration(VM&, Duration const& duration); GC::Ref<Duration> create_negated_temporal_duration(VM&, Duration const&);
ThrowCompletionOr<double> calculate_offset_shift(VM&, Value relative_to_value, double years, double months, double weeks, double days); TimeDuration time_duration_from_components(double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
Crypto::SignedBigInteger total_duration_nanoseconds(double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, double offset_shift); ThrowCompletionOr<TimeDuration> add_time_duration(VM&, TimeDuration const&, TimeDuration const&);
ThrowCompletionOr<TimeDurationRecord> balance_time_duration(VM& vm, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, StringView largest_unit); ThrowCompletionOr<TimeDuration> add_24_hour_days_to_time_duration(VM&, TimeDuration const&, double days);
ThrowCompletionOr<TimeDurationRecord> balance_duration(VM&, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, StringView largest_unit, Object* relative_to = nullptr); i8 compare_time_duration(TimeDuration const&, TimeDuration const&);
ThrowCompletionOr<TimeDuration> round_time_duration_to_increment(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, RoundingMode);
enum class Overflow { i8 time_duration_sign(TimeDuration const&);
Positive, ThrowCompletionOr<TimeDuration> round_time_duration(VM&, TimeDuration const&, Crypto::UnsignedBigInteger const& increment, Unit, RoundingMode);
Negative, double total_time_duration(TimeDuration const&, Unit);
}; String temporal_duration_to_string(Duration const&, Precision);
ThrowCompletionOr<GC::Ref<Duration>> add_durations(VM&, ArithmeticOperation, Duration const&, Value);
Variant<TimeDurationRecord, Overflow> balance_possibly_infinite_time_duration(VM& vm, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, Crypto::SignedBigInteger const& nanoseconds, StringView largest_unit);
ThrowCompletionOr<DateDurationRecord> unbalance_duration_relative(VM&, double years, double months, double weeks, double days, StringView largest_unit, Value relative_to);
ThrowCompletionOr<DateDurationRecord> balance_duration_relative(VM&, double years, double months, double weeks, double days, StringView largest_unit, Value relative_to);
ThrowCompletionOr<DurationRecord> add_duration(VM&, double years1, double months1, double weeks1, double days1, double hours1, double minutes1, double seconds1, double milliseconds1, double microseconds1, double nanoseconds1, double years2, double months2, double weeks2, double days2, double hours2, double minutes2, double seconds2, double milliseconds2, double microseconds2, double nanoseconds2, Value relative_to_value);
ThrowCompletionOr<MoveRelativeDateResult> move_relative_date(VM&, Object& calendar, PlainDate& relative_to, Duration& duration, FunctionObject* date_add);
ThrowCompletionOr<ZonedDateTime*> move_relative_zoned_date_time(VM&, ZonedDateTime&, double years, double months, double weeks, double days);
ThrowCompletionOr<RoundedDuration> round_duration(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, u32 increment, StringView unit, StringView rounding_mode, Object* relative_to_object = nullptr, Optional<CalendarMethods> const& = {});
ThrowCompletionOr<DurationRecord> adjust_rounded_duration_days(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, u32 increment, StringView unit, StringView rounding_mode, Object* relative_to_object);
ThrowCompletionOr<String> temporal_duration_to_string(VM&, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Variant<StringView, u8> const& precision);
ThrowCompletionOr<GC::Ref<Duration>> add_duration_to_or_subtract_duration_from_duration(VM&, ArithmeticOperation, Duration const&, Value other_value, Value options_value);
// 7.5.22 DaysUntil ( earlier, later ), https://tc39.es/proposal-temporal/#sec-temporal-daysuntil
template<typename EarlierObjectType, typename LaterObjectType>
double days_until(EarlierObjectType& earlier, LaterObjectType& later)
{
// 1. Let epochDays1 be MakeDay(𝔽(earlier.[[ISOYear]]), 𝔽(earlier.[[ISOMonth]] - 1), 𝔽(earlier.[[ISODay]])).
auto epoch_days_1 = make_day(earlier.iso_year(), earlier.iso_month() - 1, earlier.iso_day());
// 2. Assert: epochDays1 is finite.
VERIFY(isfinite(epoch_days_1));
// 3. Let epochDays2 be MakeDay(𝔽(later.[[ISOYear]]), 𝔽(later.[[ISOMonth]] - 1), 𝔽(later.[[ISODay]])).
auto epoch_days_2 = make_day(later.iso_year(), later.iso_month() - 1, later.iso_day());
// 4. Assert: epochDays2 is finite.
VERIFY(isfinite(epoch_days_2));
// 5. Return (epochDays2) - (epochDays1).
return epoch_days_2 - epoch_days_1;
}
} }

View file

@ -1,16 +1,14 @@
/* /*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org> * Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h> #include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Duration.h> #include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/DurationConstructor.h> #include <LibJS/Runtime/Temporal/DurationConstructor.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS::Temporal { namespace JS::Temporal {
@ -44,7 +42,7 @@ ThrowCompletionOr<Value> DurationConstructor::call()
auto& vm = this->vm(); auto& vm = this->vm();
// 1. If NewTarget is undefined, then // 1. If NewTarget is undefined, then
// a. Throw a TypeError exception. // a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.Duration"); return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.Duration");
} }
@ -53,55 +51,51 @@ ThrowCompletionOr<GC::Ref<Object>> DurationConstructor::construct(FunctionObject
{ {
auto& vm = this->vm(); auto& vm = this->vm();
// 2. Let y be ? ToIntegerIfIntegral(years). auto next_integer_argument = [&, index = 0]() mutable -> ThrowCompletionOr<double> {
auto y = TRY(to_integer_if_integral(vm, vm.argument(0), ErrorType::TemporalInvalidDuration)); if (auto value = vm.argument(index++); !value.is_undefined())
return to_integer_if_integral(vm, value, ErrorType::TemporalInvalidDuration);
return 0;
};
// 3. Let mo be ? ToIntegerIfIntegral(months). // 2. If years is undefined, let y be 0; else let y be ? ToIntegerIfIntegral(years).
auto mo = TRY(to_integer_if_integral(vm, vm.argument(1), ErrorType::TemporalInvalidDuration)); auto years = TRY(next_integer_argument());
// 4. Let w be ? ToIntegerIfIntegral(weeks). // 3. If months is undefined, let mo be 0; else let mo be ? ToIntegerIfIntegral(months).
auto w = TRY(to_integer_if_integral(vm, vm.argument(2), ErrorType::TemporalInvalidDuration)); auto months = TRY(next_integer_argument());
// 5. Let d be ? ToIntegerIfIntegral(days). // 4. If weeks is undefined, let w be 0; else let w be ? ToIntegerIfIntegral(weeks).
auto d = TRY(to_integer_if_integral(vm, vm.argument(3), ErrorType::TemporalInvalidDuration)); auto weeks = TRY(next_integer_argument());
// 6. Let h be ? ToIntegerIfIntegral(hours). // 5. If days is undefined, let d be 0; else let d be ? ToIntegerIfIntegral(days).
auto h = TRY(to_integer_if_integral(vm, vm.argument(4), ErrorType::TemporalInvalidDuration)); auto days = TRY(next_integer_argument());
// 7. Let m be ? ToIntegerIfIntegral(minutes). // 6. If hours is undefined, let h be 0; else let h be ? ToIntegerIfIntegral(hours).
auto m = TRY(to_integer_if_integral(vm, vm.argument(5), ErrorType::TemporalInvalidDuration)); auto hours = TRY(next_integer_argument());
// 8. Let s be ? ToIntegerIfIntegral(seconds). // 7. If minutes is undefined, let m be 0; else let m be ? ToIntegerIfIntegral(minutes).
auto s = TRY(to_integer_if_integral(vm, vm.argument(6), ErrorType::TemporalInvalidDuration)); auto minutes = TRY(next_integer_argument());
// 9. Let ms be ? ToIntegerIfIntegral(milliseconds). // 8. If seconds is undefined, let s be 0; else let s be ? ToIntegerIfIntegral(seconds).
auto ms = TRY(to_integer_if_integral(vm, vm.argument(7), ErrorType::TemporalInvalidDuration)); auto seconds = TRY(next_integer_argument());
// 10. Let mis be ? ToIntegerIfIntegral(microseconds). // 9. If milliseconds is undefined, let ms be 0; else let ms be ? ToIntegerIfIntegral(milliseconds).
auto mis = TRY(to_integer_if_integral(vm, vm.argument(8), ErrorType::TemporalInvalidDuration)); auto milliseconds = TRY(next_integer_argument());
// 11. Let ns be ? ToIntegerIfIntegral(nanoseconds). // 10. If microseconds is undefined, let mis be 0; else let mis be ? ToIntegerIfIntegral(microseconds).
auto ns = TRY(to_integer_if_integral(vm, vm.argument(9), ErrorType::TemporalInvalidDuration)); auto microseconds = TRY(next_integer_argument());
// 11. If nanoseconds is undefined, let ns be 0; else let ns be ? ToIntegerIfIntegral(nanoseconds).
auto nanoseconds = TRY(next_integer_argument());
// 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget). // 12. Return ? CreateTemporalDuration(y, mo, w, d, h, m, s, ms, mis, ns, NewTarget).
return TRY(create_temporal_duration(vm, y, mo, w, d, h, m, s, ms, mis, ns, &new_target)); return TRY(create_temporal_duration(vm, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, new_target));
} }
// 7.2.2 Temporal.Duration.from ( item ), https://tc39.es/proposal-temporal/#sec-temporal.duration.from // 7.2.2 Temporal.Duration.from ( item ), https://tc39.es/proposal-temporal/#sec-temporal.duration.from
JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::from) JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::from)
{ {
auto item = vm.argument(0); // 1. Return ? ToTemporalDuration(item).
return TRY(to_temporal_duration(vm, vm.argument(0)));
// 1. If Type(item) is Object and item has an [[InitializedTemporalDuration]] internal slot, then
if (item.is_object() && is<Duration>(item.as_object())) {
auto& duration = static_cast<Duration&>(item.as_object());
// a. Return ! CreateTemporalDuration(item.[[Years]], item.[[Months]], item.[[Weeks]], item.[[Days]], item.[[Hours]], item.[[Minutes]], item.[[Seconds]], item.[[Milliseconds]], item.[[Microseconds]], item.[[Nanoseconds]]).
return MUST(create_temporal_duration(vm, duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds()));
}
// 2. Return ? ToTemporalDuration(item).
return TRY(to_temporal_duration(vm, item));
} }
// 7.2.3 Temporal.Duration.compare ( one, two [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.compare // 7.2.3 Temporal.Duration.compare ( one, two [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.compare
@ -113,36 +107,72 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare)
// 2. Set two to ? ToTemporalDuration(two). // 2. Set two to ? ToTemporalDuration(two).
auto two = TRY(to_temporal_duration(vm, vm.argument(1))); auto two = TRY(to_temporal_duration(vm, vm.argument(1)));
// 3. Set options to ? GetOptionsObject(options). // 3. Let resolvedOptions be ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(2))); auto resolved_options = TRY(get_options_object(vm, vm.argument(2)));
// 4. Let relativeTo be ? ToRelativeTemporalObject(options). // 4. Let relativeToRecord be ? GetTemporalRelativeToOption(resolvedOptions).
auto relative_to = relative_to_converted_to_value(TRY(to_relative_temporal_object(vm, *options))); auto relative_to_record = TRY(get_temporal_relative_to_option(vm, resolved_options));
// 5. Let shift1 be ? CalculateOffsetShift(relativeTo, one.[[Years]], one.[[Months]], one.[[Weeks]], one.[[Days]]). // 5. If one.[[Years]] = two.[[Years]], and one.[[Months]] = two.[[Months]], and one.[[Weeks]] = two.[[Weeks]], and
auto shift1 = TRY(calculate_offset_shift(vm, relative_to, one->years(), one->months(), one->weeks(), one->days())); // one.[[Days]] = two.[[Days]], and one.[[Hours]] = two.[[Hours]], and one.[[Minutes]] = two.[[Minutes]], and
// one.[[Seconds]] = two.[[Seconds]], and one.[[Milliseconds]] = two.[[Milliseconds]], and
// 6. Let shift2 be ? CalculateOffsetShift(relativeTo, two.[[Years]], two.[[Months]], two.[[Weeks]], two.[[Days]]). // one.[[Microseconds]] = two.[[Microseconds]], and one.[[Nanoseconds]] = two.[[Nanoseconds]], then
auto shift2 = TRY(calculate_offset_shift(vm, relative_to, two->years(), two->months(), two->weeks(), two->days())); if (one->years() == two->years()
&& one->months() == two->months()
double days1; && one->weeks() == two->weeks()
double days2; && one->days() == two->days()
&& one->hours() == two->hours()
// 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], one.[[Weeks]], or two.[[Weeks]] are not 0, then && one->minutes() == two->minutes()
if (one->years() != 0 || two->years() != 0 || one->months() != 0 || two->months() != 0 || one->weeks() != 0 || two->weeks() != 0) { && one->seconds() == two->seconds()
// a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], one.[[Months]], one.[[Weeks]], one.[[Days]], "day", relativeTo). && one->milliseconds() == two->milliseconds()
auto unbalance_result1 = TRY(unbalance_duration_relative(vm, one->years(), one->months(), one->weeks(), one->days(), "day"sv, relative_to)); && one->microseconds() == two->microseconds()
&& one->nanoseconds() == two->nanoseconds()) {
// b. Let unbalanceResult2 be ? UnbalanceDurationRelative(two.[[Years]], two.[[Months]], two.[[Weeks]], two.[[Days]], "day", relativeTo). // a. Return +0𝔽.
auto unbalance_result2 = TRY(unbalance_duration_relative(vm, two->years(), two->months(), two->weeks(), two->days(), "day"sv, relative_to)); return 0;
// c. Let days1 be unbalanceResult1.[[Days]].
days1 = unbalance_result1.days;
// d. Let days2 be unbalanceResult2.[[Days]].
days2 = unbalance_result2.days;
} }
// 8. Else,
// 6. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
// 7. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
auto [zoned_relative_to, plain_relative_to] = relative_to_record;
// 8. Let largestUnit1 be DefaultTemporalLargestUnit(one).
auto largest_unit1 = default_temporal_largest_unit(one);
// 9. Let largestUnit2 be DefaultTemporalLargestUnit(two).
auto largest_unit2 = default_temporal_largest_unit(two);
// 10. Let duration1 be ToInternalDurationRecord(one).
auto duration1 = to_internal_duration_record(vm, one);
// 11. Let duration2 be ToInternalDurationRecord(two).
auto duration2 = to_internal_duration_record(vm, two);
// 12. If zonedRelativeTo is not undefined, and either TemporalUnitCategory(largestUnit1) or TemporalUnitCategory(largestUnit2) is date, then
if (zoned_relative_to && (temporal_unit_category(largest_unit1) == UnitCategory::Date || temporal_unit_category(largest_unit2) == UnitCategory::Date)) {
// FIXME: a. Let timeZone be zonedRelativeTo.[[TimeZone]].
// FIXME: b. Let calendar be zonedRelativeTo.[[Calendar]].
// FIXME: c. Let after1 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration1, constrain).
// FIXME: d. Let after2 be ? AddZonedDateTime(zonedRelativeTo.[[EpochNanoseconds]], timeZone, calendar, duration2, constrain).
// FIXME: e. If after1 > after2, return 1𝔽.
// FIXME: f. If after1 < after2, return -1𝔽.
// g. Return +0𝔽.
return 0;
}
double days1 = 0;
double days2 = 0;
// 13. If IsCalendarUnit(largestUnit1) is true or IsCalendarUnit(largestUnit2) is true, then
if (is_calendar_unit(largest_unit1) || is_calendar_unit(largest_unit2)) {
// a. If plainRelativeTo is undefined, throw a RangeError exception.
if (!plain_relative_to)
return vm.throw_completion<RangeError>(ErrorType::TemporalMissingStartingPoint, "calendar units");
// FIXME: b. Let days1 be ? DateDurationDays(duration1.[[Date]], plainRelativeTo).
// FIXME: c. Let days2 be ? DateDurationDays(duration2.[[Date]], plainRelativeTo).
}
// 14. Else,
else { else {
// a. Let days1 be one.[[Days]]. // a. Let days1 be one.[[Days]].
days1 = one->days(); days1 = one->days();
@ -151,22 +181,14 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare)
days2 = two->days(); days2 = two->days();
} }
// 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], one.[[Minutes]], one.[[Seconds]], one.[[Milliseconds]], one.[[Microseconds]], one.[[Nanoseconds]], shift1). // 15. Let timeDuration1 be ? Add24HourDaysToTimeDuration(duration1.[[Time]], days1).
auto ns1 = total_duration_nanoseconds(days1, one->hours(), one->minutes(), one->seconds(), one->milliseconds(), one->microseconds(), Crypto::SignedBigInteger { one->nanoseconds() }, shift1); auto time_duration1 = TRY(add_24_hour_days_to_time_duration(vm, duration1.time, days1));
// 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], two.[[Minutes]], two.[[Seconds]], two.[[Milliseconds]], two.[[Microseconds]], two.[[Nanoseconds]], shift2). // 16. Let timeDuration2 be ? Add24HourDaysToTimeDuration(duration2.[[Time]], days2).
auto ns2 = total_duration_nanoseconds(days2, two->hours(), two->minutes(), two->seconds(), two->milliseconds(), two->microseconds(), Crypto::SignedBigInteger { two->nanoseconds() }, shift2); auto time_duration2 = TRY(add_24_hour_days_to_time_duration(vm, duration2.time, days2));
// 11. If ns1 > ns2, return 1𝔽. // 17. Return 𝔽(CompareTimeDuration(timeDuration1, timeDuration2)).
if (ns1 > ns2) return compare_time_duration(time_duration1, time_duration2);
return Value(1);
// 12. If ns1 < ns2, return -1𝔽.
if (ns1 < ns2)
return Value(-1);
// 13. Return +0𝔽.
return Value(0);
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */

View file

@ -1,16 +1,12 @@
/* /*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Duration.h> #include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/DurationPrototype.h> #include <LibJS/Runtime/Temporal/DurationPrototype.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <math.h>
namespace JS::Temporal { namespace JS::Temporal {
@ -28,19 +24,14 @@ void DurationPrototype::initialize(Realm& realm)
auto& vm = this->vm(); auto& vm = this->vm();
// 7.3.2 Temporal.Duration.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype-@@tostringtag // 7.3.2 Temporal.Duration.prototype[ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Duration"_string), Attribute::Configurable); define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Duration"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.years, years_getter, {}, Attribute::Configurable); #define __JS_ENUMERATE(unit) \
define_native_accessor(realm, vm.names.months, months_getter, {}, Attribute::Configurable); define_native_accessor(realm, vm.names.unit, unit##_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.weeks, weeks_getter, {}, Attribute::Configurable); JS_ENUMERATE_DURATION_UNITS
define_native_accessor(realm, vm.names.days, days_getter, {}, Attribute::Configurable); #undef __JS_ENUMERATE
define_native_accessor(realm, vm.names.hours, hours_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.minutes, minutes_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.seconds, seconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.milliseconds, milliseconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.microseconds, microseconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.nanoseconds, nanoseconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.sign, sign_getter, {}, Attribute::Configurable); define_native_accessor(realm, vm.names.sign, sign_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.blank, blank_getter, {}, Attribute::Configurable); define_native_accessor(realm, vm.names.blank, blank_getter, {}, Attribute::Configurable);
@ -59,114 +50,27 @@ void DurationPrototype::initialize(Realm& realm)
} }
// 7.3.3 get Temporal.Duration.prototype.years, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.years // 7.3.3 get Temporal.Duration.prototype.years, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.years
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::years_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Years]]).
return Value(duration->years());
}
// 7.3.4 get Temporal.Duration.prototype.months, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.months // 7.3.4 get Temporal.Duration.prototype.months, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.months
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::months_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Months]]).
return Value(duration->months());
}
// 7.3.5 get Temporal.Duration.prototype.weeks, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.weeks // 7.3.5 get Temporal.Duration.prototype.weeks, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.weeks
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::weeks_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Weeks]]).
return Value(duration->weeks());
}
// 7.3.6 get Temporal.Duration.prototype.days, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.days // 7.3.6 get Temporal.Duration.prototype.days, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.days
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::days_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Days]]).
return Value(duration->days());
}
// 7.3.7 get Temporal.Duration.prototype.hours, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.hours // 7.3.7 get Temporal.Duration.prototype.hours, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.hours
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::hours_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Hours]]).
return Value(duration->hours());
}
// 7.3.8 get Temporal.Duration.prototype.minutes, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.minutes // 7.3.8 get Temporal.Duration.prototype.minutes, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.minutes
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::minutes_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Minutes]]).
return Value(duration->minutes());
}
// 7.3.9 get Temporal.Duration.prototype.seconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.seconds // 7.3.9 get Temporal.Duration.prototype.seconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.seconds
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::seconds_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Seconds]]).
return Value(duration->seconds());
}
// 7.3.10 get Temporal.Duration.prototype.milliseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.milliseconds // 7.3.10 get Temporal.Duration.prototype.milliseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.milliseconds
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::milliseconds_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Milliseconds]]).
return Value(duration->milliseconds());
}
// 7.3.11 get Temporal.Duration.prototype.microseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.microseconds // 7.3.11 get Temporal.Duration.prototype.microseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.microseconds
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::microseconds_getter)
{
// 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(duration.[[Microseconds]]).
return Value(duration->microseconds());
}
// 7.3.12 get Temporal.Duration.prototype.nanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.nanoseconds // 7.3.12 get Temporal.Duration.prototype.nanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.nanoseconds
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::nanoseconds_getter) #define __JS_ENUMERATE(unit) \
{ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::unit##_getter) \
// 1. Let duration be the this value. { \
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). /* 1. Let duration be the this value. */ \
auto duration = TRY(typed_this_object(vm)); /* 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). */ \
auto duration = TRY(typed_this_object(vm)); \
// 3. Return 𝔽(duration.[[Nanoseconds]]). \
return Value(duration->nanoseconds()); /* 3. Return 𝔽(duration.[[<unit>]]). */ \
} return duration->unit(); \
}
JS_ENUMERATE_DURATION_UNITS
#undef __JS_ENUMERATE
// 7.3.13 get Temporal.Duration.prototype.sign, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.sign // 7.3.13 get Temporal.Duration.prototype.sign, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.sign
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::sign_getter) JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::sign_getter)
@ -175,8 +79,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::sign_getter)
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return 𝔽(! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]])). // 3. Return 𝔽(DurationSign(duration)).
return Value(duration_sign(duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds())); return duration_sign(duration);
} }
// 7.3.14 get Temporal.Duration.prototype.blank, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.blank // 7.3.14 get Temporal.Duration.prototype.blank, https://tc39.es/proposal-temporal/#sec-get-temporal.duration.prototype.blank
@ -186,15 +90,12 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::blank_getter)
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Let sign be ! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]]). // 3. If DurationSign(duration) = 0, return true.
auto sign = duration_sign(duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds()); if (duration_sign(duration) == 0)
return true;
// 4. If sign = 0, return true. // 4. Return false.
if (sign == 0) return false;
return Value(true);
// 5. Return false.
return Value(false);
} }
// 7.3.15 Temporal.Duration.prototype.with ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with // 7.3.15 Temporal.Duration.prototype.with ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.with
@ -278,7 +179,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::negated)
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return ! CreateNegatedTemporalDuration(duration). // 3. Return CreateNegatedTemporalDuration(duration).
return create_negated_temporal_duration(vm, duration); return create_negated_temporal_duration(vm, duration);
} }
@ -290,35 +191,33 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::abs)
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return ! CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])). // 3. Return ! CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])).
return TRY(create_temporal_duration(vm, fabs(duration->years()), fabs(duration->months()), fabs(duration->weeks()), fabs(duration->days()), fabs(duration->hours()), fabs(duration->minutes()), fabs(duration->seconds()), fabs(duration->milliseconds()), fabs(duration->microseconds()), fabs(duration->nanoseconds()))); return MUST(create_temporal_duration(vm, fabs(duration->years()), fabs(duration->months()), fabs(duration->weeks()), fabs(duration->days()), fabs(duration->hours()), fabs(duration->minutes()), fabs(duration->seconds()), fabs(duration->milliseconds()), fabs(duration->microseconds()), fabs(duration->nanoseconds())));
} }
// 7.3.18 Temporal.Duration.prototype.add ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add // 7.3.18 Temporal.Duration.prototype.add ( other ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.add
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::add) JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::add)
{ {
auto other = vm.argument(0); auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let duration be the this value. // 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromDuration(add, duration, other, options). // 3. Return ? AddDurations(ADD, duration, other).
return TRY(add_duration_to_or_subtract_duration_from_duration(vm, ArithmeticOperation::Add, duration, other, options)); return TRY(add_durations(vm, ArithmeticOperation::Add, duration, other));
} }
// 7.3.19 Temporal.Duration.prototype.subtract ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract // 7.3.19 Temporal.Duration.prototype.subtract ( other ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::subtract) JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::subtract)
{ {
auto other = vm.argument(0); auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let duration be the this value. // 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromDuration(subtract, duration, other, options). // 3. Return ? AddDurations(SUBTRACT, duration, other).
return TRY(add_duration_to_or_subtract_duration_from_duration(vm, ArithmeticOperation::Subtract, duration, other, options)); return TRY(add_durations(vm, ArithmeticOperation::Subtract, duration, other));
} }
// 7.3.20 Temporal.Duration.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.round // 7.3.20 Temporal.Duration.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.round
@ -338,17 +237,18 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject); return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
} }
Object* round_to; GC::Ptr<Object> round_to;
// 4. If Type(roundTo) is String, then // 4. If roundTo is a String, then
if (round_to_value.is_string()) { if (round_to_value.is_string()) {
// a. Let paramString be roundTo. // a. Let paramString be roundTo.
auto param_string = round_to_value;
// b. Set roundTo to OrdinaryObjectCreate(null). // b. Set roundTo to OrdinaryObjectCreate(null).
round_to = Object::create(realm, nullptr); round_to = Object::create(realm, nullptr);
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString). // c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0))); MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, param_string));
} }
// 5. Else, // 5. Else,
else { else {
@ -362,156 +262,244 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
// 7. Let largestUnitPresent be true. // 7. Let largestUnitPresent be true.
bool largest_unit_present = true; bool largest_unit_present = true;
// 8. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", datetime, undefined). // 8. NOTE: The following steps read options and perform independent validation in alphabetical order
auto smallest_unit = TRY(get_temporal_unit(vm, *round_to, vm.names.smallestUnit, UnitGroup::DateTime, Optional<StringView> {})); // (GetTemporalRelativeToOption reads "relativeTo", GetRoundingIncrementOption reads "roundingIncrement" and
// GetRoundingModeOption reads "roundingMode").
// 9. If smallestUnit is undefined, then // 9. Let largestUnit be ? GetTemporalUnitValuedOption(roundTo, "largestUnit", DATETIME, UNSET, « auto »).
if (!smallest_unit.has_value()) { auto largest_unit = TRY(get_temporal_unit_valued_option(vm, *round_to, vm.names.largestUnit, UnitGroup::DateTime, Unset {}, { { Auto {} } }));
// 10. Let relativeToRecord be ? GetTemporalRelativeToOption(roundTo).
// 11. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
// 12. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
auto [zoned_relative_to, plain_relative_to] = TRY(get_temporal_relative_to_option(vm, *round_to));
// 13. Let roundingIncrement be ? GetRoundingIncrementOption(roundTo).
auto rounding_increment = TRY(get_rounding_increment_option(vm, *round_to));
// 14. Let roundingMode be ? GetRoundingModeOption(roundTo, HALF-EXPAND).
auto rounding_mode = TRY(get_rounding_mode_option(vm, *round_to, RoundingMode::HalfExpand));
// 15. Let smallestUnit be ? GetTemporalUnitValuedOption(roundTo, "smallestUnit", DATETIME, UNSET).
auto smallest_unit = TRY(get_temporal_unit_valued_option(vm, *round_to, vm.names.smallestUnit, UnitGroup::DateTime, Unset {}));
// 16. If smallestUnit is UNSET, then
if (smallest_unit.has<Unset>()) {
// a. Set smallestUnitPresent to false. // a. Set smallestUnitPresent to false.
smallest_unit_present = false; smallest_unit_present = false;
// b. Set smallestUnit to "nanosecond". // b. Set smallestUnit to NANOSECOND.
smallest_unit = "nanosecond"_string; smallest_unit = Unit::Nanosecond;
} }
// 10. Let defaultLargestUnit be ! DefaultTemporalLargestUnit(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]]). auto smallest_unit_value = smallest_unit.get<Unit>();
auto default_largest_unit = default_temporal_largest_unit(duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds());
// 11. Set defaultLargestUnit to ! LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit). // 17. Let existingLargestUnit be DefaultTemporalLargestUnit(duration).
default_largest_unit = larger_of_two_temporal_units(default_largest_unit, *smallest_unit); auto existing_largest_unit = default_temporal_largest_unit(duration);
// 12. Let largestUnit be ? GetTemporalUnit(roundTo, "largestUnit", datetime, undefined, « "auto" »). // 18. Let defaultLargestUnit be LargerOfTwoTemporalUnits(existingLargestUnit, smallestUnit).
auto largest_unit = TRY(get_temporal_unit(vm, *round_to, vm.names.largestUnit, UnitGroup::DateTime, Optional<StringView> {}, { "auto"sv })); auto default_largest_unit = larger_of_two_temporal_units(existing_largest_unit, smallest_unit_value);
// 13. If largestUnit is undefined, then // 19. If largestUnit is UNSET, then
if (!largest_unit.has_value()) { if (largest_unit.has<Unset>()) {
// a. Set largestUnitPresent to false. // a. Set largestUnitPresent to false.
largest_unit_present = false; largest_unit_present = false;
// b. Set largestUnit to defaultLargestUnit. // b. Set largestUnit to defaultLargestUnit.
largest_unit = MUST(String::from_utf8(default_largest_unit)); largest_unit = default_largest_unit;
} }
// 14. Else if largestUnit is "auto", then // 20. Else if largestUnit is AUTO, then
else if (*largest_unit == "auto"sv) { else if (largest_unit.has<Auto>()) {
// a. Set largestUnit to defaultLargestUnit. // a. Set largestUnit to defaultLargestUnit.
largest_unit = MUST(String::from_utf8(default_largest_unit)); largest_unit = default_largest_unit;
} }
// 15. If smallestUnitPresent is false and largestUnitPresent is false, then // 21. If smallestUnitPresent is false and largestUnitPresent is false, then
if (!smallest_unit_present && !largest_unit_present) { if (!smallest_unit_present && !largest_unit_present) {
// a. Throw a RangeError exception. // a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalMissingUnits); return vm.throw_completion<RangeError>(ErrorType::TemporalMissingUnits);
} }
// 16. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception. auto largest_unit_value = largest_unit.get<Unit>();
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 17. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand"). // 22. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *round_to, "halfExpand"sv)); if (larger_of_two_temporal_units(largest_unit_value, smallest_unit_value) != largest_unit_value)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidUnitRange, temporal_unit_to_string(smallest_unit_value), temporal_unit_to_string(largest_unit_value));
// 18. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit). // 23. Let maximum be MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit); auto maximum = maximum_temporal_duration_rounding_increment(smallest_unit_value);
// 19. Let roundingIncrement be ? ToTemporalRoundingIncrement(options). // 24. If maximum is not UNSET, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(vm, *round_to)); if (!maximum.has<Unset>())
TRY(validate_temporal_rounding_increment(vm, rounding_increment, maximum.get<u64>(), false));
// 20. If maximum is not undefined, perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false). // 25. If roundingIncrement > 1, and largestUnit is not smallestUnit, and TemporalUnitCategory(smallestUnit) is DATE,
if (maximum.has_value()) { // throw a RangeError exception.
TRY(validate_temporal_rounding_increment(vm, rounding_increment, *maximum, false)); if (rounding_increment > 1 && largest_unit_value != smallest_unit_value && temporal_unit_category(smallest_unit_value) == UnitCategory::Date)
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, rounding_increment, "roundingIncrement");
// 26. If zonedRelativeTo is not undefined, then
if (zoned_relative_to) {
// a. Let internalDuration be ToInternalDurationRecord(duration).
auto internal_duration = to_internal_duration_record(vm, duration);
// FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]].
// FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]].
// FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
// FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain).
// FIXME: f. Set internalDuration to ? DifferenceZonedDateTimeWithRounding(relativeEpochNs, targetEpochNs, timeZone, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
// g. If TemporalUnitCategory(largestUnit) is date, set largestUnit to hour.
if (temporal_unit_category(largest_unit_value) == UnitCategory::Date)
largest_unit_value = Unit::Hour;
// h. Return ? TemporalDurationFromInternal(internalDuration, largestUnit).
return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value));
} }
// 21. Let relativeTo be ? ToRelativeTemporalObject(roundTo). // 27. If plainRelativeTo is not undefined, then
auto relative_to = TRY(to_relative_temporal_object(vm, *round_to)); if (plain_relative_to) {
auto relative_to_value = relative_to_converted_to_value(relative_to); // a. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
// 22. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], largestUnit, relativeTo). // FIXME: b. Let targetTime be AddTime(MidnightTimeRecord(), internalDuration.[[Time]]).
auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *largest_unit, relative_to_value)); // FIXME: c. Let calendar be plainRelativeTo.[[Calendar]].
// FIXME: d. Let dateDuration be ! AdjustDateDurationRecord(internalDuration.[[Date]], targetTime.[[Days]]).
// FIXME: e. Let targetDate be ? CalendarDateAdd(calendar, plainRelativeTo.[[ISODate]], dateDuration, constrain).
// FIXME: f. Let isoDateTime be CombineISODateAndTimeRecord(plainRelativeTo.[[ISODate]], MidnightTimeRecord()).
// FIXME: g. Let targetDateTime be CombineISODateAndTimeRecord(targetDate, targetTime).
// FIXME: h. Set internalDuration to ? DifferencePlainDateTimeWithRounding(isoDateTime, targetDateTime, calendar, largestUnit, roundingIncrement, smallestUnit, roundingMode).
auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, relative_to.plain_relative_to, relative_to.zoned_relative_to, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } })); // i. Return ? TemporalDurationFromInternal(internalDuration, largestUnit).
// 23. Let roundResult be (? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]]. return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value));
auto round_result = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr, calendar_record)).duration_record;
// 24. Let adjustResult be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo).
auto adjust_result = TRY(adjust_rounded_duration_days(vm, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr));
// 25. Let balanceResult be ? BalanceDurationRelative(adjustResult.[[Years]], adjustResult.[[Months]], adjustResult.[[Weeks]], adjustResult.[[Days]], largestUnit, relativeTo).
auto balance_result = TRY(balance_duration_relative(vm, adjust_result.years, adjust_result.months, adjust_result.weeks, adjust_result.days, *largest_unit, relative_to_value));
// 26. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (relative_to.zoned_relative_to) {
// a. Set relativeTo to ? MoveRelativeZonedDateTime(relativeTo, balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], 0).
relative_to_value = TRY(move_relative_zoned_date_time(vm, *relative_to.zoned_relative_to, balance_result.years, balance_result.months, balance_result.weeks, 0));
} }
// 27. Let result be ? BalanceDuration(balanceResult.[[Days]], adjustResult.[[Hours]], adjustResult.[[Minutes]], adjustResult.[[Seconds]], adjustResult.[[Milliseconds]], adjustResult.[[Microseconds]], adjustResult.[[Nanoseconds]], largestUnit, relativeTo). // 28. If IsCalendarUnit(existingLargestUnit) is true, or IsCalendarUnit(largestUnit) is true, throw a RangeError exception.
auto result = TRY(balance_duration(vm, balance_result.days, adjust_result.hours, adjust_result.minutes, adjust_result.seconds, adjust_result.milliseconds, adjust_result.microseconds, Crypto::SignedBigInteger { adjust_result.nanoseconds }, *largest_unit, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr)); if (is_calendar_unit(existing_largest_unit))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(existing_largest_unit));
if (is_calendar_unit(largest_unit_value))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(largest_unit_value));
// 28. Return ! CreateTemporalDuration(balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). // 29. Assert: IsCalendarUnit(smallestUnit) is false.
return MUST(create_temporal_duration(vm, balance_result.years, balance_result.months, balance_result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); VERIFY(!is_calendar_unit(smallest_unit_value));
// 30. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
// 31. If smallestUnit is DAY, then
if (smallest_unit_value == Unit::Day) {
// a. Let fractionalDays be TotalTimeDuration(internalDuration.[[Time]], DAY).
auto fractional_days = total_time_duration(internal_duration.time, Unit::Day);
// b. Let days be RoundNumberToIncrement(fractionalDays, roundingIncrement, roundingMode).
auto days = round_number_to_increment(fractional_days, rounding_increment, rounding_mode);
// c. Let dateDuration be ? CreateDateDurationRecord(0, 0, 0, days).
auto date_duration = TRY(create_date_duration_record(vm, 0, 0, 0, days));
// d. Set internalDuration to ! CombineDateAndTimeDuration(dateDuration, 0).
internal_duration = MUST(combine_date_and_time_duration(vm, date_duration, TimeDuration { 0 }));
}
// 32. Else,
else {
// a. Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], roundingIncrement, smallestUnit, roundingMode).
auto time_duration = TRY(round_time_duration(vm, internal_duration.time, Crypto::UnsignedBigInteger { rounding_increment }, smallest_unit_value, rounding_mode));
// b. Set internalDuration to ! CombineDateAndTimeDuration(ZeroDateDuration(), timeDuration).
internal_duration = MUST(combine_date_and_time_duration(vm, zero_date_duration(vm), move(time_duration)));
}
// 33. Return ? TemporalDurationFromInternal(internalDuration, largestUnit).
return TRY(temporal_duration_from_internal(vm, internal_duration, largest_unit_value));
} }
// 7.3.21 Temporal.Duration.prototype.total ( totalOf ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total // 7.3.21 Temporal.Duration.prototype.total ( totalOf ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.total
// FIXME: This is well out of date with the spec.
JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total) JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
auto total_of_value = vm.argument(0);
// 1. Let duration be the this value. // 1. Let duration be the this value.
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. If totalOf is undefined, throw a TypeError exception. // 3. If totalOf is undefined, throw a TypeError exception.
if (vm.argument(0).is_undefined()) if (total_of_value.is_undefined())
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject); return vm.throw_completion<TypeError>(ErrorType::IsUndefined, "totalOf"sv);
Object* total_of; GC::Ptr<Object> total_of;
// 4. If Type(totalOf) is String, then // 4. If totalOf is a String, then
if (vm.argument(0).is_string()) { if (total_of_value.is_string()) {
// a. Let paramString be totalOf. // a. Let paramString be totalOf.
auto param_string = total_of_value;
// b. Set totalOf to OrdinaryObjectCreate(null). // b. Set totalOf to OrdinaryObjectCreate(null).
total_of = Object::create(realm, nullptr); total_of = Object::create(realm, nullptr);
// c. Perform ! CreateDataPropertyOrThrow(totalOf, "unit", paramString). // c. Perform ! CreateDataPropertyOrThrow(totalOf, "unit", paramString).
MUST(total_of->create_data_property_or_throw(vm.names.unit, vm.argument(0))); MUST(total_of->create_data_property_or_throw(vm.names.unit, param_string));
} }
// 5. Else, // 5. Else,
else { else {
// a. Set totalOf to ? GetOptionsObject(totalOf). // a. Set totalOf to ? GetOptionsObject(totalOf).
total_of = TRY(get_options_object(vm, vm.argument(0))); total_of = TRY(get_options_object(vm, total_of_value));
} }
// 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf). // 6. NOTE: The following steps read options and perform independent validation in alphabetical order
auto relative_to = TRY(to_relative_temporal_object(vm, *total_of)); // (GetTemporalRelativeToOption reads "relativeTo").
auto relative_to_value = relative_to_converted_to_value(relative_to);
// 7. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required). // 7. Let relativeToRecord be ? GetTemporalRelativeToOption(totalOf).
auto unit = TRY(get_temporal_unit(vm, *total_of, vm.names.unit, UnitGroup::DateTime, TemporalUnitRequired {})); // 8. Let zonedRelativeTo be relativeToRecord.[[ZonedRelativeTo]].
// 9. Let plainRelativeTo be relativeToRecord.[[PlainRelativeTo]].
auto [zoned_relative_to, plain_relative_to] = TRY(get_temporal_relative_to_option(vm, *total_of));
// 8. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], unit, relativeTo). // 10. Let unit be ? GetTemporalUnitValuedOption(totalOf, "unit", DATETIME, REQUIRED).
auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *unit, relative_to_value)); auto unit = TRY(get_temporal_unit_valued_option(vm, *total_of, vm.names.unit, UnitGroup::DateTime, Required {})).get<Unit>();
// 9. Let intermediate be undefined. double total = 0;
ZonedDateTime* intermediate = nullptr;
// 10. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then // 11. If zonedRelativeTo is not undefined, then
if (relative_to.zoned_relative_to) { if (zoned_relative_to) {
// a. Set intermediate to ? MoveRelativeZonedDateTime(relativeTo, unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], 0). // FIXME: a. Let internalDuration be ToInternalDurationRecord(duration).
intermediate = TRY(move_relative_zoned_date_time(vm, *relative_to.zoned_relative_to, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, 0)); // FIXME: b. Let timeZone be zonedRelativeTo.[[TimeZone]].
// FIXME: c. Let calendar be zonedRelativeTo.[[Calendar]].
// FIXME: d. Let relativeEpochNs be zonedRelativeTo.[[EpochNanoseconds]].
// FIXME: e. Let targetEpochNs be ? AddZonedDateTime(relativeEpochNs, timeZone, calendar, internalDuration, constrain).
// FIXME: f. Let total be ? DifferenceZonedDateTimeWithTotal(relativeEpochNs, targetEpochNs, timeZone, calendar, unit).
}
// 12. Else if plainRelativeTo is not undefined, then
else if (plain_relative_to) {
// FIXME: a. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
// FIXME: b. Let targetTime be AddTime(MidnightTimeRecord(), internalDuration.[[Time]]).
// FIXME: c. Let calendar be plainRelativeTo.[[Calendar]].
// FIXME: d. Let dateDuration be ! AdjustDateDurationRecord(internalDuration.[[Date]], targetTime.[[Days]]).
// FIXME: e. Let targetDate be ? CalendarDateAdd(calendar, plainRelativeTo.[[ISODate]], dateDuration, constrain).
// FIXME: f. Let isoDateTime be CombineISODateAndTimeRecord(plainRelativeTo.[[ISODate]], MidnightTimeRecord()).
// FIXME: g. Let targetDateTime be CombineISODateAndTimeRecord(targetDate, targetTime).
// FIXME: h. Let total be ? DifferencePlainDateTimeWithTotal(isoDateTime, targetDateTime, calendar, unit).
}
// 13. Else,
else {
// a. Let largestUnit be DefaultTemporalLargestUnit(duration).
auto largest_unit = default_temporal_largest_unit(duration);
// b. If IsCalendarUnit(largestUnit) is true, or IsCalendarUnit(unit) is true, throw a RangeError exception.
if (is_calendar_unit(largest_unit))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(largest_unit));
if (is_calendar_unit(unit))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidLargestUnit, temporal_unit_to_string(unit));
// c. Let internalDuration be ToInternalDurationRecordWith24HourDays(duration).
auto internal_duration = to_internal_duration_record_with_24_hour_days(vm, duration);
// d. Let total be TotalTimeDuration(internalDuration.[[Time]], unit).
total = total_time_duration(internal_duration.time, unit);
} }
// 11. Let balanceResult be ? BalanceDuration(unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], unit, intermediate). // 14. Return 𝔽(total).
auto balance_result = TRY(balance_duration(vm, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), Crypto::SignedBigInteger { duration->nanoseconds() }, *unit, intermediate)); return total;
// 12. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]], 1, unit, "trunc", relativeTo).
auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, relative_to.plain_relative_to, relative_to.zoned_relative_to, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
auto round_record = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds, 1, *unit, "trunc"sv, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr, calendar_record));
// 13. Return 𝔽(roundRecord.[[Total]]).
return Value(round_record.total);
} }
// 7.3.22 Temporal.Duration.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tostring // 7.3.22 Temporal.Duration.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tostring
@ -521,24 +509,55 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::to_string)
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options). // 3. Let resolvedOptions be ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(0))); auto resolved_options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let precision be ? ToSecondsStringPrecisionRecord(options). // 4. NOTE: The following steps read options and perform independent validation in alphabetical order
auto precision = TRY(to_seconds_string_precision_record(vm, *options)); // (GetTemporalFractionalSecondDigitsOption reads "fractionalSecondDigits" and GetRoundingModeOption reads
// "roundingMode").
// 5. If precision.[[Unit]] is "minute", throw a RangeError exception. // 5. Let digits be ? GetTemporalFractionalSecondDigitsOption(resolvedOptions).
if (precision.unit == "minute"sv) auto digits = TRY(get_temporal_fractional_second_digits_option(vm, resolved_options));
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, "minute"sv, "smallestUnit"sv);
// 6. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). // 6. Let roundingMode be ? GetRoundingModeOption(resolvedOptions, TRUNC).
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *options, "trunc"sv)); auto rounding_mode = TRY(get_rounding_mode_option(vm, resolved_options, RoundingMode::Trunc));
// 7. Let result be (? RoundDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode)).[[DurationRecord]]. // 7. Let smallestUnit be ? GetTemporalUnitValuedOption(resolvedOptions, "smallestUnit", TIME, UNSET).
auto result = TRY(round_duration(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), precision.increment, precision.unit, rounding_mode)).duration_record; auto smallest_unit = TRY(get_temporal_unit_valued_option(vm, resolved_options, vm.names.smallestUnit, UnitGroup::Time, Unset {}));
// 8. Return ! TemporalDurationToString(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], precision.[[Precision]]). // 8. If smallestUnit is HOUR or MINUTE, throw a RangeError exception.
return PrimitiveString::create(vm, MUST(temporal_duration_to_string(vm, result.years, result.months, result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, precision.precision))); if (auto const* unit = smallest_unit.get_pointer<Unit>(); unit && (*unit == Unit::Hour || *unit == Unit::Minute))
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, temporal_unit_to_string(*unit), vm.names.smallestUnit);
// 9. Let precision be ToSecondsStringPrecisionRecord(smallestUnit, digits).
auto precision = to_seconds_string_precision_record(smallest_unit, digits);
// 10. If precision.[[Unit]] is NANOSECOND and precision.[[Increment]] = 1, then
if (precision.unit == Unit::Nanosecond && precision.increment == 1) {
// a. Return TemporalDurationToString(duration, precision.[[Precision]]).
return PrimitiveString::create(vm, temporal_duration_to_string(duration, precision.precision.downcast<Auto, u8>()));
}
// 11. Let largestUnit be DefaultTemporalLargestUnit(duration).
auto largest_unit = default_temporal_largest_unit(duration);
// 12. Let internalDuration be ToInternalDurationRecord(duration).
auto internal_duration = to_internal_duration_record(vm, duration);
// 13. Let timeDuration be ? RoundTimeDuration(internalDuration.[[Time]], precision.[[Increment]], precision.[[Unit]], roundingMode).
auto time_duration = TRY(round_time_duration(vm, internal_duration.time, precision.increment, precision.unit, rounding_mode));
// 14. Set internalDuration to ! CombineDateAndTimeDuration(internalDuration.[[Date]], timeDuration).
internal_duration = MUST(combine_date_and_time_duration(vm, internal_duration.date, move(time_duration)));
// 15. Let roundedLargestUnit be LargerOfTwoTemporalUnits(largestUnit, SECOND).
auto rounded_largest_unit = larger_of_two_temporal_units(largest_unit, Unit::Second);
// 16. Let roundedDuration be ? TemporalDurationFromInternal(internalDuration, roundedLargestUnit).
auto rounded_duration = TRY(temporal_duration_from_internal(vm, internal_duration, rounded_largest_unit));
// 17. Return TemporalDurationToString(roundedDuration, precision.[[Precision]]).
return PrimitiveString::create(vm, temporal_duration_to_string(rounded_duration, precision.precision.downcast<Auto, u8>()));
} }
// 7.3.23 Temporal.Duration.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tojson // 7.3.23 Temporal.Duration.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tojson
@ -548,8 +567,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::to_json)
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return ! TemporalDurationToString(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "auto"). // 3. Return TemporalDurationToString(duration, AUTO).
return PrimitiveString::create(vm, MUST(temporal_duration_to_string(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), "auto"sv))); return PrimitiveString::create(vm, temporal_duration_to_string(duration, Auto {}));
} }
// 7.3.24 Temporal.Duration.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tolocalestring // 7.3.24 Temporal.Duration.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.tolocalestring
@ -560,8 +579,8 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::to_locale_string)
// 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). // 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]).
auto duration = TRY(typed_this_object(vm)); auto duration = TRY(typed_this_object(vm));
// 3. Return ! TemporalDurationToString(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "auto"). // 3. Return TemporalDurationToString(duration, AUTO).
return PrimitiveString::create(vm, MUST(temporal_duration_to_string(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), "auto"sv))); return PrimitiveString::create(vm, temporal_duration_to_string(duration, Auto {}));
} }
// 7.3.25 Temporal.Duration.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.valueof // 7.3.25 Temporal.Duration.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.duration.prototype.valueof

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
@ -22,16 +23,11 @@ public:
private: private:
explicit DurationPrototype(Realm&); explicit DurationPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(years_getter); #define __JS_ENUMERATE(unit) \
JS_DECLARE_NATIVE_FUNCTION(months_getter); JS_DECLARE_NATIVE_FUNCTION(unit##_getter);
JS_DECLARE_NATIVE_FUNCTION(weeks_getter); JS_ENUMERATE_DURATION_UNITS
JS_DECLARE_NATIVE_FUNCTION(days_getter); #undef __JS_ENUMERATE
JS_DECLARE_NATIVE_FUNCTION(hours_getter);
JS_DECLARE_NATIVE_FUNCTION(minutes_getter);
JS_DECLARE_NATIVE_FUNCTION(seconds_getter);
JS_DECLARE_NATIVE_FUNCTION(milliseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(microseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(nanoseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(sign_getter); JS_DECLARE_NATIVE_FUNCTION(sign_getter);
JS_DECLARE_NATIVE_FUNCTION(blank_getter); JS_DECLARE_NATIVE_FUNCTION(blank_getter);
JS_DECLARE_NATIVE_FUNCTION(with); JS_DECLARE_NATIVE_FUNCTION(with);

File diff suppressed because it is too large Load diff

View file

@ -1,225 +1,35 @@
/* /*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/GenericLexer.h>
#include <AK/Optional.h> #include <AK/Optional.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <AK/Vector.h>
namespace JS::Temporal { namespace JS::Temporal {
struct Annotation {
bool critical { false };
StringView key;
StringView value;
};
struct ParseResult { struct ParseResult {
Optional<StringView> sign; Optional<char> sign;
Optional<StringView> date_year;
Optional<StringView> date_month;
Optional<StringView> date_day;
Optional<StringView> time_hour;
Optional<StringView> time_minute;
Optional<StringView> time_second;
Optional<StringView> time_fraction;
Optional<StringView> utc_designator;
Optional<StringView> time_zone_annotation;
Optional<StringView> time_zone_numeric_utc_offset;
Optional<StringView> time_zone_utc_offset_sign;
Optional<StringView> time_zone_utc_offset_hour;
Optional<StringView> time_zone_utc_offset_minute;
Optional<StringView> time_zone_utc_offset_second;
Optional<StringView> time_zone_utc_offset_fraction;
Optional<StringView> time_zone_identifier;
Optional<StringView> duration_years; Optional<StringView> duration_years;
Optional<StringView> duration_months; Optional<StringView> duration_months;
Optional<StringView> duration_weeks; Optional<StringView> duration_weeks;
Optional<StringView> duration_days; Optional<StringView> duration_days;
Optional<StringView> duration_whole_hours; Optional<StringView> duration_hours;
Optional<StringView> duration_hours_fraction; Optional<StringView> duration_hours_fraction;
Optional<StringView> duration_whole_minutes; Optional<StringView> duration_minutes;
Optional<StringView> duration_minutes_fraction; Optional<StringView> duration_minutes_fraction;
Optional<StringView> duration_whole_seconds; Optional<StringView> duration_seconds;
Optional<StringView> duration_seconds_fraction; Optional<StringView> duration_seconds_fraction;
Optional<StringView> annotation_key;
Optional<StringView> annotation_value;
Vector<Annotation> annotations;
}; };
enum class Production { enum class Production {
TemporalInstantString,
TemporalDateTimeString,
TemporalDurationString, TemporalDurationString,
TemporalMonthDayString,
TemporalTimeString,
TemporalYearMonthString,
TemporalZonedDateTimeString,
TimeZoneIdentifier,
TimeZoneNumericUTCOffset,
AnnotationValue,
DateMonth,
}; };
Optional<ParseResult> parse_iso8601(Production, StringView); Optional<ParseResult> parse_iso8601(Production, StringView);
namespace Detail {
// 13.33 ISO 8601 grammar, https://tc39.es/proposal-temporal/#sec-temporal-iso8601grammar
class ISO8601Parser {
public:
explicit ISO8601Parser(StringView input)
: m_input(input)
, m_state({
.lexer = GenericLexer { input },
.parse_result = {},
})
{
}
[[nodiscard]] GenericLexer const& lexer() const { return m_state.lexer; }
[[nodiscard]] ParseResult const& parse_result() const { return m_state.parse_result; }
[[nodiscard]] bool parse_decimal_digits();
[[nodiscard]] bool parse_decimal_digit();
[[nodiscard]] bool parse_non_zero_digit();
[[nodiscard]] bool parse_ascii_sign();
[[nodiscard]] bool parse_sign();
[[nodiscard]] bool parse_unpadded_hour();
[[nodiscard]] bool parse_hour();
[[nodiscard]] bool parse_minute_second();
[[nodiscard]] bool parse_decimal_separator();
[[nodiscard]] bool parse_days_designator();
[[nodiscard]] bool parse_hours_designator();
[[nodiscard]] bool parse_minutes_designator();
[[nodiscard]] bool parse_months_designator();
[[nodiscard]] bool parse_duration_designator();
[[nodiscard]] bool parse_seconds_designator();
[[nodiscard]] bool parse_date_time_separator();
[[nodiscard]] bool parse_time_designator();
[[nodiscard]] bool parse_weeks_designator();
[[nodiscard]] bool parse_years_designator();
[[nodiscard]] bool parse_utc_designator();
[[nodiscard]] bool parse_annotation_critical_flag();
[[nodiscard]] bool parse_date_year();
[[nodiscard]] bool parse_date_month();
[[nodiscard]] bool parse_date_month_with_thirty_days();
[[nodiscard]] bool parse_date_day();
[[nodiscard]] bool parse_date_spec_year_month();
[[nodiscard]] bool parse_date_spec_month_day();
[[nodiscard]] bool parse_valid_month_day();
[[nodiscard]] bool parse_date();
[[nodiscard]] bool parse_time_hour();
[[nodiscard]] bool parse_time_minute();
[[nodiscard]] bool parse_time_second();
[[nodiscard]] bool parse_fractional_part();
[[nodiscard]] bool parse_fraction();
[[nodiscard]] bool parse_time_fraction();
[[nodiscard]] bool parse_time_zone_utc_offset_sign();
[[nodiscard]] bool parse_time_zone_utc_offset_hour();
[[nodiscard]] bool parse_time_zone_utc_offset_minute();
[[nodiscard]] bool parse_time_zone_utc_offset_second();
[[nodiscard]] bool parse_time_zone_utc_offset_fractional_part();
[[nodiscard]] bool parse_time_zone_utc_offset_fraction();
[[nodiscard]] bool parse_time_zone_numeric_utc_offset();
[[nodiscard]] bool parse_time_zone_utc_offset();
[[nodiscard]] bool parse_time_zone_utc_offset_name();
[[nodiscard]] bool parse_tz_leading_char();
[[nodiscard]] bool parse_tz_char();
[[nodiscard]] bool parse_time_zone_iana_component();
[[nodiscard]] bool parse_time_zone_iana_name_tail();
[[nodiscard]] bool parse_time_zone_iana_legacy_name();
[[nodiscard]] bool parse_time_zone_iana_name();
[[nodiscard]] bool parse_time_zone_identifier();
[[nodiscard]] bool parse_time_zone_annotation();
[[nodiscard]] bool parse_a_key_leading_char();
[[nodiscard]] bool parse_a_key_char();
[[nodiscard]] bool parse_a_val_char();
[[nodiscard]] bool parse_annotation_key_tail();
[[nodiscard]] bool parse_annotation_key();
[[nodiscard]] bool parse_annotation_value_component();
[[nodiscard]] bool parse_annotation_value_tail();
[[nodiscard]] bool parse_annotation_value();
[[nodiscard]] bool parse_annotation();
[[nodiscard]] bool parse_annotations();
[[nodiscard]] bool parse_time_spec();
[[nodiscard]] bool parse_time_spec_with_optional_offset_not_ambiguous();
[[nodiscard]] bool parse_date_time();
[[nodiscard]] bool parse_annotated_time();
[[nodiscard]] bool parse_annotated_date_time();
[[nodiscard]] bool parse_annotated_date_time_time_required();
[[nodiscard]] bool parse_annotated_year_month();
[[nodiscard]] bool parse_annotated_month_day();
[[nodiscard]] bool parse_duration_whole_seconds();
[[nodiscard]] bool parse_duration_seconds_fraction();
[[nodiscard]] bool parse_duration_seconds_part();
[[nodiscard]] bool parse_duration_whole_minutes();
[[nodiscard]] bool parse_duration_minutes_fraction();
[[nodiscard]] bool parse_duration_minutes_part();
[[nodiscard]] bool parse_duration_whole_hours();
[[nodiscard]] bool parse_duration_hours_fraction();
[[nodiscard]] bool parse_duration_hours_part();
[[nodiscard]] bool parse_duration_time();
[[nodiscard]] bool parse_duration_days();
[[nodiscard]] bool parse_duration_days_part();
[[nodiscard]] bool parse_duration_weeks();
[[nodiscard]] bool parse_duration_weeks_part();
[[nodiscard]] bool parse_duration_months();
[[nodiscard]] bool parse_duration_months_part();
[[nodiscard]] bool parse_duration_years();
[[nodiscard]] bool parse_duration_years_part();
[[nodiscard]] bool parse_duration_date();
[[nodiscard]] bool parse_duration();
[[nodiscard]] bool parse_temporal_instant_string();
[[nodiscard]] bool parse_temporal_date_time_string();
[[nodiscard]] bool parse_temporal_duration_string();
[[nodiscard]] bool parse_temporal_month_day_string();
[[nodiscard]] bool parse_temporal_time_string();
[[nodiscard]] bool parse_temporal_year_month_string();
[[nodiscard]] bool parse_temporal_zoned_date_time_string();
private:
struct State {
GenericLexer lexer;
ParseResult parse_result;
};
struct StateTransaction {
explicit StateTransaction(ISO8601Parser& parser)
: m_parser(parser)
, m_saved_state(parser.m_state)
, m_start_index(parser.m_state.lexer.tell())
{
}
~StateTransaction()
{
if (!m_commit)
m_parser.m_state = move(m_saved_state);
}
void commit() { m_commit = true; }
StringView parsed_string_view() const
{
return m_parser.m_input.substring_view(m_start_index, m_parser.m_state.lexer.tell() - m_start_index);
}
private:
ISO8601Parser& m_parser;
State m_saved_state;
size_t m_start_index { 0 };
bool m_commit { false };
};
StringView m_input;
State m_state;
};
}
} }

View file

@ -2,369 +2,36 @@
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org> * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/TypeCasts.h>
#include <AK/Variant.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h> #include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal { namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(Instant); // nsMaxInstant = 10**8 × nsPerDay = 8.64 × 10**21
Crypto::SignedBigInteger const NANOSECONDS_MAX_INSTANT = "8640000000000000000000"_sbigint;
// 8 Temporal.Instant Objects, https://tc39.es/proposal-temporal/#sec-temporal-instant-objects
Instant::Instant(BigInt const& nanoseconds, Object& prototype) // nsMinInstant = -nsMaxInstant = -8.64 × 10**21
: Object(ConstructWithPrototypeTag::Tag, prototype) Crypto::SignedBigInteger const NANOSECONDS_MIN_INSTANT = "-8640000000000000000000"_sbigint;
, m_nanoseconds(nanoseconds)
{ // nsPerDay = 10**6 × (msPerDay) = 8.64 × 10**13
} Crypto::UnsignedBigInteger const NANOSECONDS_PER_DAY = 86400000000000_bigint;
void Instant::visit_edges(Cell::Visitor& visitor) // Non-standard:
{ Crypto::UnsignedBigInteger const NANOSECONDS_PER_HOUR = 3600000000000_bigint;
Base::visit_edges(visitor); Crypto::UnsignedBigInteger const NANOSECONDS_PER_MINUTE = 60000000000_bigint;
Crypto::UnsignedBigInteger const NANOSECONDS_PER_SECOND = 1000000000_bigint;
visitor.visit(m_nanoseconds); Crypto::UnsignedBigInteger const NANOSECONDS_PER_MILLISECOND = 1000000_bigint;
} Crypto::UnsignedBigInteger const NANOSECONDS_PER_MICROSECOND = 1000_bigint;
Crypto::UnsignedBigInteger const NANOSECONDS_PER_NANOSECOND = 1_bigint;
// 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds) Crypto::UnsignedBigInteger const MICROSECONDS_PER_MILLISECOND = 1000_bigint;
{ Crypto::UnsignedBigInteger const MILLISECONDS_PER_SECOND = 1000_bigint;
return is_valid_epoch_nanoseconds(epoch_nanoseconds.big_integer()); Crypto::UnsignedBigInteger const SECONDS_PER_MINUTE = 60_bigint;
} Crypto::UnsignedBigInteger const MINUTES_PER_HOUR = 60_bigint;
Crypto::UnsignedBigInteger const HOURS_PER_DAY = 24_bigint;
// 8.5.1 IsValidEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidepochnanoseconds
bool is_valid_epoch_nanoseconds(Crypto::SignedBigInteger const& epoch_nanoseconds)
{
// 1. Assert: Type(epochNanoseconds) is BigInt.
// 2. If (epochNanoseconds) < nsMinInstant or (epochNanoseconds) > nsMaxInstant, then
if (epoch_nanoseconds < ns_min_instant || epoch_nanoseconds > ns_max_instant) {
// a. Return false.
return false;
}
// 3. Return true.
return true;
}
// 8.5.2 CreateTemporalInstant ( epochNanoseconds [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalinstant
ThrowCompletionOr<Instant*> create_temporal_instant(VM& vm, BigInt const& epoch_nanoseconds, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: Type(epochNanoseconds) is BigInt.
// 2. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds));
// 3. If newTarget is not present, set newTarget to %Temporal.Instant%.
if (!new_target)
new_target = realm.intrinsics().temporal_instant_constructor();
// 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.Instant.prototype%", « [[InitializedTemporalInstant]], [[Nanoseconds]] »).
// 5. Set object.[[Nanoseconds]] to epochNanoseconds.
auto object = TRY(ordinary_create_from_constructor<Instant>(vm, *new_target, &Intrinsics::temporal_instant_prototype, epoch_nanoseconds));
// 6. Return object.
return object.ptr();
}
// 8.5.3 ToTemporalInstant ( item ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalinstant
ThrowCompletionOr<Instant*> to_temporal_instant(VM& vm, Value item)
{
// 1. If Type(item) is Object, then
if (item.is_object()) {
// a. If item has an [[InitializedTemporalInstant]] internal slot, then
if (is<Instant>(item.as_object())) {
// i. Return item.
return &static_cast<Instant&>(item.as_object());
}
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(item.as_object())) {
auto& zoned_date_time = static_cast<ZonedDateTime&>(item.as_object());
// i. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).
return create_temporal_instant(vm, zoned_date_time.nanoseconds());
}
}
// 2. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// 3. Let epochNanoseconds be ? ParseTemporalInstant(string).
auto* epoch_nanoseconds = TRY(parse_temporal_instant(vm, string));
// 4. Return ! CreateTemporalInstant((epochNanoseconds)).
return create_temporal_instant(vm, *epoch_nanoseconds);
}
// 8.5.4 ParseTemporalInstant ( isoString ), https://tc39.es/proposal-temporal/#sec-temporal-parsetemporalinstant
ThrowCompletionOr<BigInt*> parse_temporal_instant(VM& vm, StringView iso_string)
{
// 1. Assert: Type(isoString) is String.
// 2. Let result be ? ParseTemporalInstantString(isoString).
auto result = TRY(parse_temporal_instant_string(vm, iso_string));
// 3. Let offsetString be result.[[TimeZoneOffsetString]].
auto& offset_string = result.time_zone_offset;
// 4. Assert: offsetString is not undefined.
VERIFY(offset_string.has_value());
// 5. Let utc be GetUTCEpochNanoseconds(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
auto utc = get_utc_epoch_nanoseconds(result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond);
// 6. If IsTimeZoneOffsetString(offsetString) is false, throw a RangeError exception.
if (!is_time_zone_offset_string(*offset_string))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, *offset_string);
// 7. Let offsetNanoseconds be ParseTimeZoneOffsetString(offsetString).
auto offset_nanoseconds = parse_time_zone_offset_string(*offset_string);
// 7. Let result be utc - (offsetNanoseconds).
auto result_ns = utc.minus(Crypto::SignedBigInteger { offset_nanoseconds });
// 8. If ! IsValidEpochNanoseconds(result) is false, then
if (!is_valid_epoch_nanoseconds(result_ns)) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
}
// 9. Return result.
return BigInt::create(vm, move(result_ns)).ptr();
}
// 8.5.5 CompareEpochNanoseconds ( epochNanosecondsOne, epochNanosecondsTwo ), https://tc39.es/proposal-temporal/#sec-temporal-compareepochnanoseconds
i32 compare_epoch_nanoseconds(BigInt const& epoch_nanoseconds_one, BigInt const& epoch_nanoseconds_two)
{
// 1. If epochNanosecondsOne > epochNanosecondsTwo, return 1.
if (epoch_nanoseconds_one.big_integer() > epoch_nanoseconds_two.big_integer())
return 1;
// 2. If epochNanosecondsOne < epochNanosecondsTwo, return -1.
if (epoch_nanoseconds_one.big_integer() < epoch_nanoseconds_two.big_integer())
return -1;
// 3. Return 0.
return 0;
}
// 8.5.6 AddInstant ( epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addinstant
ThrowCompletionOr<BigInt*> add_instant(VM& vm, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
{
VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
// 1. Let result be epochNanoseconds + (nanoseconds) + (microseconds) × 1000 + (milliseconds) × 10^6 + (seconds) × 10^9 + (minutes) × 60 × 10^9 + (hours) × 3600 × 10^9.
auto result = BigInt::create(vm,
epoch_nanoseconds.big_integer()
.plus(Crypto::SignedBigInteger { nanoseconds })
.plus(Crypto::SignedBigInteger { microseconds }.multiplied_by(Crypto::SignedBigInteger { 1'000 }))
.plus(Crypto::SignedBigInteger { milliseconds }.multiplied_by(Crypto::SignedBigInteger { 1'000'000 }))
.plus(Crypto::SignedBigInteger { seconds }.multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
.plus(Crypto::SignedBigInteger { minutes }.multiplied_by(Crypto::SignedBigInteger { 60 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 }))
.plus(Crypto::SignedBigInteger { hours }.multiplied_by(Crypto::SignedBigInteger { 3600 }).multiplied_by(Crypto::SignedBigInteger { 1'000'000'000 })));
// 2. If ! IsValidEpochNanoseconds(result) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(*result))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 3. Return result.
return result.ptr();
}
// 8.5.6 DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, largestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
TimeDurationRecord difference_instant(VM& vm, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView largest_unit, StringView rounding_mode)
{
static Crypto::UnsignedBigInteger const BIGINT_ONE_THOUSAND { 1'000 };
// 1. Let difference be (ns2) - (ns1).
auto difference = nanoseconds2.big_integer().minus(nanoseconds1.big_integer());
// 2. Let nanoseconds be remainder(difference, 1000).
auto nanoseconds = remainder(difference, BIGINT_ONE_THOUSAND);
// 3. Let microseconds be remainder(truncate(difference / 1000), 1000).
auto microseconds = remainder(difference.divided_by(BIGINT_ONE_THOUSAND).quotient, BIGINT_ONE_THOUSAND);
// 4. Let milliseconds be remainder(truncate(difference / 10^6), 1000).
auto milliseconds = remainder(difference.divided_by(Crypto::UnsignedBigInteger { 1'000'000 }).quotient, BIGINT_ONE_THOUSAND);
// 5. Let seconds be truncate(difference / 10^9).
auto seconds = difference.divided_by(Crypto::UnsignedBigInteger { 1'000'000'000 }).quotient;
// 6. If smallestUnit is "nanosecond" and roundingIncrement is 1, then
if (smallest_unit == "nanosecond"sv && rounding_increment == 1) {
// a. Return ! BalanceTimeDuration(0, 0, 0, seconds, milliseconds, microseconds, nanoseconds, largestUnit).
return MUST(balance_time_duration(vm, 0, 0, 0, seconds.to_double(), milliseconds.to_double(), microseconds.to_double(), nanoseconds, largest_unit));
}
// 7. Let roundResult be ! RoundDuration(0, 0, 0, 0, 0, 0, seconds, milliseconds, microseconds, nanoseconds, roundingIncrement, smallestUnit, roundingMode).
auto round_result = MUST(round_duration(vm, 0, 0, 0, 0, 0, 0, seconds.to_double(), milliseconds.to_double(), microseconds.to_double(), nanoseconds.to_double(), rounding_increment, smallest_unit, rounding_mode)).duration_record;
// 8. Assert: roundResult.[[Days]] is 0.
VERIFY(round_result.days == 0);
// 9. Return ! BalanceTimeDuration(0, roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
return MUST(balance_time_duration(vm, 0, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger { round_result.nanoseconds }, largest_unit));
}
// 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
BigInt* round_temporal_instant(VM& vm, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode)
{
// 1. Assert: Type(ns) is BigInt.
u64 increment_nanoseconds;
// 2. If unit is "hour", then
if (unit == "hour"sv) {
// a. Let incrementNs be increment × 3.6 × 10^12.
increment_nanoseconds = increment * 3600000000000;
}
// 3. Else if unit is "minute", then
else if (unit == "minute"sv) {
// a. Let incrementNs be increment × 6 × 10^10.
increment_nanoseconds = increment * 60000000000;
}
// 4. Else if unit is "second", then
else if (unit == "second"sv) {
// a. Let incrementNs be increment × 10^9.
increment_nanoseconds = increment * 1000000000;
}
// 5. Else if unit is "millisecond", then
else if (unit == "millisecond"sv) {
// a. Let incrementNs be increment × 10^6.
increment_nanoseconds = increment * 1000000;
}
// 6. Else if unit is "microsecond", then
else if (unit == "microsecond"sv) {
// a. Let incrementNs be increment × 10^3.
increment_nanoseconds = increment * 1000;
}
// 7. Else,
else {
// a. Assert: unit is "nanosecond".
VERIFY(unit == "nanosecond"sv);
// b. Let incrementNs be increment.
increment_nanoseconds = increment;
}
// 8. Return RoundNumberToIncrementAsIfPositive((ns), incrementNs, roundingMode).
return BigInt::create(vm, round_number_to_increment_as_if_positive(nanoseconds.big_integer(), increment_nanoseconds, rounding_mode));
}
// 8.5.9 TemporalInstantToString ( instant, timeZone, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporalinstanttostring
ThrowCompletionOr<String> temporal_instant_to_string(VM& vm, Instant& instant, Value time_zone, Variant<StringView, u8> const& precision)
{
// 1. Assert: Type(instant) is Object.
// 2. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
// 3. Let outputTimeZone be timeZone.
auto output_time_zone = time_zone;
// 4. If outputTimeZone is undefined, then
if (output_time_zone.is_undefined()) {
// a. Set outputTimeZone to ! CreateTemporalTimeZone("UTC").
output_time_zone = MUST_OR_THROW_OOM(create_temporal_time_zone(vm, "UTC"_string));
}
// 5. Let isoCalendar be ! GetISO8601Calendar().
auto* iso_calendar = get_iso8601_calendar(vm);
// 6. Let dateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(outputTimeZone, instant, isoCalendar).
auto* date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, output_time_zone, instant, *iso_calendar));
// 7. Let dateTimeString be ! TemporalDateTimeToString(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], undefined, precision, "never").
auto date_time_string = MUST_OR_THROW_OOM(temporal_date_time_to_string(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), nullptr, precision, "never"sv));
String time_zone_string;
// 8. If timeZone is undefined, then
if (time_zone.is_undefined()) {
// a. Let timeZoneString be "Z".
time_zone_string = "Z"_string;
}
// 9. Else,
else {
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
// b. Let timeZoneString be ! FormatISOTimeZoneOffsetString(offsetNs).
time_zone_string = MUST_OR_THROW_OOM(format_iso_time_zone_offset_string(vm, offset_ns));
}
// 10. Return the string-concatenation of dateTimeString and timeZoneString.
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}", date_time_string, time_zone_string));
}
// 8.5.9 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_instant(VM& vm, DifferenceOperation operation, Instant const& instant, Value other_value, Value options)
{
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalInstant(other).
auto* other = TRY(to_temporal_instant(vm, other_value));
// 3. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
auto resolved_options = TRY(TRY(get_options_object(vm, options))->snapshot_own_properties(vm, nullptr));
// 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, time, « », "nanosecond", "second").
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Time, {}, { "nanosecond"sv }, "second"sv));
// 5. Let result be DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[LargestUnit]], settings.[[RoundingMode]]).
auto result = difference_instant(vm, instant.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode);
// 6. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
// 8.5.11 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(VM& vm, ArithmeticOperation operation, Instant const& instant, Value temporal_duration_like)
{
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == ArithmeticOperation::Subtract ? -1 : 1;
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
auto duration = TRY(to_temporal_duration_record(vm, temporal_duration_like));
// 3. If duration.[[Days]] is not 0, throw a RangeError exception.
if (duration.days != 0)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "days", duration.days);
// 4. If duration.[[Months]] is not 0, throw a RangeError exception.
if (duration.months != 0)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "months", duration.months);
// 5. If duration.[[Weeks]] is not 0, throw a RangeError exception.
if (duration.weeks != 0)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "weeks", duration.weeks);
// 6. If duration.[[Years]] is not 0, throw a RangeError exception.
if (duration.years != 0)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDurationPropertyValueNonZero, "years", duration.years);
// 7. Let ns be ? AddInstant(instant.[[Nanoseconds]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
auto* ns = TRY(add_instant(vm, instant.nanoseconds(), sign * duration.hours, sign * duration.minutes, sign * duration.seconds, sign * duration.milliseconds, sign * duration.microseconds, sign * duration.nanoseconds));
// 8. Return ! CreateTemporalInstant(ns).
return MUST(create_temporal_instant(vm, *ns));
}
} }

View file

@ -1,58 +1,39 @@
/* /*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#pragma once #pragma once
#include <AK/Optional.h> #include <LibCrypto/BigInt/SignedBigInteger.h>
#include <AK/Variant.h> #include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
namespace JS::Temporal { namespace JS::Temporal {
class Instant final : public Object {
JS_OBJECT(Instant, Object);
GC_DECLARE_ALLOCATOR(Instant);
public:
virtual ~Instant() override = default;
[[nodiscard]] BigInt const& nanoseconds() const { return m_nanoseconds; }
private:
Instant(BigInt const& nanoseconds, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 8.4 Properties of Temporal.Instant Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-instant-instances
GC::Ref<BigInt const> m_nanoseconds; // [[Nanoseconds]]
};
// https://tc39.es/proposal-temporal/#eqn-nsMaxInstant // https://tc39.es/proposal-temporal/#eqn-nsMaxInstant
// nsMaxInstant = 10^8 × nsPerDay = 8.64 × 10^21 extern Crypto::SignedBigInteger const NANOSECONDS_MAX_INSTANT;
static auto const ns_max_instant = "8640000000000000000000"_sbigint;
// https://tc39.es/proposal-temporal/#eqn-nsMinInstant // https://tc39.es/proposal-temporal/#eqn-nsMinInstant
// nsMinInstant = -nsMaxInstant = -8.64 × 10^21 extern Crypto::SignedBigInteger const NANOSECONDS_MIN_INSTANT;
static auto const ns_min_instant = "-8640000000000000000000"_sbigint;
bool is_valid_epoch_nanoseconds(BigInt const& epoch_nanoseconds); // https://tc39.es/proposal-temporal/#eqn-nsPerDay
bool is_valid_epoch_nanoseconds(Crypto::SignedBigInteger const& epoch_nanoseconds); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_DAY;
ThrowCompletionOr<Instant*> create_temporal_instant(VM&, BigInt const& nanoseconds, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<Instant*> to_temporal_instant(VM&, Value item); // Non-standard:
ThrowCompletionOr<BigInt*> parse_temporal_instant(VM&, StringView iso_string); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_HOUR;
i32 compare_epoch_nanoseconds(BigInt const&, BigInt const&); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_MINUTE;
ThrowCompletionOr<BigInt*> add_instant(VM&, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_SECOND;
TimeDurationRecord difference_instant(VM&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView largest_unit, StringView rounding_mode); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_MILLISECOND;
BigInt* round_temporal_instant(VM&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_MICROSECOND;
ThrowCompletionOr<String> temporal_instant_to_string(VM&, Instant&, Value time_zone, Variant<StringView, u8> const& precision); extern Crypto::UnsignedBigInteger const NANOSECONDS_PER_NANOSECOND;
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_instant(VM&, DifferenceOperation, Instant const&, Value other, Value options);
ThrowCompletionOr<Instant*> add_duration_to_or_subtract_duration_from_instant(VM&, ArithmeticOperation, Instant const&, Value temporal_duration_like); extern Crypto::UnsignedBigInteger const MICROSECONDS_PER_MILLISECOND;
extern Crypto::UnsignedBigInteger const MILLISECONDS_PER_SECOND;
extern Crypto::UnsignedBigInteger const SECONDS_PER_MINUTE;
extern Crypto::UnsignedBigInteger const MINUTES_PER_HOUR;
extern Crypto::UnsignedBigInteger const HOURS_PER_DAY;
} }

View file

@ -1,169 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/ValueInlines.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(InstantConstructor);
// 8.1 The Temporal.Instant Constructor, https://tc39.es/proposal-temporal/#sec-temporal-instant-constructor
InstantConstructor::InstantConstructor(Realm& realm)
: NativeFunction(realm.vm().names.Instant.as_string(), realm.intrinsics().function_prototype())
{
}
void InstantConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 8.2.1 Temporal.Instant.prototype, https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_instant_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.from, from, 1, attr);
define_native_function(realm, vm.names.fromEpochSeconds, from_epoch_seconds, 1, attr);
define_native_function(realm, vm.names.fromEpochMilliseconds, from_epoch_milliseconds, 1, attr);
define_native_function(realm, vm.names.fromEpochMicroseconds, from_epoch_microseconds, 1, attr);
define_native_function(realm, vm.names.fromEpochNanoseconds, from_epoch_nanoseconds, 1, attr);
define_native_function(realm, vm.names.compare, compare, 2, attr);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}
// 8.1.1 Temporal.Instant ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant
ThrowCompletionOr<Value> InstantConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.Instant");
}
// 8.1.1 Temporal.Instant ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant
ThrowCompletionOr<GC::Ref<Object>> InstantConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Let epochNanoseconds be ? ToBigInt(epochNanoseconds).
auto epoch_nanoseconds = TRY(vm.argument(0).to_bigint(vm));
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 4. Return ? CreateTemporalInstant(epochNanoseconds, NewTarget).
return *TRY(create_temporal_instant(vm, epoch_nanoseconds, &new_target));
}
// 8.2.2 Temporal.Instant.from ( item ), https://tc39.es/proposal-temporal/#sec-temporal.instant.from
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from)
{
auto item = vm.argument(0);
// 1. If Type(item) is Object and item has an [[InitializedTemporalInstant]] internal slot, then
if (item.is_object() && is<Instant>(item.as_object())) {
// a. Return ! CreateTemporalInstant(item.[[Nanoseconds]]).
return MUST(create_temporal_instant(vm, BigInt::create(vm, static_cast<Instant&>(item.as_object()).nanoseconds().big_integer())));
}
// 2. Return ? ToTemporalInstant(item).
return TRY(to_temporal_instant(vm, item));
}
// 8.2.3 Temporal.Instant.fromEpochSeconds ( epochSeconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochseconds
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_seconds)
{
// 1. Set epochSeconds to ? ToNumber(epochSeconds).
auto epoch_seconds_value = TRY(vm.argument(0).to_number(vm));
// 2. Set epochSeconds to ? NumberToBigInt(epochSeconds).
auto* epoch_seconds = TRY(number_to_bigint(vm, epoch_seconds_value));
// 3. Let epochNanoseconds be epochSeconds × 10^9.
auto epoch_nanoseconds = BigInt::create(vm, epoch_seconds->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000'000'000 }));
// 4. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 5. Return ! CreateTemporalInstant(epochNanoseconds).
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
}
// 8.2.4 Temporal.Instant.fromEpochMilliseconds ( epochMilliseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochmilliseconds
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_milliseconds)
{
// 1. Set epochMilliseconds to ? ToNumber(epochMilliseconds).
auto epoch_milliseconds_value = TRY(vm.argument(0).to_number(vm));
// 2. Set epochMilliseconds to ? NumberToBigInt(epochMilliseconds).
auto* epoch_milliseconds = TRY(number_to_bigint(vm, epoch_milliseconds_value));
// 3. Let epochNanoseconds be epochMilliseconds × 10^6.
auto epoch_nanoseconds = BigInt::create(vm, epoch_milliseconds->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000'000 }));
// 4. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 5. Return ! CreateTemporalInstant(epochNanoseconds).
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
}
// 8.2.5 Temporal.Instant.fromEpochMicroseconds ( epochMicroseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochmicroseconds
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_microseconds)
{
// 1. Set epochMicroseconds to ? ToBigInt(epochMicroseconds).
auto epoch_microseconds = TRY(vm.argument(0).to_bigint(vm));
// 2. Let epochNanoseconds be epochMicroseconds × 1000.
auto epoch_nanoseconds = BigInt::create(vm, epoch_microseconds->big_integer().multiplied_by(Crypto::UnsignedBigInteger { 1'000 }));
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 4. Return ! CreateTemporalInstant(epochNanoseconds).
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
}
// 8.2.6 Temporal.Instant.fromEpochNanoseconds ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal.instant.fromepochnanoseconds
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::from_epoch_nanoseconds)
{
// 1. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).
auto epoch_nanoseconds = TRY(vm.argument(0).to_bigint(vm));
// 2. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 3. Return ! CreateTemporalInstant(epochNanoseconds).
return MUST(create_temporal_instant(vm, epoch_nanoseconds));
}
// 8.2.7 Temporal.Instant.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.instant.compare
JS_DEFINE_NATIVE_FUNCTION(InstantConstructor::compare)
{
// 1. Set one to ? ToTemporalInstant(one).
auto* one = TRY(to_temporal_instant(vm, vm.argument(0)));
// 2. Set two to ? ToTemporalInstant(two).
auto* two = TRY(to_temporal_instant(vm, vm.argument(1)));
// 3. Return 𝔽(! CompareEpochNanoseconds(one.[[Nanoseconds]], two.[[Nanoseconds]])).
return Value(compare_epoch_nanoseconds(one->nanoseconds(), two->nanoseconds()));
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class InstantConstructor final : public NativeFunction {
JS_OBJECT(InstantConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(InstantConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~InstantConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit InstantConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
JS_DECLARE_NATIVE_FUNCTION(from_epoch_seconds);
JS_DECLARE_NATIVE_FUNCTION(from_epoch_milliseconds);
JS_DECLARE_NATIVE_FUNCTION(from_epoch_microseconds);
JS_DECLARE_NATIVE_FUNCTION(from_epoch_nanoseconds);
JS_DECLARE_NATIVE_FUNCTION(compare);
};
}

View file

@ -1,413 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/InstantPrototype.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(InstantPrototype);
// 8.3 Properties of the Temporal.Instant Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-instant-prototype-object
InstantPrototype::InstantPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void InstantPrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 8.3.2 Temporal.Instant.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Instant"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.epochSeconds, epoch_seconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.epochMilliseconds, epoch_milliseconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.epochMicroseconds, epoch_microseconds_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.epochNanoseconds, epoch_nanoseconds_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.add, add, 1, attr);
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
define_native_function(realm, vm.names.until, until, 1, attr);
define_native_function(realm, vm.names.since, since, 1, attr);
define_native_function(realm, vm.names.round, round, 1, attr);
define_native_function(realm, vm.names.equals, equals, 1, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
define_native_function(realm, vm.names.toZonedDateTime, to_zoned_date_time, 1, attr);
define_native_function(realm, vm.names.toZonedDateTimeISO, to_zoned_date_time_iso, 1, attr);
}
// 8.3.3 get Temporal.Instant.prototype.epochSeconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochseconds
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_seconds_getter)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Let ns be instant.[[Nanoseconds]].
auto& ns = instant->nanoseconds();
// 4. Let s be truncate((ns) / 10^9).
auto [s, _] = ns.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000'000'000 });
// 5. Return 𝔽(s).
return Value((double)s.to_base_deprecated(10).to_number<i64>().value());
}
// 8.3.4 get Temporal.Instant.prototype.epochMilliseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochmilliseconds
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_milliseconds_getter)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Let ns be instant.[[Nanoseconds]].
auto& ns = instant->nanoseconds();
// 4. Let ms be truncate((ns) / 10^6).
auto [ms, _] = ns.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000'000 });
// 5. Return 𝔽(ms).
return Value((double)ms.to_base_deprecated(10).to_number<i64>().value());
}
// 8.3.5 get Temporal.Instant.prototype.epochMicroseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochmicroseconds
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_microseconds_getter)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Let ns be instant.[[Nanoseconds]].
auto& ns = instant->nanoseconds();
// 4. Let µs be truncate((ns) / 10^3).
auto [us, _] = ns.big_integer().divided_by(Crypto::UnsignedBigInteger { 1'000 });
// 5. Return (µs).
return BigInt::create(vm, move(us));
}
// 8.3.6 get Temporal.Instant.prototype.epochNanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.instant.prototype.epochnanoseconds
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::epoch_nanoseconds_getter)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Let ns be instant.[[Nanoseconds]].
auto& ns = instant->nanoseconds();
// 4. Return ns.
return &ns;
}
// 8.3.7 Temporal.Instant.prototype.add ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.add
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::add)
{
auto temporal_duration_like = vm.argument(0);
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromInstant(add, instant, temporalDurationLike).
return TRY(add_duration_to_or_subtract_duration_from_instant(vm, ArithmeticOperation::Add, instant, temporal_duration_like));
}
// 8.3.8 Temporal.Instant.prototype.subtract ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::subtract)
{
auto temporal_duration_like = vm.argument(0);
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromInstant(subtract, instant, temporalDurationLike).
return TRY(add_duration_to_or_subtract_duration_from_instant(vm, ArithmeticOperation::Subtract, instant, temporal_duration_like));
}
// 8.3.9 Temporal.Instant.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.until
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::until)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalInstant(until, instant, other, options).
return TRY(difference_temporal_instant(vm, DifferenceOperation::Until, instant, other, options));
}
// 8.3.10 Temporal.Instant.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.since
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::since)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalInstant(since, instant, other, options).
return TRY(difference_temporal_instant(vm, DifferenceOperation::Since, instant, other, options));
}
// 8.3.11 Temporal.Instant.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::round)
{
auto& realm = *vm.current_realm();
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
}
Object* round_to;
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// b. Set roundTo to OrdinaryObjectCreate(null).
round_to = Object::create(realm, nullptr);
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(vm, vm.argument(0)));
}
// 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required).
auto smallest_unit_value = TRY(get_temporal_unit(vm, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}));
// 6. If smallestUnit is undefined, throw a RangeError exception.
if (!smallest_unit_value.has_value())
return vm.throw_completion<RangeError>(ErrorType::OptionIsNotValidValue, vm.names.undefined.as_string(), "smallestUnit");
// At this point smallest_unit_value can only be a string
auto& smallest_unit = *smallest_unit_value;
// 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *round_to, "halfExpand"sv));
double maximum;
// 8. If smallestUnit is "hour", then
if (smallest_unit == "hour"sv) {
// a. Let maximum be HoursPerDay.
maximum = hours_per_day;
}
// 9. Else if smallestUnit is "minute", then
else if (smallest_unit == "minute"sv) {
// a. Let maximum be MinutesPerHour × HoursPerDay.
maximum = minutes_per_hour * hours_per_day;
}
// 10. Else if smallestUnit is "second", then
else if (smallest_unit == "second"sv) {
// a. Let maximum be SecondsPerMinute × MinutesPerHour × HoursPerDay.
maximum = seconds_per_minute * minutes_per_hour * hours_per_day;
}
// 11. Else if smallestUnit is "millisecond", then
else if (smallest_unit == "millisecond"sv) {
// a. Let maximum be (msPerDay).
maximum = ms_per_day;
}
// 12. Else if smallestUnit is "microsecond", then
else if (smallest_unit == "microsecond"sv) {
// a. Let maximum be 10^3 × (msPerDay).
maximum = 1000 * ms_per_day;
}
// 13. Else,
else {
// a. Assert: smallestUnit is "nanosecond".
VERIFY(smallest_unit == "nanosecond"sv);
// b. Let maximum be nsPerDay.
maximum = ns_per_day;
}
// 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(options).
auto rounding_increment = TRY(to_temporal_rounding_increment(vm, *round_to));
// 15. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, true).
TRY(validate_temporal_rounding_increment(vm, rounding_increment, maximum, true));
// 16. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
auto* rounded_ns = round_temporal_instant(vm, instant->nanoseconds(), rounding_increment, smallest_unit, rounding_mode);
// 17. Return ! CreateTemporalInstant(roundedNs).
return MUST(create_temporal_instant(vm, *rounded_ns));
}
// 8.3.12 Temporal.Instant.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::equals)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Set other to ? ToTemporalInstant(other).
auto other = TRY(to_temporal_instant(vm, vm.argument(0)));
// 4. If instant.[[Nanoseconds]] ≠ other.[[Nanoseconds]], return false.
if (instant->nanoseconds().big_integer() != other->nanoseconds().big_integer())
return Value(false);
// 5. Return true.
return Value(true);
}
// 8.3.13 Temporal.Instant.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_string)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let timeZone be ? Get(options, "timeZone").
auto time_zone = TRY(options->get(vm.names.timeZone));
// 5. If timeZone is not undefined, then
if (!time_zone.is_undefined()) {
// a. Set timeZone to ? ToTemporalTimeZone(timeZone).
time_zone = TRY(to_temporal_time_zone(vm, time_zone));
}
// 6. Let precision be ? ToSecondsStringPrecisionRecord(options).
auto precision = TRY(to_seconds_string_precision_record(vm, *options));
// 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *options, "trunc"sv));
// 8. Let roundedNs be ! RoundTemporalInstant(instant.[[Nanoseconds]], precision.[[Increment]], precision.[[Unit]], roundingMode).
auto* rounded_ns = round_temporal_instant(vm, instant->nanoseconds(), precision.increment, precision.unit, rounding_mode);
// 9. Let roundedInstant be ! CreateTemporalInstant(roundedNs).
auto* rounded_instant = MUST(create_temporal_instant(vm, *rounded_ns));
// 10. Return ? TemporalInstantToString(roundedInstant, timeZone, precision.[[Precision]]).
return PrimitiveString::create(vm, TRY(temporal_instant_to_string(vm, *rounded_instant, time_zone, precision.precision)));
}
// 8.3.14 Temporal.Instant.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tolocalestring
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_locale_string)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Return ? TemporalInstantToString(instant, undefined, "auto").
return PrimitiveString::create(vm, TRY(temporal_instant_to_string(vm, instant, js_undefined(), "auto"sv)));
}
// 8.3.15 Temporal.Instant.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_json)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Return ? TemporalInstantToString(instant, undefined, "auto").
return PrimitiveString::create(vm, TRY(temporal_instant_to_string(vm, instant, js_undefined(), "auto"sv)));
}
// 8.3.16 Temporal.Instant.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::value_of)
{
// 1. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.Instant", "a primitive value");
}
// 8.3.17 Temporal.Instant.prototype.toZonedDateTime ( item ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tozoneddatetime
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_zoned_date_time)
{
auto item = vm.argument(0);
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. If Type(item) is not Object, then
if (!item.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, item);
}
// 4. Let calendarLike be ? Get(item, "calendar").
auto calendar_like = TRY(item.as_object().get(vm.names.calendar));
// 5. If calendarLike is undefined, then
if (calendar_like.is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.calendar.as_string());
}
// 6. Let calendar be ? ToTemporalCalendar(calendarLike).
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
// 7. Let temporalTimeZoneLike be ? Get(item, "timeZone").
auto temporal_time_zone_like = TRY(item.as_object().get(vm.names.timeZone));
// 8. If temporalTimeZoneLike is undefined, then
if (temporal_time_zone_like.is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.timeZone.as_string());
}
// 9. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
auto* time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
// 10. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, calendar).
return TRY(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, *calendar));
}
// 8.3.18 Temporal.Instant.prototype.toZonedDateTimeISO ( timeZone ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.tozoneddatetimeiso
JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::to_zoned_date_time_iso)
{
// 1. Let instant be the this value.
// 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]).
auto instant = TRY(typed_this_object(vm));
// 3. Set timeZone to ? ToTemporalTimeZone(timeZone).
auto* time_zone = TRY(to_temporal_time_zone(vm, vm.argument(0)));
// 4. Let calendar be ! GetISO8601Calendar().
auto* calendar = get_iso8601_calendar(vm);
// 5. Return ? CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, calendar).
return TRY(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, *calendar));
}
}

View file

@ -1,43 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/Instant.h>
namespace JS::Temporal {
class InstantPrototype final : public PrototypeObject<InstantPrototype, Instant> {
JS_PROTOTYPE_OBJECT(InstantPrototype, Instant, Temporal.Instant);
GC_DECLARE_ALLOCATOR(InstantPrototype);
public:
virtual void initialize(Realm&) override;
virtual ~InstantPrototype() override = default;
private:
explicit InstantPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(epoch_seconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_milliseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_microseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_nanoseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(until);
JS_DECLARE_NATIVE_FUNCTION(since);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time);
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time_iso);
};
}

View file

@ -1,241 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Time.h>
#include <LibCrypto/BigInt/SignedBigInteger.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/Now.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(Now);
// 2 The Temporal.Now Object, https://tc39.es/proposal-temporal/#sec-temporal-now-object
Now::Now(Realm& realm)
: Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype())
{
}
void Now::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 2.1.1 Temporal.Now [ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal-now-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.Now"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.timeZone, time_zone, 0, attr);
define_native_function(realm, vm.names.instant, instant, 0, attr);
define_native_function(realm, vm.names.plainDateTime, plain_date_time, 1, attr);
define_native_function(realm, vm.names.plainDateTimeISO, plain_date_time_iso, 0, attr);
define_native_function(realm, vm.names.zonedDateTime, zoned_date_time, 1, attr);
define_native_function(realm, vm.names.zonedDateTimeISO, zoned_date_time_iso, 0, attr);
define_native_function(realm, vm.names.plainDate, plain_date, 1, attr);
define_native_function(realm, vm.names.plainDateISO, plain_date_iso, 0, attr);
define_native_function(realm, vm.names.plainTimeISO, plain_time_iso, 0, attr);
}
// 2.2.1 Temporal.Now.timeZone ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.timezone
JS_DEFINE_NATIVE_FUNCTION(Now::time_zone)
{
// 1. Return ! SystemTimeZone().
return system_time_zone(vm);
}
// 2.2.2 Temporal.Now.instant ( ), https://tc39.es/proposal-temporal/#sec-temporal.now.instant
JS_DEFINE_NATIVE_FUNCTION(Now::instant)
{
// 1. Return ! SystemInstant().
return system_instant(vm);
}
// 2.2.3 Temporal.Now.plainDateTime ( calendarLike [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetime
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_time)
{
auto calendar_like = vm.argument(0);
auto temporal_time_zone_like = vm.argument(1);
// 1. Return ? SystemDateTime(temporalTimeZoneLike, calendarLike).
return TRY(system_date_time(vm, temporal_time_zone_like, calendar_like));
}
// 2.2.4 Temporal.Now.plainDateTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindatetimeiso
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_time_iso)
{
auto temporal_time_zone_like = vm.argument(0);
// 1, Let calendar be ! GetISO8601Calendar().
auto* calendar = get_iso8601_calendar(vm);
// 2. Return ? SystemDateTime(temporalTimeZoneLike, calendar).
return TRY(system_date_time(vm, temporal_time_zone_like, calendar));
}
// 2.2.5 Temporal.Now.zonedDateTime ( calendarLike [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetime
JS_DEFINE_NATIVE_FUNCTION(Now::zoned_date_time)
{
auto calendar_like = vm.argument(0);
auto temporal_time_zone_like = vm.argument(1);
// 1. Return ? SystemZonedDateTime(temporalTimeZoneLike, calendarLike).
return TRY(system_zoned_date_time(vm, temporal_time_zone_like, calendar_like));
}
// 2.2.6 Temporal.Now.zonedDateTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.zoneddatetimeiso
JS_DEFINE_NATIVE_FUNCTION(Now::zoned_date_time_iso)
{
auto temporal_time_zone_like = vm.argument(0);
// 1, Let calendar be ! GetISO8601Calendar().
auto* calendar = get_iso8601_calendar(vm);
// 2. Return ? SystemZonedDateTime(temporalTimeZoneLike, calendar).
return TRY(system_zoned_date_time(vm, temporal_time_zone_like, calendar));
}
// 2.2.7 Temporal.Now.plainDate ( calendarLike [ , temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindate
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date)
{
auto calendar_like = vm.argument(0);
auto temporal_time_zone_like = vm.argument(1);
// 1. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendarLike).
auto* date_time = TRY(system_date_time(vm, temporal_time_zone_like, calendar_like));
// 2. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
return MUST(create_temporal_date(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
}
// 2.2.8 Temporal.Now.plainDateISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaindateiso
JS_DEFINE_NATIVE_FUNCTION(Now::plain_date_iso)
{
auto temporal_time_zone_like = vm.argument(0);
// 1. Let calendar be ! GetISO8601Calendar().
auto* calendar = get_iso8601_calendar(vm);
// 2. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar).
auto* date_time = TRY(system_date_time(vm, temporal_time_zone_like, calendar));
// 3. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
return MUST(create_temporal_date(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
}
// 2.2.9 Temporal.Now.plainTimeISO ( [ temporalTimeZoneLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.now.plaintimeiso
JS_DEFINE_NATIVE_FUNCTION(Now::plain_time_iso)
{
auto temporal_time_zone_like = vm.argument(0);
// 1. Let calendar be ! GetISO8601Calendar().
auto* calendar = get_iso8601_calendar(vm);
// 2. Let dateTime be ? SystemDateTime(temporalTimeZoneLike, calendar).
auto* date_time = TRY(system_date_time(vm, temporal_time_zone_like, calendar));
// 3. Return ! CreateTemporalTime(dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
return MUST(create_temporal_time(vm, date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()));
}
// 2.3.1 SystemTimeZone ( ), https://tc39.es/proposal-temporal/#sec-temporal-systemtimezone
TimeZone* system_time_zone(VM& vm)
{
// 1. Let identifier be ! DefaultTimeZone().
auto identifier = system_time_zone_identifier();
// 2. Return ! CreateTemporalTimeZone(identifier).
// FIXME: Propagate possible OOM error
return MUST(create_temporal_time_zone(vm, move(identifier)));
}
// 2.3.2 SystemUTCEpochNanoseconds ( ), https://tc39.es/proposal-temporal/#sec-temporal-systemutcepochnanoseconds
BigInt* system_utc_epoch_nanoseconds(VM& vm)
{
// 1. Let ns be the approximate current UTC date and time, in nanoseconds since the epoch.
auto now = AK::UnixDateTime::now().nanoseconds_since_epoch();
auto ns = Crypto::SignedBigInteger { now };
// 2. Set ns to the result of clamping ns between nsMinInstant and nsMaxInstant.
// NOTE: Duration::to_nanoseconds() already clamps between -(2^63) and 2^63 - 1, the range of an i64,
// if an overflow occurs during seconds -> nanoseconds conversion.
// 3. Return (ns).
return BigInt::create(vm, move(ns));
}
// 2.3.3 SystemInstant ( ), https://tc39.es/proposal-temporal/#sec-temporal-systeminstant
Instant* system_instant(VM& vm)
{
// 1. Let ns be ! SystemUTCEpochNanoseconds().
auto* ns = system_utc_epoch_nanoseconds(vm);
// 2. Return ! CreateTemporalInstant(ns).
return MUST(create_temporal_instant(vm, *ns));
}
// 2.3.4 SystemDateTime ( temporalTimeZoneLike, calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemdatetime
ThrowCompletionOr<PlainDateTime*> system_date_time(VM& vm, Value temporal_time_zone_like, Value calendar_like)
{
Object* time_zone;
// 1. If temporalTimeZoneLike is undefined, then
if (temporal_time_zone_like.is_undefined()) {
// a. Let timeZone be ! SystemTimeZone().
time_zone = system_time_zone(vm);
}
// 2. Else,
else {
// a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
}
// 3. Let calendar be ? ToTemporalCalendar(calendarLike).
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
// 4. Let instant be ! SystemInstant().
auto* instant = system_instant(vm);
// 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
return builtin_time_zone_get_plain_date_time_for(vm, time_zone, *instant, *calendar);
}
// 2.3.5 SystemZonedDateTime ( temporalTimeZoneLike, calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal-systemzoneddatetime
ThrowCompletionOr<ZonedDateTime*> system_zoned_date_time(VM& vm, Value temporal_time_zone_like, Value calendar_like)
{
Object* time_zone;
// 1. If temporalTimeZoneLike is undefined, then
if (temporal_time_zone_like.is_undefined()) {
// a. Let timeZone be ! SystemTimeZone().
time_zone = system_time_zone(vm);
}
// 2. Else,
else {
// a. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
}
// 3. Let calendar be ? ToTemporalCalendar(calendarLike).
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
// 4. Let ns be ! SystemUTCEpochNanoseconds().
auto* ns = system_utc_epoch_nanoseconds(vm);
// 5. Return ? CreateTemporalZonedDateTime(ns, timeZone, calendar).
return create_temporal_zoned_date_time(vm, *ns, *time_zone, *calendar);
}
}

View file

@ -1,42 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class Now final : public Object {
JS_OBJECT(Now, Object);
GC_DECLARE_ALLOCATOR(Now);
public:
virtual void initialize(Realm&) override;
virtual ~Now() override = default;
private:
explicit Now(Realm&);
JS_DECLARE_NATIVE_FUNCTION(time_zone);
JS_DECLARE_NATIVE_FUNCTION(instant);
JS_DECLARE_NATIVE_FUNCTION(plain_date_time);
JS_DECLARE_NATIVE_FUNCTION(plain_date_time_iso);
JS_DECLARE_NATIVE_FUNCTION(zoned_date_time);
JS_DECLARE_NATIVE_FUNCTION(zoned_date_time_iso);
JS_DECLARE_NATIVE_FUNCTION(plain_date);
JS_DECLARE_NATIVE_FUNCTION(plain_date_iso);
JS_DECLARE_NATIVE_FUNCTION(plain_time_iso);
};
TimeZone* system_time_zone(VM&);
BigInt* system_utc_epoch_nanoseconds(VM&);
Instant* system_instant(VM&);
ThrowCompletionOr<PlainDateTime*> system_date_time(VM&, Value temporal_time_zone_like, Value calendar_like);
ThrowCompletionOr<ZonedDateTime*> system_zoned_date_time(VM&, Value temporal_time_zone_like, Value calendar_like);
}

View file

@ -1,583 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.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/PlainDateConstructor.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDate);
// 3 Temporal.PlainDate Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-objects
PlainDate::PlainDate(i32 year, u8 month, u8 day, Object& calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_iso_year(year)
, m_iso_month(month)
, m_iso_day(day)
, m_calendar(calendar)
{
}
void PlainDate::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_calendar);
}
// 3.5.2 CreateISODateRecord ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-create-iso-date-record
ISODateRecord create_iso_date_record(i32 year, u8 month, u8 day)
{
// 1. Assert: IsValidISODate(year, month, day) is true.
VERIFY(is_valid_iso_date(year, month, day));
// 2. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
return { .year = year, .month = month, .day = day };
}
// 3.5.1 CreateTemporalDate ( isoYear, isoMonth, isoDay, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldate
ThrowCompletionOr<PlainDate*> create_temporal_date(VM& vm, i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: isoYear is an integer.
// 2. Assert: isoMonth is an integer.
// 3. Assert: isoDay is an integer.
// 4. Assert: Type(calendar) is Object.
// 5. 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::TemporalInvalidPlainDate);
// 6. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
if (!iso_date_time_within_limits(iso_year, iso_month, iso_day, 12, 0, 0, 0, 0, 0))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
// 7. If newTarget is not present, set newTarget to %Temporal.PlainDate%.
if (!new_target)
new_target = realm.intrinsics().temporal_plain_date_constructor();
// 8. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDate.prototype%", « [[InitializedTemporalDate]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).
// 9. Set object.[[ISOYear]] to isoYear.
// 10. Set object.[[ISOMonth]] to isoMonth.
// 11. Set object.[[ISODay]] to isoDay.
// 12. Set object.[[Calendar]] to calendar.
auto object = TRY(ordinary_create_from_constructor<PlainDate>(vm, *new_target, &Intrinsics::temporal_plain_date_prototype, iso_year, iso_month, iso_day, calendar));
return object.ptr();
}
// 3.5.2 ToTemporalDate ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldate
ThrowCompletionOr<PlainDate*> to_temporal_date(VM& vm, Value item, Object const* options)
{
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
// 3. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalDate]] internal slot, then
if (is<PlainDate>(item_object)) {
// i. Return item.
return static_cast<PlainDate*>(&item_object);
}
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(item_object)) {
auto& zoned_date_time = static_cast<ZonedDateTime&>(item_object);
// i. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
auto* instant = create_temporal_instant(vm, zoned_date_time.nanoseconds()).release_value();
// iii. Let plainDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
auto* plain_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &zoned_date_time.time_zone(), *instant, zoned_date_time.calendar()));
// iv. Return ! CreateTemporalDate(plainDateTime.[[ISOYear]], plainDateTime.[[ISOMonth]], plainDateTime.[[ISODay]], plainDateTime.[[Calendar]]).
return create_temporal_date(vm, plain_date_time->iso_year(), plain_date_time->iso_month(), plain_date_time->iso_day(), plain_date_time->calendar());
}
// c. If item has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(item_object)) {
auto& date_time_item = static_cast<PlainDateTime&>(item_object);
// i. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// ii. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
return create_temporal_date(vm, date_time_item.iso_year(), date_time_item.iso_month(), date_time_item.iso_day(), date_time_item.calendar());
}
// d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
auto* calendar = TRY(get_temporal_calendar_with_iso_default(vm, item_object));
// e. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, *calendar, { "day"sv, "month"sv, "monthCode"sv, "year"sv }));
// f. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, item_object, field_names, Vector<StringView> {}));
// g. Return ? CalendarDateFromFields(calendar, fields, options).
return calendar_date_from_fields(vm, *calendar, *fields, options);
}
// 4. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// 5. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// 6. Let result be ? ParseTemporalDateString(string).
auto result = TRY(parse_temporal_date_string(vm, string));
// 7. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
VERIFY(is_valid_iso_date(result.year, result.month, result.day));
// 8. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, result.calendar.has_value() ? PrimitiveString::create(vm, *result.calendar) : js_undefined()));
// 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar).
return create_temporal_date(vm, result.year, result.month, result.day, *calendar);
}
// 3.5.3 DifferenceISODate ( y1, m1, d1, y2, m2, d2, largestUnit ), https://tc39.es/proposal-temporal/#sec-temporal-differenceisodate
DateDurationRecord difference_iso_date(VM& vm, i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2, StringView largest_unit)
{
VERIFY(largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv));
// 1. If largestUnit is "year" or "month", then
if (largest_unit.is_one_of("year"sv, "month"sv)) {
// a. Let sign be -(! CompareISODate(y1, m1, d1, y2, m2, d2)).
auto sign = -compare_iso_date(year1, month1, day1, year2, month2, day2);
// b. If sign is 0, return ! CreateDateDurationRecord(0, 0, 0, 0).
if (sign == 0)
return create_date_duration_record(0, 0, 0, 0);
// c. Let start be the Record { [[Year]]: y1, [[Month]]: m1, [[Day]]: d1 }.
auto start = ISODateRecord { .year = year1, .month = month1, .day = day1 };
// d. Let end be the Record { [[Year]]: y2, [[Month]]: m2, [[Day]]: d2 }.
auto end = ISODateRecord { .year = year2, .month = month2, .day = day2 };
// e. Let years be end.[[Year]] - start.[[Year]].
double years = end.year - start.year;
// f. Let mid be ! AddISODate(y1, m1, d1, years, 0, 0, 0, "constrain").
auto mid = MUST(add_iso_date(vm, year1, month1, day1, years, 0, 0, 0, "constrain"sv));
// g. Let midSign be -(! CompareISODate(mid.[[Year]], mid.[[Month]], mid.[[Day]], y2, m2, d2)).
auto mid_sign = -compare_iso_date(mid.year, mid.month, mid.day, year2, month2, day2);
// h. If midSign is 0, then
if (mid_sign == 0) {
// i. If largestUnit is "year", return ! CreateDateDurationRecord(years, 0, 0, 0).
if (largest_unit == "year"sv)
return create_date_duration_record(years, 0, 0, 0);
// ii. Return ! CreateDateDurationRecord(0, years × 12, 0, 0).
return create_date_duration_record(0, years * 12, 0, 0);
}
// i. Let months be end.[[Month]] - start.[[Month]].
double months = end.month - start.month;
// j. If midSign is not equal to sign, then
if (mid_sign != sign) {
// i. Set years to years - sign.
years -= sign;
// ii. Set months to months + sign × 12.
months += sign * 12;
}
// k. Set mid to ! AddISODate(y1, m1, d1, years, months, 0, 0, "constrain").
mid = MUST(add_iso_date(vm, year1, month1, day1, years, months, 0, 0, "constrain"sv));
// l. Set midSign to -(! CompareISODate(mid.[[Year]], mid.[[Month]], mid.[[Day]], y2, m2, d2)).
mid_sign = -compare_iso_date(mid.year, mid.month, mid.day, year2, month2, day2);
// m. If midSign is 0, then
if (mid_sign == 0) {
// i. If largestUnit is "year", return ! CreateDateDurationRecord(years, months, 0, 0).
if (largest_unit == "year"sv)
return create_date_duration_record(years, months, 0, 0);
// ii. Return ! CreateDateDurationRecord(0, months + years × 12, 0, 0).
return create_date_duration_record(0, months + years * 12, 0, 0);
}
// n. If midSign is not equal to sign, then
if (mid_sign != sign) {
// i. Set months to months - sign.
months -= sign;
// ii. If months is equal to -sign, then
if (months == -sign) {
// 1. Set years to years - sign.
years -= sign;
// 2. Set months to 11 × sign.
months = 11 * sign;
}
// iii. Set mid to ! AddISODate(y1, m1, d1, years, months, 0, 0, "constrain").
mid = MUST(add_iso_date(vm, year1, month1, day1, years, months, 0, 0, "constrain"sv));
}
double days;
// o. If mid.[[Month]] = end.[[Month]], then
if (mid.month == end.month) {
// i. Assert: mid.[[Year]] = end.[[Year]].
VERIFY(mid.year == end.year);
// ii. Let days be end.[[Day]] - mid.[[Day]].
days = end.day - mid.day;
}
// p. Else if sign < 0, let days be -mid.[[Day]] - (! ISODaysInMonth(end.[[Year]], end.[[Month]]) - end.[[Day]]).
else if (sign < 0) {
days = -mid.day - (iso_days_in_month(end.year, end.month) - end.day);
}
// q. Else, let days be end.[[Day]] + (! ISODaysInMonth(mid.[[Year]], mid.[[Month]]) - mid.[[Day]]).
else {
days = end.day + (iso_days_in_month(mid.year, mid.month) - mid.day);
}
// r. If largestUnit is "month", then
if (largest_unit == "month"sv) {
// i. Set months to months + years × 12.
months += years * 12;
// ii. Set years to 0.
years = 0;
}
// s. Return ! CreateDateDurationRecord(years, months, 0, days).
return create_date_duration_record(years, months, 0, days);
}
// 2. Else,
else {
// a. Assert: largestUnit is "day" or "week".
VERIFY(largest_unit.is_one_of("day"sv, "week"sv));
// b. Let epochDays1 be MakeDay(𝔽(y1), 𝔽(m1 - 1), 𝔽(d1)).
auto epoch_days_1 = make_day(year1, month1 - 1, day1);
// c. Assert: epochDays1 is finite.
VERIFY(isfinite(epoch_days_1));
// d. Let epochDays2 be MakeDay(𝔽(y2), 𝔽(m2 - 1), 𝔽(d2)).
auto epoch_days_2 = make_day(year2, month2 - 1, day2);
// e. Assert: epochDays2 is finite.
VERIFY(isfinite(epoch_days_2));
// f. Let days be (epochDays2) - (epochDays1).
auto days = epoch_days_2 - epoch_days_1;
// g. Let weeks be 0.
double weeks = 0;
// h. If largestUnit is "week", then
if (largest_unit == "week"sv) {
// i. Set weeks to truncate(days / 7).
weeks = trunc(days / 7);
// ii. Set days to remainder(days, 7).
days = fmod(days, 7);
}
// i. Return ! CreateDateDurationRecord(0, 0, weeks, days).
return create_date_duration_record(0, 0, weeks, days);
}
}
// 3.5.4 RegulateISODate ( year, month, day, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisodate
ThrowCompletionOr<ISODateRecord> regulate_iso_date(VM& vm, double year, double month, double day, StringView overflow)
{
VERIFY(year == trunc(year) && month == trunc(month) && day == trunc(day));
// 1. If overflow is "constrain", then
if (overflow == "constrain"sv) {
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat this double as normal integer from this point onwards. This
// does not change the exposed behavior as the parent's call to CreateTemporalDate will immediately check that this value is a valid
// ISO value for years: -273975 - 273975, which is a subset of this check.
if (!AK::is_within_range<i32>(year))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
// a. Set month to the result of clamping month between 1 and 12.
month = clamp(month, 1, 12);
// b. Let daysInMonth be ! ISODaysInMonth(year, month).
auto days_in_month = iso_days_in_month(static_cast<i32>(year), static_cast<u8>(month));
// c. Set day to the result of clamping day between 1 and daysInMonth.
day = clamp(day, 1, days_in_month);
// d. Return CreateISODateRecord(year, month, day).
return create_iso_date_record(static_cast<i32>(year), static_cast<u8>(month), static_cast<u8>(day));
}
// 2. Else,
else {
// a. Assert: overflow is "reject".
VERIFY(overflow == "reject"sv);
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to IsValidISODate will immediately check that these values are valid ISO
// values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
if (!AK::is_within_range<i32>(year) || !AK::is_within_range<u8>(month) || !AK::is_within_range<u8>(day))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
auto y = static_cast<i32>(year);
auto m = static_cast<u8>(month);
auto d = static_cast<u8>(day);
// b. If IsValidISODate(year, month, day) is false, throw a RangeError exception.
if (!is_valid_iso_date(y, m, d))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
// c. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day }.
return ISODateRecord { .year = y, .month = m, .day = d };
}
}
// 3.5.5 IsValidISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidisodate
bool is_valid_iso_date(i32 year, u8 month, u8 day)
{
// 1. If month < 1 or month > 12, then
if (month < 1 || month > 12) {
// a. Return false.
return false;
}
// 2. Let daysInMonth be ! ISODaysInMonth(year, month).
auto days_in_month = iso_days_in_month(year, month);
// 3. If day < 1 or day > daysInMonth, then
if (day < 1 || day > days_in_month) {
// a. Return false.
return false;
}
// 4. Return true.
return true;
}
// 3.5.6 DifferenceDate ( calendarRec, one, two, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencedate
ThrowCompletionOr<GC::Ref<Duration>> difference_date(VM& vm, CalendarMethods const& calendar_record, PlainDate const& one, PlainDate const& two, Object const& options)
{
// FIXME: 1. Assert: one.[[Calendar]] and two.[[Calendar]] have been determined to be equivalent as with CalendarEquals.
// FIXME: 2. Assert: options is an ordinary Object.
// 3. Assert: options.[[Prototype]] is null.
VERIFY(!options.prototype());
// 4. Assert: options has a "largestUnit" data property.
VERIFY(MUST(options.has_own_property(vm.names.largestUnit)));
// 5. If one.[[ISOYear]] = two.[[ISOYear]] and one.[[ISOMonth]] = two.[[ISOMonth]] and one.[[ISODay]] = two.[[ISODay]], then
if (one.iso_year() == two.iso_year() && one.iso_month() == two.iso_month() && one.iso_day() == two.iso_day()) {
// a. Return ! CreateTemporalDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
}
// 6. If ! Get(options, "largestUnit") is "day", then
auto largest_unit = MUST(options.get(vm.names.largestUnit));
if (largest_unit.is_string() && largest_unit.as_string().utf8_string_view() == "day"sv) {
// a. Let days be DaysUntil(one, two).
auto days = days_until(one, two);
// b. Return ! CreateTemporalDuration(0, 0, 0, days, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, 0, 0, 0, days, 0, 0, 0, 0, 0, 0));
}
// 7. Return ? CalendarDateUntil(calendarRec, one, two, options).
return TRY(calendar_date_until(vm, calendar_record, Value { &one }, Value { &two }, options));
}
// 3.5.6 BalanceISODate ( year, month, day ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodate
ISODateRecord balance_iso_date(double year, double month, double day)
{
// 1. Let epochDays be MakeDay(𝔽(year), 𝔽(month - 1), 𝔽(day)).
auto epoch_days = make_day(year, month - 1, day);
// 2. Assert: epochDays is finite.
VERIFY(isfinite(epoch_days));
// 3. Let ms be MakeDate(epochDays, +0𝔽).
auto ms = make_date(epoch_days, 0);
// 4. Return CreateISODateRecord((YearFromTime(ms)), (MonthFromTime(ms)) + 1, (DateFromTime(ms))).
return create_iso_date_record(year_from_time(ms), static_cast<u8>(month_from_time(ms) + 1), date_from_time(ms));
}
// 3.5.7 PadISOYear ( y ), https://tc39.es/proposal-temporal/#sec-temporal-padisoyear
ThrowCompletionOr<String> pad_iso_year(VM& vm, i32 y)
{
// 1. Assert: y is an integer.
// 2. If y ≥ 0 and y ≤ 9999, then
if (y >= 0 && y <= 9999) {
// a. Return ToZeroPaddedDecimalString(y, 4).
return TRY_OR_THROW_OOM(vm, String::formatted("{:04}", y));
}
// 3. If y > 0, let yearSign be "+"; otherwise, let yearSign be "-".
auto year_sign = y > 0 ? '+' : '-';
// 4. Let year be ToZeroPaddedDecimalString(abs(y), 6).
// 5. Return the string-concatenation of yearSign and year.
return TRY_OR_THROW_OOM(vm, String::formatted("{}{:06}", year_sign, abs(y)));
}
// 3.5.8 TemporalDateToString ( temporalDate, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldatetostring
ThrowCompletionOr<String> temporal_date_to_string(VM& vm, PlainDate& temporal_date, StringView show_calendar)
{
// 1. Assert: Type(temporalDate) is Object.
// 2. Assert: temporalDate has an [[InitializedTemporalDate]] internal slot.
// 3. Let year be ! PadISOYear(temporalDate.[[ISOYear]]).
auto year = MUST_OR_THROW_OOM(pad_iso_year(vm, temporal_date.iso_year()));
// 4. Let month be ToZeroPaddedDecimalString(monthDay.[[ISOMonth]], 2).
auto month = TRY_OR_THROW_OOM(vm, String::formatted("{:02}", temporal_date.iso_month()));
// 5. Let day be ToZeroPaddedDecimalString(monthDay.[[ISODay]], 2).
auto day = TRY_OR_THROW_OOM(vm, String::formatted("{:02}", temporal_date.iso_day()));
// 6. Let calendar be ? MaybeFormatCalendarAnnotation(temporalDate.[[Calendar]], showCalendar).
auto calendar = TRY(maybe_format_calendar_annotation(vm, &temporal_date.calendar(), show_calendar));
// 7. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, and calendar.
return TRY_OR_THROW_OOM(vm, String::formatted("{}-{}-{}{}", year, month, day, calendar));
}
// 3.5.9 AddISODate ( year, month, day, years, months, weeks, days, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-addisodate
ThrowCompletionOr<ISODateRecord> add_iso_date(VM& vm, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow)
{
// 1. Assert: year, month, day, years, months, weeks, and days are integers.
VERIFY(years == trunc(years) && months == trunc(months) && weeks == trunc(weeks) && days == trunc(days));
// 2. Assert: overflow is either "constrain" or "reject".
VERIFY(overflow == "constrain"sv || overflow == "reject"sv);
// 3. Let intermediate be ! BalanceISOYearMonth(year + years, month + months).
auto intermediate_year_month = balance_iso_year_month(year + years, month + months);
// 4. Let intermediate be ? RegulateISODate(intermediate.[[Year]], intermediate.[[Month]], day, overflow).
auto intermediate = TRY(regulate_iso_date(vm, intermediate_year_month.year, intermediate_year_month.month, day, overflow));
// 5. Set days to days + 7 × weeks.
days += 7 * weeks;
// 6. Let d be intermediate.[[Day]] + days.
auto d = intermediate.day + days;
// 7. Return BalanceISODate(intermediate.[[Year]], intermediate.[[Month]], d).
return balance_iso_date(intermediate.year, intermediate.month, d);
}
// 3.5.10 CompareISODate ( y1, m1, d1, y2, m2, d2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodate
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2)
{
// 1. Assert: y1, m1, d1, y2, m2, and d2 are integers.
// 2. If y1 > y2, return 1.
if (year1 > year2)
return 1;
// 3. If y1 < y2, return -1.
if (year1 < year2)
return -1;
// 4. If m1 > m2, return 1.
if (month1 > month2)
return 1;
// 5. If m1 < m2, return -1.
if (month1 < month2)
return -1;
// 6. If d1 > d2, return 1.
if (day1 > day2)
return 1;
// 7. If d1 < d2, return -1.
if (day1 < day2)
return -1;
// 8. Return 0.
return 0;
}
// 3.5.11 DifferenceTemporalPlainDate ( operation, temporalDate, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindate
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_date(VM& vm, DifferenceOperation operation, PlainDate& temporal_date, Value other_value, Value options)
{
// 1. If operation is SINCE, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalDate(other).
auto* other = TRY(to_temporal_date(vm, other_value));
// 3. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
if (!TRY(calendar_equals(vm, temporal_date.calendar(), other->calendar())))
return vm.throw_completion<RangeError>(ErrorType::TemporalDifferentCalendars);
// 4. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
auto resolved_options = TRY(TRY(get_options_object(vm, options))->snapshot_own_properties(vm, nullptr));
// 5. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, DATE, « », "day", "day").
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Date, {}, { "day"sv }, "day"sv));
// 6. If temporalDate.[[ISOYear]] = other.[[ISOYear]], and temporalDate.[[ISOMonth]] = other.[[ISOMonth]], and temporalDate.[[ISODay]] = other.[[ISODay]], then
if (temporal_date.iso_year() == other->iso_year() && temporal_date.iso_month() == other->iso_month() && temporal_date.iso_day() == other->iso_day()) {
// a. Return ! CreateTemporalDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
}
// 7. Let calendarRec be ? CreateCalendarMethodsRecord(temporalDate.[[Calendar]], « DATE-ADD, DATE-UNTIL »).
// FIXME: The type of calendar in PlainDate does not align with latest spec
auto calendar_record = TRY(create_calendar_methods_record(vm, GC::Ref<Object> { temporal_date.calendar() }, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
// 8. Perform ! CreateDataPropertyOrThrow(resolvedOptions, "largestUnit", settings.[[LargestUnit]]).
MUST(resolved_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, settings.largest_unit)));
// 9. Let result be ? DifferenceDate(calendarRec, temporalDate, other, resolvedOptions).
auto result = TRY(difference_date(vm, calendar_record, temporal_date, *other, resolved_options));
// 10. If settings.[[SmallestUnit]] is "day" and settings.[[RoundingIncrement]] = 1, let roundingGranularityIsNoop be true; else let roundingGranularityIsNoop be false.
bool rounding_granularity_is_noop = settings.smallest_unit == "day"sv && settings.rounding_increment == 1;
// 11. If roundingGranularityIsNoop is false, then
if (!rounding_granularity_is_noop) {
// a. Let roundRecord be ? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], ZeroTimeDuration(), settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], temporalDate, calendarRec).
auto round_record = TRY(round_duration(vm, result->years(), result->months(), result->weeks(), result->days(), 0, 0, 0, 0, 0, 0, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &temporal_date, calendar_record)).duration_record;
// FIXME: b. Let roundResult be roundRecord.[[NormalizedDuration]].
// FIXME: c. Set result to ? BalanceDateDurationRelative(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], settings.[[LargestUnit]], settings.[[SmallestUnit]], temporalDate, calendarRec).
result = MUST(create_temporal_duration(vm, round_record.years, round_record.months, round_record.weeks, round_record.days, 0, 0, 0, 0, 0, 0));
}
// 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, sign * result->years(), sign * result->months(), sign * result->weeks(), sign * result->days(), 0, 0, 0, 0, 0, 0));
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Forward.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
namespace JS::Temporal {
class PlainDate final : public Object {
JS_OBJECT(PlainDate, Object);
GC_DECLARE_ALLOCATOR(PlainDate);
public:
virtual ~PlainDate() override = default;
[[nodiscard]] i32 iso_year() const { return m_iso_year; }
[[nodiscard]] u8 iso_month() const { return m_iso_month; }
[[nodiscard]] u8 iso_day() const { return m_iso_day; }
[[nodiscard]] Object const& calendar() const { return m_calendar; }
[[nodiscard]] Object& calendar() { return m_calendar; }
private:
PlainDate(i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 3.4 Properties of Temporal.PlainDate Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plaindate-instances
i32 m_iso_year { 0 }; // [[ISOYear]]
u8 m_iso_month { 1 }; // [[ISOMonth]]
u8 m_iso_day { 1 }; // [[ISODay]]
GC::Ref<Object> m_calendar; // [[Calendar]]
};
// 3.5.1 ISO Date Records, https://tc39.es/proposal-temporal/#sec-temporal-iso-date-records
struct ISODateRecord {
i32 year;
u8 month;
u8 day;
};
ISODateRecord create_iso_date_record(i32 year, u8 month, u8 day);
ThrowCompletionOr<PlainDate*> create_temporal_date(VM&, i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<PlainDate*> to_temporal_date(VM&, Value item, Object const* options = nullptr);
DateDurationRecord difference_iso_date(VM&, i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2, StringView largest_unit);
ThrowCompletionOr<ISODateRecord> regulate_iso_date(VM&, double year, double month, double day, StringView overflow);
bool is_valid_iso_date(i32 year, u8 month, u8 day);
ISODateRecord balance_iso_date(double year, double month, double day);
ThrowCompletionOr<String> pad_iso_year(VM&, i32 y);
ThrowCompletionOr<String> temporal_date_to_string(VM&, PlainDate&, StringView show_calendar);
ThrowCompletionOr<ISODateRecord> add_iso_date(VM&, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow);
i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2);
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_date(VM&, DifferenceOperation, PlainDate&, Value other, Value options);
ThrowCompletionOr<GC::Ref<Duration>> difference_date(VM& vm, CalendarMethods const&, PlainDate const& one, PlainDate const& two, Object const& options);
}

View file

@ -1,111 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.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/PlainDateConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDateConstructor);
// 3.1 The Temporal.PlainDate Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaindate-constructor
PlainDateConstructor::PlainDateConstructor(Realm& realm)
: NativeFunction(realm.vm().names.PlainDate.as_string(), realm.intrinsics().function_prototype())
{
}
void PlainDateConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 3.2.1 Temporal.PlainDate.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_date_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.from, from, 1, attr);
define_native_function(realm, vm.names.compare, compare, 2, attr);
define_direct_property(vm.names.length, Value(3), Attribute::Configurable);
}
// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
ThrowCompletionOr<Value> PlainDateConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainDate");
}
// 3.1.1 Temporal.PlainDate ( isoYear, isoMonth, isoDay [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate
ThrowCompletionOr<GC::Ref<Object>> PlainDateConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Let y be ? ToIntegerWithTruncation(isoYear).
auto y = TRY(to_integer_with_truncation(vm, vm.argument(0), ErrorType::TemporalInvalidPlainDate));
// 3. Let m be ? ToIntegerWithTruncation(isoMonth).
auto m = TRY(to_integer_with_truncation(vm, vm.argument(1), ErrorType::TemporalInvalidPlainDate));
// 4. Let d be ? ToIntegerWithTruncation(isoDay).
auto d = TRY(to_integer_with_truncation(vm, vm.argument(2), ErrorType::TemporalInvalidPlainDate));
// 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, vm.argument(3)));
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to CreateTemporalDate will immediately check that these values are valid
// ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
if (!AK::is_within_range<i32>(y) || !AK::is_within_range<u8>(m) || !AK::is_within_range<u8>(d))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDate);
// 6. Return ? CreateTemporalDate(y, m, d, calendar, NewTarget).
return *TRY(create_temporal_date(vm, y, m, d, *calendar, &new_target));
}
// 3.2.2 Temporal.PlainDate.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.from
JS_DEFINE_NATIVE_FUNCTION(PlainDateConstructor::from)
{
// 1. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
auto item = vm.argument(0);
// 2. If Type(item) is Object and item has an [[InitializedTemporalDate]] internal slot, then
if (item.is_object() && is<PlainDate>(item.as_object())) {
auto& plain_date_item = static_cast<PlainDate&>(item.as_object());
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// b. Return ! CreateTemporalDate(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]]).
return MUST(create_temporal_date(vm, plain_date_item.iso_year(), plain_date_item.iso_month(), plain_date_item.iso_day(), plain_date_item.calendar()));
}
// 3. Return ? ToTemporalDate(item, options).
return TRY(to_temporal_date(vm, item, options));
}
// 3.2.3 Temporal.PlainDate.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.compare
JS_DEFINE_NATIVE_FUNCTION(PlainDateConstructor::compare)
{
// 1. Set one to ? ToTemporalDate(one).
auto* one = TRY(to_temporal_date(vm, vm.argument(0)));
// 2. Set two to ? ToTemporalDate(two).
auto* two = TRY(to_temporal_date(vm, vm.argument(1)));
// 3. Return 𝔽(! CompareISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]])).
return Value(compare_iso_date(one->iso_year(), one->iso_month(), one->iso_day(), two->iso_year(), two->iso_month(), two->iso_day()));
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class PlainDateConstructor final : public NativeFunction {
JS_OBJECT(PlainDateConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(PlainDateConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~PlainDateConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit PlainDateConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
JS_DECLARE_NATIVE_FUNCTION(compare);
};
}

View file

@ -1,665 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#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/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDatePrototype.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDatePrototype);
// 3.3 Properties of the Temporal.PlainDate Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plaindate-prototype-object
PlainDatePrototype::PlainDatePrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void PlainDatePrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 3.3.2 Temporal.PlainDate.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.PlainDate"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.calendar, calendar_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.year, year_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.month, month_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.monthCode, month_code_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.day, day_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.dayOfWeek, day_of_week_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.dayOfYear, day_of_year_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.weekOfYear, week_of_year_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.yearOfWeek, year_of_week_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.daysInWeek, days_in_week_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.daysInMonth, days_in_month_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.daysInYear, days_in_year_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.monthsInYear, months_in_year_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.inLeapYear, in_leap_year_getter, {}, Attribute::Configurable);
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.calendarId, calendar_id_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.toPlainYearMonth, to_plain_year_month, 0, attr);
define_native_function(realm, vm.names.toPlainMonthDay, to_plain_month_day, 0, attr);
define_native_function(realm, vm.names.getISOFields, get_iso_fields, 0, attr);
define_native_function(realm, vm.names.add, add, 1, attr);
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
define_native_function(realm, vm.names.with, with, 1, attr);
define_native_function(realm, vm.names.withCalendar, with_calendar, 1, attr);
define_native_function(realm, vm.names.until, until, 1, attr);
define_native_function(realm, vm.names.since, since, 1, attr);
define_native_function(realm, vm.names.equals, equals, 1, attr);
define_native_function(realm, vm.names.toPlainDateTime, to_plain_date_time, 0, attr);
define_native_function(realm, vm.names.toZonedDateTime, to_zoned_date_time, 1, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
}
// 3.3.3 get Temporal.PlainDate.prototype.calendar, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.calendar
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::calendar_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Return temporalDate.[[Calendar]].
return Value(&temporal_date->calendar());
}
// 3.3.4 get Temporal.PlainDate.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.year
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::year_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarYear(calendar, temporalDate)).
return TRY(calendar_year(vm, calendar, temporal_date));
}
// 3.3.5 get Temporal.PlainDate.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.month
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::month_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarMonth(calendar, temporalDate)).
return TRY(calendar_month(vm, calendar, temporal_date));
}
// 3.3.6 get Temporal.PlainDate.prototype.monthCode, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.monthCode
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::month_code_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return ? CalendarMonthCode(calendar, temporalDate).
return PrimitiveString::create(vm, TRY(calendar_month_code(vm, calendar, temporal_date)));
}
// 3.3.7 get Temporal.PlainDate.prototype.day, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.day
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::day_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarDay(calendar, temporalDate)).
return TRY(calendar_day(vm, calendar, temporal_date));
}
// 3.3.8 get Temporal.PlainDate.prototype.dayOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.dayofweek
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::day_of_week_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// Return 𝔽(? CalendarDayOfWeek(calendar, temporalDate)).
return TRY(calendar_day_of_week(vm, calendar, temporal_date));
}
// 3.3.9 get Temporal.PlainDate.prototype.dayOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.dayofyear
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::day_of_year_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarDayOfYear(calendar, temporalDate)).
return TRY(calendar_day_of_year(vm, calendar, temporal_date));
}
// 3.3.10 get Temporal.PlainDate.prototype.weekOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.weekofyear
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::week_of_year_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarWeekOfYear(calendar, temporalDate)).
return TRY(calendar_week_of_year(vm, calendar, temporal_date));
}
// 3.3.11 get Temporal.PlainDate.prototype.yearOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.yearofweek
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::year_of_week_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarYearOfWeek(calendar, temporalDate)).
return TRY(calendar_year_of_week(vm, calendar, temporal_date));
}
// 3.3.12 get Temporal.PlainDate.prototype.daysInWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinweek
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::days_in_week_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarDaysInWeek(calendar, temporalDate)).
return TRY(calendar_days_in_week(vm, calendar, temporal_date));
}
// 3.3.13 get Temporal.PlainDate.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinmonth
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::days_in_month_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarDaysInMonth(calendar, temporalDate)).
return TRY(calendar_days_in_month(vm, calendar, temporal_date));
}
// 3.3.14 get Temporal.PlainDate.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.daysinyear
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::days_in_year_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarDaysInYear(calendar, temporalDate)).
return TRY(calendar_days_in_year(vm, calendar, temporal_date));
}
// 3.3.15 get Temporal.PlainDate.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.monthsinyear
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::months_in_year_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return 𝔽(? CalendarMonthsInYear(calendar, temporalDate)).
return TRY(calendar_months_in_year(vm, calendar, temporal_date));
}
// 3.3.16 get Temporal.PlainDate.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.inleapyear
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::in_leap_year_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Return ? CalendarInLeapYear(calendar, temporalDate).
return Value(TRY(calendar_in_leap_year(vm, calendar, temporal_date)));
}
// 15.6.5.2 get Temporal.PlainDate.prototype.era, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.era
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::era_getter)
{
// 1. Let plainDate be the this value.
// 2. Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]).
auto plain_date = TRY(typed_this_object(vm));
// 3. Let calendar be plainDate.[[Calendar]].
auto& calendar = plain_date->calendar();
// 4. Return ? CalendarEra(calendar, plainDate).
return TRY(calendar_era(vm, calendar, plain_date));
}
// 15.6.5.3 get Temporal.PlainDate.prototype.eraYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.erayear
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::era_year_getter)
{
// 1. Let plainDate be the this value.
// 2. Perform ? RequireInternalSlot(plainDate, [[InitializedTemporalDate]]).
auto plain_date = TRY(typed_this_object(vm));
// 3. Let calendar be plainDate.[[Calendar]].
auto& calendar = plain_date->calendar();
// 4. Return ? CalendarEraYear(calendar, plainDate).
return TRY(calendar_era_year(vm, calendar, plain_date));
}
// 3.3.17 Temporal.PlainDate.prototype.toPlainYearMonth ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplainyearmonth
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_plain_year_month)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "monthCode"sv, "year"sv }));
// 5. Let fields be ? PrepareTemporalFields(temporalDate, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, temporal_date, field_names, Vector<StringView> {}));
// 6. Return ? CalendarYearMonthFromFields(calendar, fields).
return TRY(calendar_year_month_from_fields(vm, calendar, *fields));
}
// 3.3.18 Temporal.PlainDate.prototype.toPlainMonthDay ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplainmonthday
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_plain_month_day)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 4. Let fieldNames be ? CalendarFields(calendar, « "day", "monthCode" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "day"sv, "monthCode"sv }));
// 5. Let fields be ? PrepareTemporalFields(temporalDate, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, temporal_date, field_names, Vector<StringView> {}));
// 6. Return ? CalendarMonthDayFromFields(calendar, fields).
return TRY(calendar_month_day_from_fields(vm, calendar, *fields));
}
// 3.3.19 Temporal.PlainDate.prototype.getISOFields ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.getisofields
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::get_iso_fields)
{
auto& realm = *vm.current_realm();
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let fields be OrdinaryObjectCreate(%Object.prototype%).
auto fields = Object::create(realm, realm.intrinsics().object_prototype());
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalDate.[[Calendar]]).
MUST(fields->create_data_property_or_throw(vm.names.calendar, Value(&temporal_date->calendar())));
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(temporalDate.[[ISODay]])).
MUST(fields->create_data_property_or_throw(vm.names.isoDay, Value(temporal_date->iso_day())));
// 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", 𝔽(temporalDate.[[ISOMonth]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMonth, Value(temporal_date->iso_month())));
// 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", 𝔽(temporalDate.[[ISOYear]])).
MUST(fields->create_data_property_or_throw(vm.names.isoYear, Value(temporal_date->iso_year())));
// 8. Return fields.
return fields;
}
// 3.3.20 Temporal.PlainDate.prototype.add ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.add
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::add)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let duration be ? ToTemporalDuration(temporalDurationLike).
auto duration = TRY(to_temporal_duration(vm, vm.argument(0)));
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 5. Return ? CalendarDateAdd(temporalDate.[[Calendar]], temporalDate, duration, options).
return TRY(calendar_date_add(vm, temporal_date->calendar(), temporal_date, *duration, options));
}
// 3.3.21 Temporal.PlainDate.prototype.subtract ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::subtract)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let duration be ? ToTemporalDuration(temporalDurationLike).
auto duration = TRY(to_temporal_duration(vm, vm.argument(0)));
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 5. Let negatedDuration be ! CreateNegatedTemporalDuration(duration).
auto negated_duration = create_negated_temporal_duration(vm, *duration);
// 6. Return ? CalendarDateAdd(temporalDate.[[Calendar]], temporalDate, negatedDuration, options).
return TRY(calendar_date_add(vm, temporal_date->calendar(), temporal_date, *negated_duration, options));
}
// 3.3.22 Temporal.PlainDate.prototype.with ( temporalDateLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.with
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::with)
{
auto temporal_date_like = vm.argument(0);
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. If Type(temporalDateLike) is not Object, then
if (!temporal_date_like.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, temporal_date_like.to_string_without_side_effects());
}
// 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalDateLike).
TRY(reject_object_with_calendar_or_time_zone(vm, temporal_date_like.as_object()));
// 5. Let calendar be temporalDate.[[Calendar]].
auto& calendar = temporal_date->calendar();
// 6. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "day"sv, "month"sv, "monthCode"sv, "year"sv }));
// 7. Let partialDate be ? PrepareTemporalFields(temporalDateLike, fieldNames, partial).
auto* partial_date = TRY(prepare_temporal_fields(vm, temporal_date_like.as_object(), field_names, PrepareTemporalFieldsPartial {}));
// 8. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
// 9. Let fields be ? PrepareTemporalFields(temporalDate, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, temporal_date, field_names, Vector<StringView> {}));
// 10. Set fields to ? CalendarMergeFields(calendar, fields, partialDate).
fields = TRY(calendar_merge_fields(vm, calendar, *fields, *partial_date));
// 11. Set fields to ? PrepareTemporalFields(fields, fieldNames, «»).
fields = TRY(prepare_temporal_fields(vm, *fields, field_names, Vector<StringView> {}));
// 12. Return ? CalendarDateFromFields(calendar, fields, options).
return TRY(calendar_date_from_fields(vm, calendar, *fields, options));
}
// 3.3.23 Temporal.PlainDate.prototype.withCalendar ( calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.withcalendar
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::with_calendar)
{
auto calendar_like = vm.argument(0);
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Let calendar be ? ToTemporalCalendar(calendarLike).
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
// 4. Return ! CreateTemporalDate(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], calendar).
return MUST(create_temporal_date(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), *calendar));
}
// 3.3.24 Temporal.PlainDate.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.until
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::until)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainDate(until, temporalDate, other, options).
return TRY(difference_temporal_plain_date(vm, DifferenceOperation::Until, temporal_date, other, options));
}
// 3.3.25 Temporal.PlainDate.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.since
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::since)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainDate(since, temporalDate, other, options).
return TRY(difference_temporal_plain_date(vm, DifferenceOperation::Since, temporal_date, other, options));
}
// 3.3.26 Temporal.PlainDate.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::equals)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Set other to ? ToTemporalDate(other).
auto* other = TRY(to_temporal_date(vm, vm.argument(0)));
// 4. If temporalDate.[[ISOYear]] ≠ other.[[ISOYear]], return false.
if (temporal_date->iso_year() != other->iso_year())
return Value(false);
// 5. If temporalDate.[[ISOMonth]] ≠ other.[[ISOMonth]], return false.
if (temporal_date->iso_month() != other->iso_month())
return Value(false);
// 6. If temporalDate.[[ISODay]] ≠ other.[[ISODay]], return false.
if (temporal_date->iso_day() != other->iso_day())
return Value(false);
// 7. Return ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]).
return Value(TRY(calendar_equals(vm, temporal_date->calendar(), other->calendar())));
}
// 3.3.27 Temporal.PlainDate.prototype.toPlainDateTime ( [ temporalTime ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.toplaindatetime
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_plain_date_time)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. If temporalTime is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], 0, 0, 0, 0, 0, 0, temporalDate.[[Calendar]]).
return TRY(create_temporal_date_time(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), 0, 0, 0, 0, 0, 0, temporal_date->calendar()));
}
// 4. Set temporalTime to ? ToTemporalTime(temporalTime).
auto* temporal_time = TRY(to_temporal_time(vm, vm.argument(0)));
// 5. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]).
return TRY(create_temporal_date_time(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), temporal_date->calendar()));
}
// 3.3.28 Temporal.PlainDate.prototype.toZonedDateTime ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tozoneddatetime
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_zoned_date_time)
{
auto item = vm.argument(0);
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
auto temporal_time_value = js_undefined();
Object* time_zone;
// 3. If Type(item) is Object, then
if (item.is_object()) {
// a. If item has an [[InitializedTemporalTimeZone]] internal slot, then
if (is<TimeZone>(item.as_object())) {
// i. Let timeZone be item.
time_zone = &item.as_object();
// ii. Let temporalTime be undefined.
}
// b. Else,
else {
// i. Let timeZoneLike be ? Get(item, "timeZone").
auto time_zone_like = TRY(item.as_object().get(vm.names.timeZone));
// ii. If timeZoneLike is undefined, then
if (time_zone_like.is_undefined()) {
// 1. Let timeZone be ? ToTemporalTimeZone(item).
time_zone = TRY(to_temporal_time_zone(vm, item));
// 2. Let temporalTime be undefined.
}
// iii. Else,
else {
// 1. Let timeZone be ? ToTemporalTimeZone(timeZoneLike).
time_zone = TRY(to_temporal_time_zone(vm, time_zone_like));
// 2. Let temporalTime be ? Get(item, "plainTime").
temporal_time_value = TRY(item.as_object().get(vm.names.plainTime));
}
}
}
// 4. Else,
else {
// a. Let timeZone be ? ToTemporalTimeZone(item).
time_zone = TRY(to_temporal_time_zone(vm, item));
// b. Let temporalTime be undefined.
}
PlainDateTime* temporal_date_time;
// 5. If temporalTime is undefined, then
if (temporal_time_value.is_undefined()) {
// a. Let temporalDateTime be ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], 0, 0, 0, 0, 0, 0, temporalDate.[[Calendar]]).
temporal_date_time = TRY(create_temporal_date_time(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), 0, 0, 0, 0, 0, 0, temporal_date->calendar()));
}
// 6. Else,
else {
// a. Set temporalTime to ? ToTemporalTime(temporalTime).
auto* temporal_time = TRY(to_temporal_time(vm, temporal_time_value));
// b. Let temporalDateTime be ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]).
temporal_date_time = TRY(create_temporal_date_time(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), temporal_time->calendar()));
}
// 7. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, temporalDateTime, "compatible").
auto instant = TRY(builtin_time_zone_get_instant_for(vm, time_zone, *temporal_date_time, "compatible"sv));
// 8. Return ! CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, temporalDate.[[Calendar]]).
return MUST(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, temporal_date->calendar()));
}
// 3.3.29 Temporal.PlainDate.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_string)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let showCalendar be ? ToCalendarNameOption(options).
auto show_calendar = TRY(to_calendar_name_option(vm, *options));
// 5. Return ? TemporalDateToString(temporalDate, showCalendar).
return PrimitiveString::create(vm, TRY(temporal_date_to_string(vm, temporal_date, show_calendar)));
}
// 3.3.30 Temporal.PlainDate.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tolocalestring
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_locale_string)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Return ? TemporalDateToString(temporalDate, "auto").
return PrimitiveString::create(vm, TRY(temporal_date_to_string(vm, temporal_date, "auto"sv)));
}
// 3.3.31 Temporal.PlainDate.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::to_json)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Return ? TemporalDateToString(temporalDate, "auto").
return PrimitiveString::create(vm, TRY(temporal_date_to_string(vm, temporal_date, "auto"sv)));
}
// 3.3.32 Temporal.PlainDate.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::value_of)
{
// 1. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.PlainDate", "a primitive value");
}
// 3.3.3 get Temporal.PlainDate.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindate.prototype.calendarid
JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::calendar_id_getter)
{
// 1. Let temporalDate be the this value.
// 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]).
auto temporal_date = TRY(typed_this_object(vm));
// 3. Return temporalDate.[[Calendar]].
auto& calendar = static_cast<Calendar&>(temporal_date->calendar());
return PrimitiveString::create(vm, calendar.identifier());
}
}

View file

@ -1,60 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
namespace JS::Temporal {
class PlainDatePrototype final : public PrototypeObject<PlainDatePrototype, PlainDate> {
JS_PROTOTYPE_OBJECT(PlainDatePrototype, PlainDate, Temporal.PlainDate);
GC_DECLARE_ALLOCATOR(PlainDatePrototype);
public:
virtual void initialize(Realm&) override;
virtual ~PlainDatePrototype() override = default;
private:
explicit PlainDatePrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(calendar_getter);
JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter);
JS_DECLARE_NATIVE_FUNCTION(year_getter);
JS_DECLARE_NATIVE_FUNCTION(month_getter);
JS_DECLARE_NATIVE_FUNCTION(month_code_getter);
JS_DECLARE_NATIVE_FUNCTION(day_getter);
JS_DECLARE_NATIVE_FUNCTION(day_of_week_getter);
JS_DECLARE_NATIVE_FUNCTION(day_of_year_getter);
JS_DECLARE_NATIVE_FUNCTION(week_of_year_getter);
JS_DECLARE_NATIVE_FUNCTION(year_of_week_getter);
JS_DECLARE_NATIVE_FUNCTION(days_in_week_getter);
JS_DECLARE_NATIVE_FUNCTION(days_in_month_getter);
JS_DECLARE_NATIVE_FUNCTION(days_in_year_getter);
JS_DECLARE_NATIVE_FUNCTION(months_in_year_getter);
JS_DECLARE_NATIVE_FUNCTION(in_leap_year_getter);
JS_DECLARE_NATIVE_FUNCTION(era_getter);
JS_DECLARE_NATIVE_FUNCTION(era_year_getter);
JS_DECLARE_NATIVE_FUNCTION(to_plain_year_month);
JS_DECLARE_NATIVE_FUNCTION(to_plain_month_day);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(with_calendar);
JS_DECLARE_NATIVE_FUNCTION(until);
JS_DECLARE_NATIVE_FUNCTION(since);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date_time);
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
};
}

View file

@ -1,447 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/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>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainDateTime);
// 5 Temporal.PlainDateTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaindatetime-objects
PlainDateTime::PlainDateTime(i32 iso_year, u8 iso_month, u8 iso_day, u8 iso_hour, u8 iso_minute, u8 iso_second, u16 iso_millisecond, u16 iso_microsecond, u16 iso_nanosecond, Object& calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_iso_year(iso_year)
, m_iso_month(iso_month)
, m_iso_day(iso_day)
, m_iso_hour(iso_hour)
, m_iso_minute(iso_minute)
, m_iso_second(iso_second)
, m_iso_millisecond(iso_millisecond)
, m_iso_microsecond(iso_microsecond)
, m_iso_nanosecond(iso_nanosecond)
, m_calendar(calendar)
{
}
void PlainDateTime::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_calendar);
}
// nsMinInstant - nsPerDay
auto const DATETIME_NANOSECONDS_MIN = "-8640000086400000000000"_sbigint;
// nsMaxInstant + nsPerDay
auto const DATETIME_NANOSECONDS_MAX = "8640000086400000000000"_sbigint;
// 5.5.1 ISODateTimeWithinLimits ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isodatetimewithinlimits
bool iso_date_time_within_limits(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond)
{
// 1. Assert: IsValidISODate(year, month, day) is true.
VERIFY(is_valid_iso_date(year, month, day));
// 2. Let ns be (GetUTCEpochNanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond)).
auto ns = get_utc_epoch_nanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
// 3. If ns ≤ nsMinInstant - nsPerDay, then
if (ns <= DATETIME_NANOSECONDS_MIN) {
// a. Return false.
return false;
}
// 4. If ns ≥ nsMaxInstant + nsPerDay, then
if (ns >= DATETIME_NANOSECONDS_MAX) {
// a. Return false.
return false;
}
// 5. Return true.
return true;
}
// 5.5.2 InterpretTemporalDateTimeFields ( calendar, fields, options ), https://tc39.es/proposal-temporal/#sec-temporal-interprettemporaldatetimefields
ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(VM& vm, Object& calendar, Object& fields, Object const* options)
{
// 1. Let timeResult be ? ToTemporalTimeRecord(fields).
auto unregulated_time_result = TRY(to_temporal_time_record(vm, fields));
// 2. Let overflow be ? ToTemporalOverflow(options).
auto overflow = TRY(to_temporal_overflow(vm, options));
// 3. Let temporalDate be ? CalendarDateFromFields(calendar, fields, options).
auto* temporal_date = TRY(calendar_date_from_fields(vm, calendar, fields, options));
// 4. Let timeResult be ? RegulateTime(timeResult.[[Hour]], timeResult.[[Minute]], timeResult.[[Second]], timeResult.[[Millisecond]], timeResult.[[Microsecond]], timeResult.[[Nanosecond]], overflow).
auto time_result = TRY(regulate_time(vm, *unregulated_time_result.hour, *unregulated_time_result.minute, *unregulated_time_result.second, *unregulated_time_result.millisecond, *unregulated_time_result.microsecond, *unregulated_time_result.nanosecond, overflow));
// 5. Return the Record { [[Year]]: temporalDate.[[ISOYear]], [[Month]]: temporalDate.[[ISOMonth]], [[Day]]: temporalDate.[[ISODay]], [[Hour]]: timeResult.[[Hour]], [[Minute]]: timeResult.[[Minute]], [[Second]]: timeResult.[[Second]], [[Millisecond]]: timeResult.[[Millisecond]], [[Microsecond]]: timeResult.[[Microsecond]], [[Nanosecond]]: timeResult.[[Nanosecond]] }.
return ISODateTime {
.year = temporal_date->iso_year(),
.month = temporal_date->iso_month(),
.day = temporal_date->iso_day(),
.hour = time_result.hour,
.minute = time_result.minute,
.second = time_result.second,
.millisecond = time_result.millisecond,
.microsecond = time_result.microsecond,
.nanosecond = time_result.nanosecond,
};
}
// 5.5.3 ToTemporalDateTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaldatetime
ThrowCompletionOr<PlainDateTime*> to_temporal_date_time(VM& vm, Value item, Object const* options)
{
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
Object* calendar;
ISODateTime result;
// 3. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(item_object)) {
// i. Return item.
return &static_cast<PlainDateTime&>(item_object);
}
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(item_object)) {
auto& zoned_date_time = static_cast<ZonedDateTime&>(item_object);
// i. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// ii. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
auto* instant = create_temporal_instant(vm, zoned_date_time.nanoseconds()).release_value();
// iii. Return ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
return builtin_time_zone_get_plain_date_time_for(vm, &zoned_date_time.time_zone(), *instant, zoned_date_time.calendar());
}
// c. If item has an [[InitializedTemporalDate]] internal slot, then
if (is<PlainDate>(item_object)) {
auto& plain_date = static_cast<PlainDate&>(item_object);
// i. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// ii. Return ? CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], 0, 0, 0, 0, 0, 0, item.[[Calendar]]).
return create_temporal_date_time(vm, plain_date.iso_year(), plain_date.iso_month(), plain_date.iso_day(), 0, 0, 0, 0, 0, 0, plain_date.calendar());
}
// d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
calendar = TRY(get_temporal_calendar_with_iso_default(vm, item_object));
// e. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »).
auto field_names = TRY(calendar_fields(vm, *calendar, { "day"sv, "hour"sv, "microsecond"sv, "millisecond"sv, "minute"sv, "month"sv, "monthCode"sv, "nanosecond"sv, "second"sv, "year"sv }));
// f. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, item_object, field_names, Vector<StringView> {}));
// g. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, options).
result = TRY(interpret_temporal_date_time_fields(vm, *calendar, *fields, options));
}
// 4. Else,
else {
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// b. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// c. Let result be ? ParseTemporalDateTimeString(string).
result = TRY(parse_temporal_date_time_string(vm, string));
// d. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
VERIFY(is_valid_iso_date(result.year, result.month, result.day));
// e. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
VERIFY(is_valid_time(result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
// f. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
calendar = TRY(to_temporal_calendar_with_iso_default(vm, result.calendar.has_value() ? PrimitiveString::create(vm, *result.calendar) : js_undefined()));
}
// 5. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], calendar).
return create_temporal_date_time(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, *calendar);
}
// 5.5.4 BalanceISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisodatetime
ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond)
{
// NOTE: The only use of this AO is in BuiltinTimeZoneGetPlainDateTimeFor, where we know that all values
// but `nanosecond` are in their usual range, hence why that's the only outlier here. The range for that
// is -86400000000000 to 86400000000999, so an i32 is not enough.
// 1. Assert: year, month, day, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Let balancedTime be ! BalanceTime(hour, minute, second, millisecond, microsecond, nanosecond).
auto balanced_time = balance_time(hour, minute, second, millisecond, microsecond, nanosecond);
// 3. Let balancedDate be BalanceISODate(year, month, day + balancedTime.[[Days]]).
auto balanced_date = balance_iso_date(year, month, day + balanced_time.days);
// 4. Return the Record { [[Year]]: balancedDate.[[Year]], [[Month]]: balancedDate.[[Month]], [[Day]]: balancedDate.[[Day]], [[Hour]]: balancedTime.[[Hour]], [[Minute]]: balancedTime.[[Minute]], [[Second]]: balancedTime.[[Second]], [[Millisecond]]: balancedTime.[[Millisecond]], [[Microsecond]]: balancedTime.[[Microsecond]], [[Nanosecond]]: balancedTime.[[Nanosecond]] }.
return ISODateTime { .year = balanced_date.year, .month = balanced_date.month, .day = balanced_date.day, .hour = balanced_time.hour, .minute = balanced_time.minute, .second = balanced_time.second, .millisecond = balanced_time.millisecond, .microsecond = balanced_time.microsecond, .nanosecond = balanced_time.nanosecond };
}
// 5.5.5 CreateTemporalDateTime ( isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaldatetime
ThrowCompletionOr<PlainDateTime*> create_temporal_date_time(VM& vm, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Assert: Type(calendar) is Object.
// 3. 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);
// 4. 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);
// 5. If ISODateTimeWithinLimits(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond) is false, then
if (!iso_date_time_within_limits(iso_year, iso_month, iso_day, hour, minute, second, millisecond, microsecond, nanosecond)) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDateTime);
}
// 6. If newTarget is not present, set newTarget to %Temporal.PlainDateTime%.
if (!new_target)
new_target = realm.intrinsics().temporal_plain_date_time_constructor();
// 7. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainDateTime.prototype%", « [[InitializedTemporalDateTime]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »).
// 8. Set object.[[ISOYear]] to isoYear.
// 9. Set object.[[ISOMonth]] to isoMonth.
// 10. Set object.[[ISODay]] to isoDay.
// 11. Set object.[[ISOHour]] to hour.
// 12. Set object.[[ISOMinute]] to minute.
// 13. Set object.[[ISOSecond]] to second.
// 14. Set object.[[ISOMillisecond]] to millisecond.
// 15. Set object.[[ISOMicrosecond]] to microsecond.
// 16. Set object.[[ISONanosecond]] to nanosecond.
// 17. Set object.[[Calendar]] to calendar.
auto object = TRY(ordinary_create_from_constructor<PlainDateTime>(vm, *new_target, &Intrinsics::temporal_plain_date_prototype, iso_year, iso_month, iso_day, hour, minute, second, millisecond, microsecond, nanosecond, calendar));
// 18. Return object.
return object.ptr();
}
// 5.5.6 TemporalDateTimeToString ( isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar, precision, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporaldatetimetostring
ThrowCompletionOr<String> temporal_date_time_to_string(VM& vm, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object const* calendar, Variant<StringView, u8> const& precision, StringView show_calendar)
{
// 1. Assert: isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Let year be ! PadISOYear(isoYear).
// 3. Let month be ToZeroPaddedDecimalString(isoMonth, 2).
// 4. Let day be ToZeroPaddedDecimalString(isoDay, 2).
// 5. Let hour be ToZeroPaddedDecimalString(hour, 2).
// 6. Let minute be ToZeroPaddedDecimalString(minute, 2).
// 7. Let seconds be ! FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision).
auto seconds = MUST_OR_THROW_OOM(format_seconds_string_part(vm, second, millisecond, microsecond, nanosecond, precision));
// 8. Let calendarString be ? MaybeFormatCalendarAnnotation(calendar, showCalendar).
auto calendar_string = TRY(maybe_format_calendar_annotation(vm, calendar, show_calendar));
// 9. Return the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), month, the code unit 0x002D (HYPHEN-MINUS), day, 0x0054 (LATIN CAPITAL LETTER T), hour, the code unit 0x003A (COLON), minute, seconds, and calendarString.
return TRY_OR_THROW_OOM(vm, String::formatted("{}-{:02}-{:02}T{:02}:{:02}{}{}", MUST_OR_THROW_OOM(pad_iso_year(vm, iso_year)), iso_month, iso_day, hour, minute, seconds, calendar_string));
}
// 5.5.7 CompareISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2 ), https://tc39.es/proposal-temporal/#sec-temporal-compareisodatetime
i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2)
{
// 1. Assert: y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, and ns2 are integers.
// 2. Let dateResult be ! CompareISODate(y1, mon1, d1, y2, mon2, d2).
auto date_result = compare_iso_date(year1, month1, day1, year2, month2, day2);
// 3. If dateResult is not 0, then
if (date_result != 0) {
// a. Return dateResult.
return date_result;
}
// 4. Return ! CompareTemporalTime(h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, ns2).
return compare_temporal_time(hour1, minute1, second1, millisecond1, microsecond1, nanosecond1, hour2, minute2, second2, millisecond2, microsecond2, nanosecond2);
}
// 5.5.8 AddDateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddatetime
ThrowCompletionOr<TemporalPlainDateTime> add_date_time(VM& vm, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options)
{
// 1. Assert: ISODateTimeWithinLimits(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) is true.
VERIFY(iso_date_time_within_limits(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond));
// 2. Let timeResult be ! AddTime(hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
auto time_result = add_time(hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
// 3. Let datePart be ! CreateTemporalDate(year, month, day, calendar).
auto* date_part = MUST(create_temporal_date(vm, year, month, day, calendar));
// 4. Let dateDuration be ? CreateTemporalDuration(years, months, weeks, days + timeResult.[[Days]], 0, 0, 0, 0, 0, 0).
auto date_duration = TRY(create_temporal_duration(vm, years, months, weeks, days + time_result.days, 0, 0, 0, 0, 0, 0));
// 5. Let addedDate be ? CalendarDateAdd(calendar, datePart, dateDuration, options).
auto* added_date = TRY(calendar_date_add(vm, calendar, date_part, *date_duration, options));
// 6. Return the Record { [[Year]]: addedDate.[[ISOYear]], [[Month]]: addedDate.[[ISOMonth]], [[Day]]: addedDate.[[ISODay]], [[Hour]]: timeResult.[[Hour]], [[Minute]]: timeResult.[[Minute]], [[Second]]: timeResult.[[Second]], [[Millisecond]]: timeResult.[[Millisecond]], [[Microsecond]]: timeResult.[[Microsecond]], [[Nanosecond]]: timeResult.[[Nanosecond]] }.
return TemporalPlainDateTime { .year = added_date->iso_year(), .month = added_date->iso_month(), .day = added_date->iso_day(), .hour = time_result.hour, .minute = time_result.minute, .second = time_result.second, .millisecond = time_result.millisecond, .microsecond = time_result.microsecond, .nanosecond = time_result.nanosecond };
}
// 5.5.9 RoundISODateTime ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, roundingMode [ , dayLength ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundisodatetime
ISODateTime round_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length)
{
// 1. Assert: year, month, day, hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Assert: ISODateTimeWithinLimits(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) is true.
VERIFY(iso_date_time_within_limits(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond));
// 3. If dayLength is not present, set dayLength to nsPerDay.
if (!day_length.has_value())
day_length = ns_per_day;
// 4. Let roundedTime be ! RoundTime(hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, roundingMode, dayLength).
auto rounded_time = round_time(hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, rounding_mode, day_length);
// 5. Let balanceResult be BalanceISODate(year, month, day + roundedTime.[[Days]]).
auto balance_result = balance_iso_date(year, month, day + rounded_time.days);
// 6. Return the Record { [[Year]]: balanceResult.[[Year]], [[Month]]: balanceResult.[[Month]], [[Day]]: balanceResult.[[Day]], [[Hour]]: roundedTime.[[Hour]], [[Minute]]: roundedTime.[[Minute]], [[Second]]: roundedTime.[[Second]], [[Millisecond]]: roundedTime.[[Millisecond]], [[Microsecond]]: roundedTime.[[Microsecond]], [[Nanosecond]]: roundedTime.[[Nanosecond]] }.
return ISODateTime { .year = balance_result.year, .month = balance_result.month, .day = balance_result.day, .hour = rounded_time.hour, .minute = rounded_time.minute, .second = rounded_time.second, .millisecond = rounded_time.millisecond, .microsecond = rounded_time.microsecond, .nanosecond = rounded_time.nanosecond };
}
// 5.5.10 DifferenceISODateTime ( y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1, y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2, calendar, largestUnit, options ), https://tc39.es/proposal-temporal/#sec-temporal-differenceisodatetime
ThrowCompletionOr<DurationRecord> difference_iso_date_time(VM& vm, i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2, Object& calendar, StringView largest_unit, Object const& options)
{
// 1. Assert: ISODateTimeWithinLimits(y1, mon1, d1, h1, min1, s1, ms1, mus1, ns1) is true.
VERIFY(iso_date_time_within_limits(year1, month1, day1, hour1, minute1, second1, millisecond1, microsecond1, nanosecond1));
// 2. Assert: ISODateTimeWithinLimits(y2, mon2, d2, h2, min2, s2, ms2, mus2, ns2) is true.
VERIFY(iso_date_time_within_limits(year2, month2, day2, hour2, minute2, second2, millisecond2, microsecond2, nanosecond2));
// 3. Let timeDifference be ! DifferenceTime(h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, ns2).
auto time_difference = difference_time(vm, hour1, minute1, second1, millisecond1, microsecond1, nanosecond1, hour2, minute2, second2, millisecond2, microsecond2, nanosecond2);
// 4. Let timeSign be ! DurationSign(0, 0, 0, 0, timeDifference.[[Hours]], timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]]).
auto time_sign = duration_sign(0, 0, 0, 0, time_difference.hours, time_difference.minutes, time_difference.seconds, time_difference.milliseconds, time_difference.microseconds, time_difference.nanoseconds);
// 5. Let dateSign be ! CompareISODate(y2, mon2, d2, y1, mon1, d1).
auto date_sign = compare_iso_date(year2, month2, day2, year1, month1, day1);
// 6. Let adjustedDate be CreateISODateRecord(y1, mon1, d1).
auto adjusted_date = create_iso_date_record(year1, month1, day1);
// 7. If timeSign is -dateSign, then
if (time_sign == -date_sign) {
// a. Set adjustedDate to BalanceISODate(adjustedDate.[[Year]], adjustedDate.[[Month]], adjustedDate.[[Day]] - timeSign).
adjusted_date = balance_iso_date(adjusted_date.year, adjusted_date.month, adjusted_date.day - time_sign);
// b. Set timeDifference to ! BalanceDuration(-timeSign, timeDifference.[[Hours]], timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], largestUnit).
time_difference = MUST(balance_duration(vm, -time_sign, time_difference.hours, time_difference.minutes, time_difference.seconds, time_difference.milliseconds, time_difference.microseconds, Crypto::SignedBigInteger { time_difference.nanoseconds }, largest_unit));
}
// 8. Let date1 be ! CreateTemporalDate(adjustedDate.[[Year]], adjustedDate.[[Month]], adjustedDate.[[Day]], calendar).
auto* date1 = MUST(create_temporal_date(vm, adjusted_date.year, adjusted_date.month, adjusted_date.day, calendar));
// 9. Let date2 be ! CreateTemporalDate(y2, mon2, d2, calendar).
auto* date2 = MUST(create_temporal_date(vm, year2, month2, day2, calendar));
// 10. Let dateLargestUnit be ! LargerOfTwoTemporalUnits("day", largestUnit).
auto date_largest_unit = larger_of_two_temporal_units("day"sv, largest_unit);
// 11. Let untilOptions be ? MergeLargestUnitOption(options, dateLargestUnit).
auto* until_options = TRY(merge_largest_unit_option(vm, options, TRY_OR_THROW_OOM(vm, String::from_utf8(date_largest_unit))));
// 12. Let dateDifference be ? CalendarDateUntil(calendar, date1, date2, untilOptions).
// FIXME: AD-HOC calendar records use as this AO is not up to date with latest spec
auto calendar_record = TRY(create_calendar_methods_record(vm, GC::Ref<Object> { calendar }, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
auto date_difference = TRY(calendar_date_until(vm, calendar_record, date1, date2, *until_options));
// 13. Let balanceResult be ? BalanceDuration(dateDifference.[[Days]], timeDifference.[[Hours]], timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]], largestUnit).
auto balance_result = TRY(balance_duration(vm, date_difference->days(), time_difference.hours, time_difference.minutes, time_difference.seconds, time_difference.milliseconds, time_difference.microseconds, Crypto::SignedBigInteger { time_difference.nanoseconds }, largest_unit));
// 14. Return ! CreateDurationRecord(dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]]).
return create_duration_record(date_difference->years(), date_difference->months(), date_difference->weeks(), balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds);
}
// 5.5.11 DifferenceTemporalPlainDateTime ( operation, dateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindatetime
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_date_time(VM& vm, DifferenceOperation operation, PlainDateTime& date_time, Value other_value, Value options_value)
{
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalDateTime(other).
auto* other = TRY(to_temporal_date_time(vm, other_value));
// 3. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception.
if (!TRY(calendar_equals(vm, date_time.calendar(), other->calendar())))
return vm.throw_completion<RangeError>(ErrorType::TemporalDifferentCalendars);
// 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « », "nanosecond", "day").
auto settings = TRY(get_difference_settings(vm, operation, options_value, UnitGroup::DateTime, {}, { "nanosecond"sv }, "day"sv));
// 5. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], settings.[[LargestUnit]], settings.[[Options]]).
auto diff = TRY(difference_iso_date_time(vm, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time.calendar(), settings.largest_unit, settings.options));
// 6. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
auto* relative_to = MUST(create_temporal_date(vm, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.calendar()));
// 7. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], relativeTo)).[[DurationRecord]].
// FIXME: AD-HOC calendar records use as this AO is not up to date with latest spec
auto calendar_record = TRY(create_calendar_methods_record(vm, GC::Ref<Object> { date_time.calendar() }, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } }));
auto round_result = TRY(round_duration(vm, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, relative_to, calendar_record)).duration_record;
// 8. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], settings.[[LargestUnit]]).
auto result = MUST(balance_duration(vm, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger { round_result.nanoseconds }, settings.largest_unit));
// 9. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(vm, sign * round_result.years, sign * round_result.months, sign * round_result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
// 5.5.12 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaindatetime
ThrowCompletionOr<PlainDateTime*> add_duration_to_or_subtract_duration_from_plain_date_time(VM& vm, ArithmeticOperation operation, PlainDateTime& date_time, Value temporal_duration_like, Value options_value)
{
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == ArithmeticOperation::Subtract ? -1 : 1;
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
auto duration = TRY(to_temporal_duration_record(vm, temporal_duration_like));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, options_value));
// 4. Let result be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]], options).
auto result = TRY(add_date_time(vm, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), date_time.calendar(), sign * duration.years, sign * duration.months, sign * duration.weeks, sign * duration.days, sign * duration.hours, sign * duration.minutes, sign * duration.seconds, sign * duration.milliseconds, sign * duration.microseconds, sign * duration.nanoseconds, options));
// 5. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
VERIFY(is_valid_iso_date(result.year, result.month, result.day));
// 6. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
VERIFY(is_valid_time(result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
// 7. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
return create_temporal_date_time(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, date_time.calendar());
}
}

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Variant.h>
#include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Duration.h>
namespace JS::Temporal {
class PlainDateTime final : public Object {
JS_OBJECT(PlainDateTime, Object);
GC_DECLARE_ALLOCATOR(PlainDateTime);
public:
virtual ~PlainDateTime() override = default;
[[nodiscard]] i32 iso_year() const { return m_iso_year; }
[[nodiscard]] u8 iso_month() const { return m_iso_month; }
[[nodiscard]] u8 iso_day() const { return m_iso_day; }
[[nodiscard]] u8 iso_hour() const { return m_iso_hour; }
[[nodiscard]] u8 iso_minute() const { return m_iso_minute; }
[[nodiscard]] u8 iso_second() const { return m_iso_second; }
[[nodiscard]] u16 iso_millisecond() const { return m_iso_millisecond; }
[[nodiscard]] u16 iso_microsecond() const { return m_iso_microsecond; }
[[nodiscard]] u16 iso_nanosecond() const { return m_iso_nanosecond; }
[[nodiscard]] Object const& calendar() const { return m_calendar; }
[[nodiscard]] Object& calendar() { return m_calendar; }
private:
PlainDateTime(i32 iso_year, u8 iso_month, u8 iso_day, u8 iso_hour, u8 iso_minute, u8 iso_second, u16 iso_millisecond, u16 iso_microsecond, u16 iso_nanosecond, Object& calendar, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 5.4 Properties of Temporal.PlainDateTime Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plaindatetime-instances
i32 m_iso_year { 0 }; // [[ISOYear]]
u8 m_iso_month { 0 }; // [[ISOMonth]]
u8 m_iso_day { 0 }; // [[ISODay]]
u8 m_iso_hour { 0 }; // [[ISOHour]]
u8 m_iso_minute { 0 }; // [[ISOMinute]]
u8 m_iso_second { 0 }; // [[ISOSecond]]
u16 m_iso_millisecond { 0 }; // [[ISOMillisecond]]
u16 m_iso_microsecond { 0 }; // [[ISOMicrosecond]]
u16 m_iso_nanosecond { 0 }; // [[ISONanosecond]]
GC::Ref<Object> m_calendar; // [[Calendar]]
};
// Used by AddDateTime to temporarily hold values
struct TemporalPlainDateTime {
i32 year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u16 millisecond;
u16 microsecond;
u16 nanosecond;
};
bool iso_date_time_within_limits(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond);
ThrowCompletionOr<ISODateTime> interpret_temporal_date_time_fields(VM&, Object& calendar, Object& fields, Object const* options);
ThrowCompletionOr<PlainDateTime*> to_temporal_date_time(VM&, Value item, Object const* options = nullptr);
ISODateTime balance_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, i64 nanosecond);
ThrowCompletionOr<PlainDateTime*> create_temporal_date_time(VM&, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<String> temporal_date_time_to_string(VM&, i32 iso_year, u8 iso_month, u8 iso_day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object const* calendar, Variant<StringView, u8> const& precision, StringView show_calendar);
i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
ThrowCompletionOr<TemporalPlainDateTime> add_date_time(VM&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options);
ISODateTime round_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length = {});
ThrowCompletionOr<DurationRecord> difference_iso_date_time(VM&, i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2, Object& calendar, StringView largest_unit, Object const& options);
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_date_time(VM&, DifferenceOperation, PlainDateTime&, Value other, Value options);
ThrowCompletionOr<PlainDateTime*> add_duration_to_or_subtract_duration_from_plain_date_time(VM&, ArithmeticOperation, PlainDateTime&, Value temporal_duration_like, Value options_value);
}

View file

@ -1,132 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.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/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainDateTimeConstructor.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 [ , calendarLike ] ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime
ThrowCompletionOr<Value> PlainDateTimeConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, 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 [ , calendarLike ] ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime
ThrowCompletionOr<GC::Ref<Object>> PlainDateTimeConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Let isoYear be ? ToIntegerWithTruncation(isoYear).
auto iso_year = TRY(to_integer_with_truncation(vm, vm.argument(0), ErrorType::TemporalInvalidPlainDateTime));
// 3. Let isoMonth be ? ToIntegerWithTruncation(isoMonth).
auto iso_month = TRY(to_integer_with_truncation(vm, vm.argument(1), ErrorType::TemporalInvalidPlainDateTime));
// 4. Let isoDay be ? ToIntegerWithTruncation(isoDay).
auto iso_day = TRY(to_integer_with_truncation(vm, vm.argument(2), ErrorType::TemporalInvalidPlainDateTime));
// 5. Let hour be ? ToIntegerWithTruncation(hour).
auto hour = TRY(to_integer_with_truncation(vm, vm.argument(3), ErrorType::TemporalInvalidPlainDateTime));
// 6. Let minute be ? ToIntegerWithTruncation(minute).
auto minute = TRY(to_integer_with_truncation(vm, vm.argument(4), ErrorType::TemporalInvalidPlainDateTime));
// 7. Let second be ? ToIntegerWithTruncation(second).
auto second = TRY(to_integer_with_truncation(vm, vm.argument(5), ErrorType::TemporalInvalidPlainDateTime));
// 8. Let millisecond be ? ToIntegerWithTruncation(millisecond).
auto millisecond = TRY(to_integer_with_truncation(vm, vm.argument(6), ErrorType::TemporalInvalidPlainDateTime));
// 9. Let microsecond be ? ToIntegerWithTruncation(microsecond).
auto microsecond = TRY(to_integer_with_truncation(vm, vm.argument(7), ErrorType::TemporalInvalidPlainDateTime));
// 10. Let nanosecond be ? ToIntegerWithTruncation(nanosecond).
auto nanosecond = TRY(to_integer_with_truncation(vm, vm.argument(8), ErrorType::TemporalInvalidPlainDateTime));
// 11. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, vm.argument(9)));
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to CreateTemporalDateTime will immediately check that these values are valid
// ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31, for hours: 0 - 23, for minutes and seconds: 0 - 59,
// milliseconds, microseconds, and nanoseconds: 0 - 999) all of which are subsets of this check.
if (!AK::is_within_range<i32>(iso_year) || !AK::is_within_range<u8>(iso_month) || !AK::is_within_range<u8>(iso_day) || !AK::is_within_range<u8>(hour) || !AK::is_within_range<u8>(minute) || !AK::is_within_range<u8>(second) || !AK::is_within_range<u16>(millisecond) || !AK::is_within_range<u16>(microsecond) || !AK::is_within_range<u16>(nanosecond))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainDateTime);
// 12. Return ? CreateTemporalDateTime(isoYear, isoMonth, isoDay, hour, minute, second, millisecond, microsecond, nanosecond, calendar, NewTarget).
return *TRY(create_temporal_date_time(vm, iso_year, iso_month, iso_day, hour, minute, second, millisecond, microsecond, nanosecond, *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)
{
auto item = vm.argument(0);
// 1. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 2. If Type(item) is Object and item has an [[InitializedTemporalDateTime]] internal slot, then
if (item.is_object() && is<PlainDateTime>(item.as_object())) {
auto& plain_date_time = static_cast<PlainDateTime&>(item.as_object());
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// b. Return ! CreateTemporalDateTime(item.[[ISOYear]], item.[[ISOMonth]], item.[[ISODay]], item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]], item.[[Calendar]]).
return MUST(create_temporal_date_time(vm, plain_date_time.iso_year(), plain_date_time.iso_month(), plain_date_time.iso_day(), plain_date_time.iso_hour(), plain_date_time.iso_minute(), plain_date_time.iso_second(), plain_date_time.iso_millisecond(), plain_date_time.iso_microsecond(), plain_date_time.iso_nanosecond(), plain_date_time.calendar()));
}
// 3. Return ? ToTemporalDateTime(item, options).
return TRY(to_temporal_date_time(vm, item, options));
}
// 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.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], one.[[ISOHour]], one.[[ISOMinute]], one.[[ISOSecond]], one.[[ISOMillisecond]], one.[[ISOMicrosecond]], one.[[ISONanosecond]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]], two.[[ISOHour]], two.[[ISOMinute]], two.[[ISOSecond]], two.[[ISOMillisecond]], two.[[ISOMicrosecond]], two.[[ISONanosecond]])).
return Value(compare_iso_date_time(one->iso_year(), one->iso_month(), one->iso_day(), one->iso_hour(), one->iso_minute(), one->iso_second(), one->iso_millisecond(), one->iso_microsecond(), one->iso_nanosecond(), two->iso_year(), two->iso_month(), two->iso_day(), two->iso_hour(), two->iso_minute(), two->iso_second(), two->iso_millisecond(), two->iso_microsecond(), two->iso_nanosecond()));
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.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

@ -1,822 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#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/Duration.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainDateTimePrototype.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.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[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype-@@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.calendar, calendar_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.calendarId, calendar_id_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);
define_native_accessor(realm, vm.names.era, era_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.eraYear, era_year_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.with, with, 1, attr);
define_native_function(realm, vm.names.withPlainTime, with_plain_time, 0, attr);
define_native_function(realm, vm.names.withPlainDate, with_plain_date, 1, attr);
define_native_function(realm, vm.names.withCalendar, with_calendar, 1, attr);
define_native_function(realm, vm.names.add, add, 1, attr);
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
define_native_function(realm, vm.names.until, until, 1, attr);
define_native_function(realm, vm.names.since, since, 1, attr);
define_native_function(realm, vm.names.round, round, 1, attr);
define_native_function(realm, vm.names.equals, equals, 1, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
define_native_function(realm, vm.names.toZonedDateTime, to_zoned_date_time, 1, attr);
define_native_function(realm, vm.names.toPlainDate, to_plain_date, 0, attr);
define_native_function(realm, vm.names.toPlainYearMonth, to_plain_year_month, 0, attr);
define_native_function(realm, vm.names.toPlainMonthDay, to_plain_month_day, 0, attr);
define_native_function(realm, vm.names.toPlainTime, to_plain_time, 0, attr);
define_native_function(realm, vm.names.getISOFields, get_iso_fields, 0, attr);
}
// 5.3.3 get Temporal.PlainDateTime.prototype.calendar, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.calendar
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::calendar_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 Value(&date_time->calendar());
}
// 5.3.4 get Temporal.PlainDateTime.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.year
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::year_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarYear(calendar, dateTime)).
return TRY(calendar_year(vm, calendar, date_time));
}
// 5.3.5 get Temporal.PlainDateTime.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.month
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::month_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarMonth(calendar, dateTime)).
return TRY(calendar_month(vm, calendar, date_time));
}
// 5.3.6 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. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return ? CalendarMonthCode(calendar, dateTime).
return PrimitiveString::create(vm, TRY(calendar_month_code(vm, calendar, date_time)));
}
// 5.3.7 get Temporal.PlainDateTime.prototype.day, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.day
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::day_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarDay(calendar, dateTime)).
return TRY(calendar_day(vm, calendar, date_time));
}
// 5.3.8 get Temporal.PlainDateTime.prototype.hour, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.hour
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::hour_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(dateTime.[[ISOHour]]).
return Value(date_time->iso_hour());
}
// 5.3.9 get Temporal.PlainDateTime.prototype.minute, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.minute
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::minute_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(dateTime.[[ISOMinute]]).
return Value(date_time->iso_minute());
}
// 5.3.10 get Temporal.PlainDateTime.prototype.second, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.second
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::second_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(dateTime.[[ISOSecond]]).
return Value(date_time->iso_second());
}
// 5.3.11 get Temporal.PlainDateTime.prototype.millisecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.millisecond
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::millisecond_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(dateTime.[[ISOMillisecond]]).
return Value(date_time->iso_millisecond());
}
// 5.3.12 get Temporal.PlainDateTime.prototype.microsecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.microsecond
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::microsecond_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(dateTime.[[ISOMicrosecond]]).
return Value(date_time->iso_microsecond());
}
// 5.3.13 get Temporal.PlainDateTime.prototype.nanosecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.nanosecond
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::nanosecond_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(dateTime.[[ISONanosecond]]).
return Value(date_time->iso_nanosecond());
}
// 5.3.14 get Temporal.PlainDateTime.prototype.dayOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofweek
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::day_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 calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarDayOfWeek(calendar, dateTime)).
return TRY(calendar_day_of_week(vm, calendar, date_time));
}
// 5.3.15 get Temporal.PlainDateTime.prototype.dayOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.dayofyear
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::day_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 calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarDayOfYear(calendar, dateTime)).
return TRY(calendar_day_of_year(vm, calendar, date_time));
}
// 5.3.16 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 calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarWeekOfYear(calendar, dateTime)).
return TRY(calendar_week_of_year(vm, calendar, date_time));
}
// 5.3.17 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 calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarYearOfWeek(calendar, dateTime)).
return TRY(calendar_year_of_week(vm, calendar, date_time));
}
// 5.3.18 get Temporal.PlainDateTime.prototype.daysInWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinweek
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::days_in_week_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarDaysInWeek(calendar, dateTime)).
return TRY(calendar_days_in_week(vm, calendar, date_time));
}
// 5.3.19 get Temporal.PlainDateTime.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinmonth
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::days_in_month_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarDaysInMonth(calendar, dateTime)).
return TRY(calendar_days_in_month(vm, calendar, date_time));
}
// 5.3.20 get Temporal.PlainDateTime.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.daysinyear
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::days_in_year_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarDaysInYear(calendar, dateTime)).
return TRY(calendar_days_in_year(vm, calendar, date_time));
}
// 5.3.21 get Temporal.PlainDateTime.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.monthsinyear
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::months_in_year_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return 𝔽(? CalendarMonthsInYear(calendar, dateTime)).
return TRY(calendar_months_in_year(vm, calendar, date_time));
}
// 5.3.22 get Temporal.PlainDateTime.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plaindatetime.prototype.inleapyear
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::in_leap_year_getter)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Return ? CalendarInLeapYear(calendar, dateTime).
return TRY(calendar_in_leap_year(vm, calendar, date_time));
}
// 15.6.6.2 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 plain_date_time = TRY(typed_this_object(vm));
// 3. Let calendar be plainDateTime.[[Calendar]].
auto& calendar = plain_date_time->calendar();
// 4. Return ? CalendarEra(calendar, plainDateTime).
return TRY(calendar_era(vm, calendar, plain_date_time));
}
// 15.6.6.3 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 plain_date_time = TRY(typed_this_object(vm));
// 3. Let calendar be plainDateTime.[[Calendar]].
auto& calendar = plain_date_time->calendar();
// 4. Return ? CalendarEraYear(calendar, plainDateTime).
return TRY(calendar_era_year(vm, calendar, plain_date_time));
}
// 5.3.23 Temporal.PlainDateTime.prototype.with ( temporalDateTimeLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.with
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::with)
{
auto temporal_date_time_like = vm.argument(0);
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. If Type(temporalDateTimeLike) is not Object, then
if (!temporal_date_time_like.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, temporal_date_time_like.to_string_without_side_effects());
}
// 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalDateTimeLike).
TRY(reject_object_with_calendar_or_time_zone(vm, temporal_date_time_like.as_object()));
// 5. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 6. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "day"sv, "hour"sv, "microsecond"sv, "millisecond"sv, "minute"sv, "month"sv, "monthCode"sv, "nanosecond"sv, "second"sv, "year"sv }));
// 7. Let partialDateTime be ? PrepareTemporalFields(temporalDateTimeLike, fieldNames, partial).
auto* partial_date_time = TRY(prepare_temporal_fields(vm, temporal_date_time_like.as_object(), field_names, PrepareTemporalFieldsPartial {}));
// 8. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 9. Let fields be ? PrepareTemporalFields(dateTime, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, date_time, field_names, Vector<StringView> {}));
// 10. Set fields to ? CalendarMergeFields(calendar, fields, partialDateTime).
fields = TRY(calendar_merge_fields(vm, calendar, *fields, *partial_date_time));
// 11. Set fields to ? PrepareTemporalFields(fields, fieldNames, «»).
fields = TRY(prepare_temporal_fields(vm, *fields, field_names, Vector<StringView> {}));
// 12. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, options).
auto result = TRY(interpret_temporal_date_time_fields(vm, calendar, *fields, options));
// 13. Assert: IsValidISODate(result.[[Year]], result.[[Month]], result.[[Day]]) is true.
VERIFY(is_valid_iso_date(result.year, result.month, result.day));
// 14. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
VERIFY(is_valid_time(result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
// 15. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], calendar).
return TRY(create_temporal_date_time(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, calendar));
}
// 5.3.24 Temporal.PlainDateTime.prototype.withPlainTime ( [ plainTimeLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withplaintime
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::with_plain_time)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. If plainTimeLike is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Return ? CreateTemporalDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], 0, 0, 0, 0, 0, 0, dateTime.[[Calendar]]).
return TRY(create_temporal_date_time(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), 0, 0, 0, 0, 0, 0, date_time->calendar()));
}
// 4. Let plainTime be ? ToTemporalTime(plainTimeLike).
auto* plain_time = TRY(to_temporal_time(vm, vm.argument(0)));
// 5. Return ? CreateTemporalDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], plainTime.[[ISOHour]], plainTime.[[ISOMinute]], plainTime.[[ISOSecond]], plainTime.[[ISOMillisecond]], plainTime.[[ISOMicrosecond]], plainTime.[[ISONanosecond]], dateTime.[[Calendar]]).
return TRY(create_temporal_date_time(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), plain_time->iso_hour(), plain_time->iso_minute(), plain_time->iso_second(), plain_time->iso_millisecond(), plain_time->iso_microsecond(), plain_time->iso_nanosecond(), date_time->calendar()));
}
// 5.3.25 Temporal.PlainDateTime.prototype.withPlainDate ( plainDateLike ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withplaindate
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::with_plain_date)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let plainDate be ? ToTemporalDate(plainDateLike).
auto* plain_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 4. Let calendar be ? ConsolidateCalendars(dateTime.[[Calendar]], plainDate.[[Calendar]]).
auto* calendar = TRY(consolidate_calendars(vm, date_time->calendar(), plain_date->calendar()));
// 5. Return ? CreateTemporalDateTime(plainDate.[[ISOYear]], plainDate.[[ISOMonth]], plainDate.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], calendar).
return TRY(create_temporal_date_time(vm, plain_date->iso_year(), plain_date->iso_month(), plain_date->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), *calendar));
}
// 5.3.26 Temporal.PlainDateTime.prototype.withCalendar ( calendarLike ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.withcalendar
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::with_calendar)
{
auto calendar_like = vm.argument(0);
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be ? ToTemporalCalendar(calendarLike).
auto* calendar = TRY(to_temporal_calendar(vm, calendar_like));
// 4. Return ? CreateTemporalDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], calendar).
return TRY(create_temporal_date_time(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), *calendar));
}
// 5.3.27 Temporal.PlainDateTime.prototype.add ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.add
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::add)
{
auto temporal_duration_like = vm.argument(0);
auto options = vm.argument(1);
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromPlainDateTime(add, dateTime, temporalDurationLike, options).
return TRY(add_duration_to_or_subtract_duration_from_plain_date_time(vm, ArithmeticOperation::Add, date_time, temporal_duration_like, options));
}
// 5.3.28 Temporal.PlainDateTime.prototype.subtract ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::subtract)
{
auto temporal_duration_like = vm.argument(0);
auto options = vm.argument(1);
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromPlainDateTime(subtract, dateTime, temporalDurationLike, options).
return TRY(add_duration_to_or_subtract_duration_from_plain_date_time(vm, ArithmeticOperation::Subtract, date_time, temporal_duration_like, options));
}
// 5.3.29 Temporal.PlainDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::until)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainDateTime(until, dateTime, other, options).
return TRY(difference_temporal_plain_date_time(vm, DifferenceOperation::Until, date_time, other, options));
}
// 5.3.30 Temporal.PlainDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::since)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainDateTime(since, dateTime, other, options).
return TRY(difference_temporal_plain_date_time(vm, DifferenceOperation::Since, date_time, other, options));
}
// 5.3.31 Temporal.PlainDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::round)
{
auto& realm = *vm.current_realm();
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
}
Object* round_to;
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// b. Set roundTo to OrdinaryObjectCreate(null).
round_to = Object::create(realm, nullptr);
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(vm, vm.argument(0)));
}
// 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required, « "day" »).
auto smallest_unit = TRY(get_temporal_unit(vm, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}, { "day"sv }));
// 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *round_to, "halfExpand"sv));
// 8. If smallestUnit is "day", then
Optional<u16> maximum;
if (smallest_unit == "day"sv) {
// a. Let maximum be 1.
maximum = 1;
}
// 9. Else
else {
// a. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit)
maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// b. Assert: maximum is not undefined
VERIFY(maximum.has_value());
}
// 10. Let roundingIncrement be ? ToTemporalDateTimeRoundingIncrement(roundTo).
auto rounding_increment = TRY(to_temporal_rounding_increment(vm, *round_to));
// 11. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
TRY(validate_temporal_rounding_increment(vm, rounding_increment, *maximum, false));
// 12. Let result be ! RoundISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).
auto result = round_iso_date_time(date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), rounding_increment, *smallest_unit, rounding_mode);
// 13. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]]).
return TRY(create_temporal_date_time(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, date_time->calendar()));
}
// 5.3.32 Temporal.PlainDateTime.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::equals)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Set other to ? ToTemporalDateTime(other).
auto* other = TRY(to_temporal_date_time(vm, vm.argument(0)));
// 4. Let result be ! CompareISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
auto result = compare_iso_date_time(date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond());
// 5. If result is not 0, return false.
if (result != 0)
return Value(false);
// 6. Return ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]).
return Value(TRY(calendar_equals(vm, date_time->calendar(), other->calendar())));
}
// 5.3.33 Temporal.PlainDateTime.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_string)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let precision be ? ToSecondsStringPrecisionRecord(options).
auto precision = TRY(to_seconds_string_precision_record(vm, *options));
// 5. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *options, "trunc"sv));
// 6. Let showCalendar be ? ToCalendarNameOption(options).
auto show_calendar = TRY(to_calendar_name_option(vm, *options));
// 7. Let result be ! RoundISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], precision.[[Increment]], precision.[[Unit]], roundingMode).
auto result = round_iso_date_time(date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), precision.increment, precision.unit, rounding_mode);
// 8. Return ? TemporalDateTimeToString(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], dateTime.[[Calendar]], precision.[[Precision]], showCalendar).
return PrimitiveString::create(vm, TRY(temporal_date_time_to_string(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, &date_time->calendar(), precision.precision, show_calendar)));
}
// 5.3.34 Temporal.PlainDateTime.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tolocalestring
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_locale_string)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ? TemporalDateTimeToString(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], "auto", "auto").
return PrimitiveString::create(vm, TRY(temporal_date_time_to_string(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), &date_time->calendar(), "auto"sv, "auto"sv)));
}
// 5.3.35 Temporal.PlainDateTime.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_json)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ? TemporalDateTimeToString(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], "auto", "auto").
return PrimitiveString::create(vm, TRY(temporal_date_time_to_string(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond(), &date_time->calendar(), "auto"sv, "auto"sv)));
}
// 5.3.36 Temporal.PlainDateTime.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::value_of)
{
// 1. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.PlainDateTime", "a primitive value");
}
// 5.3.37 Temporal.PlainDateTime.prototype.toZonedDateTime ( temporalTimeZoneLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.tozoneddatetime
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_zoned_date_time)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
auto* time_zone = TRY(to_temporal_time_zone(vm, vm.argument(0)));
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 5. Let disambiguation be ? ToTemporalDisambiguation(options).
auto disambiguation = TRY(to_temporal_disambiguation(vm, options));
// 6. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
auto instant = TRY(builtin_time_zone_get_instant_for(vm, time_zone, date_time, disambiguation));
// 7. Return ! CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, dateTime.[[Calendar]]).
return MUST(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, date_time->calendar()));
}
// 5.3.38 Temporal.PlainDateTime.prototype.toPlainDate ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaindate
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_plain_date)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
return MUST(create_temporal_date(vm, date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->calendar()));
}
// 5.3.39 Temporal.PlainDateTime.prototype.toPlainYearMonth ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplainyearmonth
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_plain_year_month)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "monthCode"sv, "year"sv }));
// 5. Let fields be ? PrepareTemporalFields(dateTime, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, date_time, field_names, Vector<StringView> {}));
// 6. Return ? CalendarYearMonthFromFields(calendar, fields).
return TRY(calendar_year_month_from_fields(vm, calendar, *fields));
}
// 5.3.40 Temporal.PlainDateTime.prototype.toPlainMonthDay ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplainmonthday
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_plain_month_day)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let calendar be dateTime.[[Calendar]].
auto& calendar = date_time->calendar();
// 4. Let fieldNames be ? CalendarFields(calendar, « "day", "monthCode" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "day"sv, "monthCode"sv }));
// 5. Let fields be ? PrepareTemporalFields(dateTime, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, date_time, field_names, Vector<StringView> {}));
// 6. Return ? CalendarMonthDayFromFields(calendar, fields).
return TRY(calendar_month_day_from_fields(vm, calendar, *fields));
}
// 5.3.41 Temporal.PlainDateTime.prototype.toPlainTime ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.toplaintime
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::to_plain_time)
{
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Return ! CreateTemporalTime(dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
return MUST(create_temporal_time(vm, date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond()));
}
// 5.3.42 Temporal.PlainDateTime.prototype.getISOFields ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.getisofields
JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::get_iso_fields)
{
auto& realm = *vm.current_realm();
// 1. Let dateTime be the this value.
// 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]).
auto date_time = TRY(typed_this_object(vm));
// 3. Let fields be OrdinaryObjectCreate(%Object.prototype%).
auto fields = Object::create(realm, realm.intrinsics().object_prototype());
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", dateTime.[[Calendar]]).
MUST(fields->create_data_property_or_throw(vm.names.calendar, Value(&date_time->calendar())));
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(dateTime.[[ISODay]])).
MUST(fields->create_data_property_or_throw(vm.names.isoDay, Value(date_time->iso_day())));
// 6. Perform ! CreateDataPropertyOrThrow(fields, "isoHour", 𝔽(dateTime.[[ISOHour]])).
MUST(fields->create_data_property_or_throw(vm.names.isoHour, Value(date_time->iso_hour())));
// 7. Perform ! CreateDataPropertyOrThrow(fields, "isoMicrosecond", 𝔽(dateTime.[[ISOMicrosecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMicrosecond, Value(date_time->iso_microsecond())));
// 8. Perform ! CreateDataPropertyOrThrow(fields, "isoMillisecond", 𝔽(dateTime.[[ISOMillisecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMillisecond, Value(date_time->iso_millisecond())));
// 9. Perform ! CreateDataPropertyOrThrow(fields, "isoMinute", 𝔽(dateTime.[[ISOMinute]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMinute, Value(date_time->iso_minute())));
// 10. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", 𝔽(dateTime.[[ISOMonth]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMonth, Value(date_time->iso_month())));
// 11. Perform ! CreateDataPropertyOrThrow(fields, "isoNanosecond", 𝔽(dateTime.[[ISONanosecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoNanosecond, Value(date_time->iso_nanosecond())));
// 12. Perform ! CreateDataPropertyOrThrow(fields, "isoSecond", 𝔽(dateTime.[[ISOSecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoSecond, Value(date_time->iso_second())));
// 13. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", 𝔽(dateTime.[[ISOYear]])).
MUST(fields->create_data_property_or_throw(vm.names.isoYear, Value(date_time->iso_year())));
// 14. Return fields.
return fields;
}
// 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 temporal_date = TRY(typed_this_object(vm));
// 3. Return dateTime.[[Calendar]].
auto& calendar = static_cast<Calendar&>(temporal_date->calendar());
return PrimitiveString::create(vm, calendar.identifier());
}
}

View file

@ -1,70 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.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_getter);
JS_DECLARE_NATIVE_FUNCTION(calendar_id_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);
JS_DECLARE_NATIVE_FUNCTION(era_getter);
JS_DECLARE_NATIVE_FUNCTION(era_year_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(with_plain_time);
JS_DECLARE_NATIVE_FUNCTION(with_plain_date);
JS_DECLARE_NATIVE_FUNCTION(with_calendar);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(until);
JS_DECLARE_NATIVE_FUNCTION(since);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date);
JS_DECLARE_NATIVE_FUNCTION(to_plain_year_month);
JS_DECLARE_NATIVE_FUNCTION(to_plain_month_day);
JS_DECLARE_NATIVE_FUNCTION(to_plain_time);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
};
}

View file

@ -1,207 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainMonthDay);
// 10 Temporal.PlainMonthDay Objects, https://tc39.es/proposal-temporal/#sec-temporal-plainmonthday-objects
PlainMonthDay::PlainMonthDay(u8 iso_month, u8 iso_day, i32 iso_year, Object& calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_iso_year(iso_year)
, m_iso_month(iso_month)
, m_iso_day(iso_day)
, m_calendar(calendar)
{
}
void PlainMonthDay::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_calendar);
}
// 10.5.1 ToTemporalMonthDay ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalmonthday
ThrowCompletionOr<PlainMonthDay*> to_temporal_month_day(VM& vm, Value item, Object const* options)
{
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
// 3. Let referenceISOYear be 1972 (the first leap year after the Unix epoch).
i32 reference_iso_year = 1972;
// 4. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalMonthDay]] internal slot, then
if (is<PlainMonthDay>(item_object)) {
// i. Return item.
return static_cast<PlainMonthDay*>(&item_object);
}
Object* calendar = nullptr;
bool calendar_absent;
// b. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
// i. Let calendar be item.[[Calendar]].
// ii. Let calendarAbsent be false.
if (is<PlainDate>(item_object)) {
calendar = &static_cast<PlainDate&>(item_object).calendar();
calendar_absent = false;
} else if (is<PlainDateTime>(item_object)) {
calendar = &static_cast<PlainDateTime&>(item_object).calendar();
calendar_absent = false;
} else if (is<PlainMonthDay>(item_object)) {
calendar = &static_cast<PlainMonthDay&>(item_object).calendar();
calendar_absent = false;
} else if (is<PlainTime>(item_object)) {
calendar = &static_cast<PlainTime&>(item_object).calendar();
calendar_absent = false;
} else if (is<PlainYearMonth>(item_object)) {
calendar = &static_cast<PlainYearMonth&>(item_object).calendar();
calendar_absent = false;
} else if (is<ZonedDateTime>(item_object)) {
calendar = &static_cast<ZonedDateTime&>(item_object).calendar();
calendar_absent = false;
} else {
// i. Let calendarLike be ? Get(item, "calendar").
auto calendar_like = TRY(item_object.get(vm.names.calendar));
// ii. If calendarLike is undefined, then
// 1. Let calendarAbsent be true.
// iii. Else,
// 1. Let calendarAbsent be false.
calendar_absent = calendar_like.is_undefined();
// iv. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
calendar = TRY(to_temporal_calendar_with_iso_default(vm, calendar_like));
}
// d. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, *calendar, { "day"sv, "month"sv, "monthCode"sv, "year"sv }));
// e. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, item_object, field_names, Vector<StringView> {}));
// f. Let month be ? Get(fields, "month").
auto month = TRY(fields->get(vm.names.month));
// g. Let monthCode be ? Get(fields, "monthCode").
auto month_code = TRY(fields->get(vm.names.monthCode));
// h. Let year be ? Get(fields, "year").
auto year = TRY(fields->get(vm.names.year));
// i. If calendarAbsent is true, and month is not undefined, and monthCode is undefined and year is undefined, then
if (calendar_absent && !month.is_undefined() && month_code.is_undefined() && year.is_undefined()) {
// i. Perform ! CreateDataPropertyOrThrow(fields, "year", 𝔽(referenceISOYear)).
MUST(fields->create_data_property_or_throw(vm.names.year, Value(reference_iso_year)));
}
// j. Return ? CalendarMonthDayFromFields(calendar, fields, options).
return calendar_month_day_from_fields(vm, *calendar, *fields, options);
}
// 5. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// 6. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// 7. Let result be ? ParseTemporalMonthDayString(string).
auto result = TRY(parse_temporal_month_day_string(vm, string));
// 8. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, result.calendar.has_value() ? PrimitiveString::create(vm, move(*result.calendar)) : js_undefined()));
// 9. If result.[[Year]] is undefined, then
if (!result.year.has_value()) {
// a. Return ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, referenceISOYear).
return TRY(create_temporal_month_day(vm, result.month, result.day, *calendar, reference_iso_year));
}
// 10. Set result to ? CreateTemporalMonthDay(result.[[Month]], result.[[Day]], calendar, referenceISOYear).
auto* plain_month_day = TRY(create_temporal_month_day(vm, result.month, result.day, *calendar, reference_iso_year));
// 11. NOTE: The following operation is called without options, in order for the calendar to store a canonical value in the [[ISOYear]] internal slot of the result.
// 12. Return ? CalendarMonthDayFromFields(calendar, result).
return TRY(calendar_month_day_from_fields(vm, *calendar, *plain_month_day));
}
// 10.5.2 CreateTemporalMonthDay ( isoMonth, isoDay, calendar, referenceISOYear [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalmonthday
ThrowCompletionOr<PlainMonthDay*> create_temporal_month_day(VM& vm, u8 iso_month, u8 iso_day, Object& calendar, i32 reference_iso_year, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: isoMonth, isoDay, and referenceISOYear are integers.
// 2. Assert: Type(calendar) is Object.
// 3. If IsValidISODate(referenceISOYear, isoMonth, isoDay) is false, throw a RangeError exception.
if (!is_valid_iso_date(reference_iso_year, iso_month, iso_day))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainMonthDay);
// 4. If ISODateTimeWithinLimits(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0) is false, throw a RangeError exception.
if (!iso_date_time_within_limits(reference_iso_year, iso_month, iso_day, 12, 0, 0, 0, 0, 0))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainMonthDay);
// 5. If newTarget is not present, set newTarget to %Temporal.PlainMonthDay%.
if (!new_target)
new_target = realm.intrinsics().temporal_plain_month_day_constructor();
// 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainMonthDay.prototype%", « [[InitializedTemporalMonthDay]], [[ISOMonth]], [[ISODay]], [[ISOYear]], [[Calendar]] »).
// 7. Set object.[[ISOMonth]] to isoMonth.
// 8. Set object.[[ISODay]] to isoDay.
// 9. Set object.[[Calendar]] to calendar.
// 10. Set object.[[ISOYear]] to referenceISOYear.
auto object = TRY(ordinary_create_from_constructor<PlainMonthDay>(vm, *new_target, &Intrinsics::temporal_plain_month_day_prototype, iso_month, iso_day, reference_iso_year, calendar));
// 11. Return object.
return object.ptr();
}
// 10.5.3 TemporalMonthDayToString ( monthDay, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporalmonthdaytostring
ThrowCompletionOr<String> temporal_month_day_to_string(VM& vm, PlainMonthDay& month_day, StringView show_calendar)
{
// 1. Assert: Type(monthDay) is Object.
// 2. Assert: monthDay has an [[InitializedTemporalMonthDay]] internal slot.
// 3. Let month be ToZeroPaddedDecimalString(temporalDate.[[ISOMonth]], 2).
// 4. Let day be ToZeroPaddedDecimalString(temporalDate.[[ISODay]], 2).
// 5. Let result be the string-concatenation of month, the code unit 0x002D (HYPHEN-MINUS), and day.
auto result = TRY_OR_THROW_OOM(vm, String::formatted("{:02}-{:02}", month_day.iso_month(), month_day.iso_day()));
// 6. Let calendarID be ? ToString(monthDay.[[Calendar]]).
auto calendar_id = TRY(Value(&month_day.calendar()).to_string(vm));
// 7. If showCalendar is one of "always" or "critical", or if calendarID is not "iso8601", then
if (show_calendar.is_one_of("always"sv, "critical"sv) || calendar_id != "iso8601"sv) {
// a. Let year be ! PadISOYear(monthDay.[[ISOYear]]).
// b. Set result to the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and result.
result = TRY_OR_THROW_OOM(vm, String::formatted("{}-{}", MUST_OR_THROW_OOM(pad_iso_year(vm, month_day.iso_year())), result));
}
// 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar).
auto calendar_string = MUST_OR_THROW_OOM(format_calendar_annotation(vm, calendar_id, show_calendar));
// 9. Set result to the string-concatenation of result and calendarString.
// 10. Return result.
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}", result, calendar_string));
}
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Object.h>
namespace JS::Temporal {
class PlainMonthDay final : public Object {
JS_OBJECT(PlainMonthDay, Object);
GC_DECLARE_ALLOCATOR(PlainMonthDay);
public:
virtual ~PlainMonthDay() override = default;
[[nodiscard]] i32 iso_year() const { return m_iso_year; }
[[nodiscard]] u8 iso_month() const { return m_iso_month; }
[[nodiscard]] u8 iso_day() const { return m_iso_day; }
[[nodiscard]] Object const& calendar() const { return m_calendar; }
[[nodiscard]] Object& calendar() { return m_calendar; }
private:
PlainMonthDay(u8 iso_month, u8 iso_day, i32 iso_year, Object& calendar, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 10.4 Properties of Temporal.PlainMonthDay Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plainmonthday-instances
i32 m_iso_year { 0 }; // [[ISOYear]]
u8 m_iso_month { 0 }; // [[ISOMonth]]
u8 m_iso_day { 0 }; // [[ISODay]]
GC::Ref<Object> m_calendar; // [[Calendar]]
};
struct ISOMonthDay {
u8 month;
u8 day;
i32 reference_iso_year;
};
ThrowCompletionOr<PlainMonthDay*> to_temporal_month_day(VM&, Value item, Object const* options = nullptr);
ThrowCompletionOr<PlainMonthDay*> create_temporal_month_day(VM&, u8 iso_month, u8 iso_day, Object& calendar, i32 reference_iso_year, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<String> temporal_month_day_to_string(VM&, PlainMonthDay&, StringView show_calendar);
}

View file

@ -1,109 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#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/PlainMonthDay.h>
#include <LibJS/Runtime/Temporal/PlainMonthDayConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainMonthDayConstructor);
// 10.1 The Temporal.PlainMonthDay Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plainmonthday-constructor
PlainMonthDayConstructor::PlainMonthDayConstructor(Realm& realm)
: NativeFunction(realm.vm().names.PlainMonthDay.as_string(), realm.intrinsics().function_prototype())
{
}
void PlainMonthDayConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 10.2.1 Temporal.PlainMonthDay.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_month_day_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);
}
// 10.1.1 Temporal.PlainMonthDay ( isoMonth, isoDay [ , calendarLike [ , referenceISOYear ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday
ThrowCompletionOr<Value> PlainMonthDayConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainMonthDay");
}
// 10.1.1 Temporal.PlainMonthDay ( isoMonth, isoDay [ , calendarLike [ , referenceISOYear ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday
ThrowCompletionOr<GC::Ref<Object>> PlainMonthDayConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
auto iso_month = vm.argument(0);
auto iso_day = vm.argument(1);
auto calendar_like = vm.argument(2);
auto reference_iso_year = vm.argument(3);
// 2. If referenceISOYear is undefined, then
if (reference_iso_year.is_undefined()) {
// a. Set referenceISOYear to 1972𝔽.
reference_iso_year = Value(1972);
}
// 3. Let m be ? ToIntegerWithTruncation(isoMonth).
auto m = TRY(to_integer_with_truncation(vm, iso_month, ErrorType::TemporalInvalidPlainMonthDay));
// 4. Let d be ? ToIntegerWithTruncation(isoDay).
auto d = TRY(to_integer_with_truncation(vm, iso_day, ErrorType::TemporalInvalidPlainMonthDay));
// 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, calendar_like));
// 6. Let ref be ? ToIntegerWithTruncation(referenceISOYear).
auto ref = TRY(to_integer_with_truncation(vm, reference_iso_year, ErrorType::TemporalInvalidPlainMonthDay));
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to CreateTemporalMonthDay will immediately check that these values are valid
// ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
if (!AK::is_within_range<i32>(ref) || !AK::is_within_range<u8>(m) || !AK::is_within_range<u8>(d))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainMonthDay);
// 7. Return ? CreateTemporalMonthDay(m, d, calendar, ref, NewTarget).
return *TRY(create_temporal_month_day(vm, m, d, *calendar, ref, &new_target));
}
// 10.2.2 Temporal.PlainMonthDay.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.from
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayConstructor::from)
{
auto item = vm.argument(0);
// 1. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
// 2. If Type(item) is Object and item has an [[InitializedTemporalMonthDay]] internal slot, then
if (item.is_object() && is<PlainMonthDay>(item.as_object())) {
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
auto& plain_month_day_object = static_cast<PlainMonthDay&>(item.as_object());
// b. Return ! CreateTemporalMonthDay(item.[[ISOMonth]], item.[[ISODay]], item.[[Calendar]], item.[[ISOYear]]).
return MUST(create_temporal_month_day(vm, plain_month_day_object.iso_month(), plain_month_day_object.iso_day(), plain_month_day_object.calendar(), plain_month_day_object.iso_year()));
}
// 3. Return ? ToTemporalMonthDay(item, options).
return TRY(to_temporal_month_day(vm, item, options));
}
}

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class PlainMonthDayConstructor final : public NativeFunction {
JS_OBJECT(PlainMonthDayConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(PlainMonthDayConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~PlainMonthDayConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit PlainMonthDayConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
};
}

View file

@ -1,296 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
#include <LibJS/Runtime/Temporal/PlainMonthDayPrototype.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainMonthDayPrototype);
// 10.3 Properties of the Temporal.PlainMonthDay Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plainmonthday-prototype-object
PlainMonthDayPrototype::PlainMonthDayPrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void PlainMonthDayPrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 10.3.2 Temporal.PlainMonthDay.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.PlainMonthDay"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.calendar, calendar_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.calendarId, calendar_id_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);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.with, with, 1, attr);
define_native_function(realm, vm.names.equals, equals, 1, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
define_native_function(realm, vm.names.toPlainDate, to_plain_date, 1, attr);
define_native_function(realm, vm.names.getISOFields, get_iso_fields, 0, attr);
}
// 10.3.3 get Temporal.PlainMonthDay.prototype.calendar, https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.prototype.calendar
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::calendar_getter)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Return monthDay.[[Calendar]].
return Value(&month_day->calendar());
}
// 10.3.4 get Temporal.PlainMonthDay.prototype.monthCode, https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.prototype.monthcode
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::month_code_getter)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Let calendar be monthDay.[[Calendar]].
auto& calendar = month_day->calendar();
// 4. Return ? CalendarMonthCode(calendar, monthDay).
return PrimitiveString::create(vm, TRY(calendar_month_code(vm, calendar, month_day)));
}
// 10.3.5 get Temporal.PlainMonthDay.prototype.day, https://tc39.es/proposal-temporal/#sec-get-temporal.plainmonthday.prototype.day
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::day_getter)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Let calendar be monthDay.[[Calendar]].
auto& calendar = month_day->calendar();
// 4. Return 𝔽(? CalendarDay(calendar, monthDay)).
return Value(TRY(calendar_day(vm, calendar, month_day)));
}
// 10.3.6 Temporal.PlainMonthDay.prototype.with ( temporalMonthDayLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.with
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::with)
{
auto temporal_month_day_like = vm.argument(0);
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. If Type(temporalMonthDayLike) is not Object, then
if (!temporal_month_day_like.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, temporal_month_day_like.to_string_without_side_effects());
}
// 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalMonthDayLike).
TRY(reject_object_with_calendar_or_time_zone(vm, temporal_month_day_like.as_object()));
// 5. Let calendar be monthDay.[[Calendar]].
auto& calendar = month_day->calendar();
// 6. Let fieldNames be ? CalendarFields(calendar, « "day", "month", "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "day"sv, "month"sv, "monthCode"sv, "year"sv }));
// 7. Let partialMonthDay be ? PrepareTemporalFields(temporalMonthDayLike, fieldNames, partial).
auto* partial_month_day = TRY(prepare_temporal_fields(vm, temporal_month_day_like.as_object(), field_names, PrepareTemporalFieldsPartial {}));
// 8. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 9. Let fields be ? PrepareTemporalFields(monthDay, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, month_day, field_names, Vector<StringView> {}));
// 10. Set fields to ? CalendarMergeFields(calendar, fields, partialMonthDay).
fields = TRY(calendar_merge_fields(vm, calendar, *fields, *partial_month_day));
// 11. Set fields to ? PrepareTemporalFields(fields, fieldNames, «»).
fields = TRY(prepare_temporal_fields(vm, *fields, field_names, Vector<StringView> {}));
// 12. Return ? CalendarMonthDayFromFields(calendar, fields, options).
return TRY(calendar_month_day_from_fields(vm, calendar, *fields, options));
}
// 10.3.7 Temporal.PlainMonthDay.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::equals)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Set other to ? ToTemporalMonthDay(other).
auto* other = TRY(to_temporal_month_day(vm, vm.argument(0)));
// 4. If monthDay.[[ISOMonth]] ≠ other.[[ISOMonth]], return false.
if (month_day->iso_month() != other->iso_month())
return Value(false);
// 5. If monthDay.[[ISODay]] ≠ other.[[ISODay]], return false.
if (month_day->iso_day() != other->iso_day())
return Value(false);
// 6. If monthDay.[[ISOYear]] ≠ other.[[ISOYear]], return false.
if (month_day->iso_year() != other->iso_year())
return Value(false);
// 7. Return ? CalendarEquals(monthDay.[[Calendar]], other.[[Calendar]]).
return Value(TRY(calendar_equals(vm, month_day->calendar(), other->calendar())));
}
// 10.3.8 Temporal.PlainMonthDay.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_string)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let showCalendar be ? ToCalendarNameOption(options).
auto show_calendar = TRY(to_calendar_name_option(vm, *options));
// 5. Return ? TemporalMonthDayToString(monthDay, showCalendar).
return PrimitiveString::create(vm, TRY(temporal_month_day_to_string(vm, month_day, show_calendar)));
}
// 10.3.9 Temporal.PlainMonthDay.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tolocalestring
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_locale_string)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Return ? TemporalMonthDayToString(monthDay, "auto").
return PrimitiveString::create(vm, TRY(temporal_month_day_to_string(vm, month_day, "auto"sv)));
}
// 10.3.10 Temporal.PlainMonthDay.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_json)
{
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Return ? TemporalMonthDayToString(monthDay, "auto").
return PrimitiveString::create(vm, TRY(temporal_month_day_to_string(vm, month_day, "auto"sv)));
}
// 10.3.11 Temporal.PlainMonthDay.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::value_of)
{
// 1. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.PlainMonthDay", "a primitive value");
}
// 10.3.12 Temporal.PlainMonthDay.prototype.toPlainDate ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.toplaindate
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::to_plain_date)
{
auto& realm = *vm.current_realm();
auto item = vm.argument(0);
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. If Type(item) is not Object, then
if (!item.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, item);
}
// 4. Let calendar be monthDay.[[Calendar]].
auto& calendar = month_day->calendar();
// 5. Let receiverFieldNames be ? CalendarFields(calendar, « "day", "monthCode" »).
auto receiver_field_names = TRY(calendar_fields(vm, calendar, { "day"sv, "monthCode"sv }));
// 6. Let fields be ? PrepareTemporalFields(monthDay, receiverFieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, month_day, receiver_field_names, Vector<StringView> {}));
// 7. Let inputFieldNames be ? CalendarFields(calendar, « "year" »).
auto input_field_names = TRY(calendar_fields(vm, calendar, { "year"sv }));
// 8. Let inputFields be ? PrepareTemporalFields(item, inputFieldNames, «»).
auto* input_fields = TRY(prepare_temporal_fields(vm, item.as_object(), input_field_names, Vector<StringView> {}));
// 9. Let mergedFields be ? CalendarMergeFields(calendar, fields, inputFields).
auto* merged_fields = TRY(calendar_merge_fields(vm, calendar, *fields, *input_fields));
// 10. Let mergedFieldNames be MergeLists(receiverFieldNames, inputFieldNames).
auto merged_field_names = merge_lists(receiver_field_names, input_field_names);
// 11. Set mergedFields to ? PrepareTemporalFields(mergedFields, mergedFieldNames, «»).
merged_fields = TRY(prepare_temporal_fields(vm, *merged_fields, merged_field_names, Vector<StringView> {}));
// 12. Let options be OrdinaryObjectCreate(null).
auto options = Object::create(realm, nullptr);
// 13. Perform ! CreateDataPropertyOrThrow(options, "overflow", "reject").
MUST(options->create_data_property_or_throw(vm.names.overflow, PrimitiveString::create(vm, vm.names.reject.as_string())));
// 14. Return ? CalendarDateFromFields(calendar, mergedFields, options).
return TRY(calendar_date_from_fields(vm, calendar, *merged_fields, options));
}
// 10.3.13 Temporal.PlainMonthDay.prototype.getISOFields ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainmonthday.prototype.getisofields
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::get_iso_fields)
{
auto& realm = *vm.current_realm();
// 1. Let monthDay be the this value.
// 2. Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// 3. Let fields be OrdinaryObjectCreate(%Object.prototype%).
auto fields = Object::create(realm, realm.intrinsics().object_prototype());
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", monthDay.[[Calendar]]).
MUST(fields->create_data_property_or_throw(vm.names.calendar, Value(&month_day->calendar())));
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(monthDay.[[ISODay]])).
MUST(fields->create_data_property_or_throw(vm.names.isoDay, Value(month_day->iso_day())));
// 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", 𝔽(monthDay.[[ISOMonth]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMonth, Value(month_day->iso_month())));
// 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", 𝔽(monthDay.[[ISOYear]])).
MUST(fields->create_data_property_or_throw(vm.names.isoYear, Value(month_day->iso_year())));
// 8. Return fields.
return fields;
}
// 10.3.3 get Temporal.PlainMonthDay.prototype.calendarId
JS_DEFINE_NATIVE_FUNCTION(PlainMonthDayPrototype::calendar_id_getter)
{
// Step 1: Let monthDay be the this value
// Step 2: Perform ? RequireInternalSlot(monthDay, [[InitializedTemporalMonthDay]]).
auto month_day = TRY(typed_this_object(vm));
// Step 3: Return monthDay.[[Calendar]].
auto& calendar = static_cast<Calendar&>(month_day->calendar());
return PrimitiveString::create(vm, calendar.identifier());
}
}

View file

@ -1,39 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
namespace JS::Temporal {
class PlainMonthDayPrototype final : public PrototypeObject<PlainMonthDayPrototype, PlainMonthDay> {
JS_PROTOTYPE_OBJECT(PlainMonthDayPrototype, PlainMonthDay, Temporal.PlainMonthDay);
GC_DECLARE_ALLOCATOR(PlainMonthDayPrototype);
public:
virtual void initialize(Realm&) override;
virtual ~PlainMonthDayPrototype() override = default;
private:
explicit PlainMonthDayPrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(calendar_getter);
JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter);
JS_DECLARE_NATIVE_FUNCTION(month_code_getter);
JS_DECLARE_NATIVE_FUNCTION(day_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
};
}

View file

@ -1,690 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.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 <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainTime);
// 4 Temporal.PlainTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-plaintime-objects
PlainTime::PlainTime(u8 iso_hour, u8 iso_minute, u8 iso_second, u16 iso_millisecond, u16 iso_microsecond, u16 iso_nanosecond, Calendar& calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_iso_hour(iso_hour)
, m_iso_minute(iso_minute)
, m_iso_second(iso_second)
, m_iso_millisecond(iso_millisecond)
, m_iso_microsecond(iso_microsecond)
, m_iso_nanosecond(iso_nanosecond)
, m_calendar(calendar)
{
}
void PlainTime::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_calendar);
}
// 4.5.1 DifferenceTime ( h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, ns2 ), https://tc39.es/proposal-temporal/#sec-temporal-differencetime
TimeDurationRecord difference_time(VM& vm, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2)
{
// 1. Let hours be h2 - h1.
auto hours = hour2 - hour1;
// 2. Let minutes be min2 - min1.
auto minutes = minute2 - minute1;
// 3. Let seconds be s2 - s1.
auto seconds = second2 - second1;
// 4. Let milliseconds be ms2 - ms1.
auto milliseconds = millisecond2 - millisecond1;
// 5. Let microseconds be mus2 - mus1.
auto microseconds = microsecond2 - microsecond1;
// 6. Let nanoseconds be ns2 - ns1.
auto nanoseconds = nanosecond2 - nanosecond1;
// 7. Let sign be ! DurationSign(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
auto sign = duration_sign(0, 0, 0, 0, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
// 8. Let bt be ! BalanceTime(hours × sign, minutes × sign, seconds × sign, milliseconds × sign, microseconds × sign, nanoseconds × sign).
auto bt = balance_time(hours * sign, minutes * sign, seconds * sign, milliseconds * sign, microseconds * sign, nanoseconds * sign);
// 9. Assert: bt.[[Days]] is 0.
VERIFY(bt.days == 0);
// 10. Return ! CreateTimeDurationRecord(0, bt.[[Hour]] × sign, bt.[[Minute]] × sign, bt.[[Second]] × sign, bt.[[Millisecond]] × sign, bt.[[Microsecond]] × sign, bt.[[Nanosecond]] × sign).
return MUST(create_time_duration_record(vm, 0, static_cast<double>(bt.hour * sign), static_cast<double>(bt.minute * sign), static_cast<double>(bt.second * sign), static_cast<double>(bt.millisecond * sign), static_cast<double>(bt.microsecond * sign), static_cast<double>(bt.nanosecond * sign)));
}
// 4.5.2 ToTemporalTime ( item [ , overflow ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltime
ThrowCompletionOr<PlainTime*> to_temporal_time(VM& vm, Value item, Optional<StringView> overflow)
{
// 1. If overflow is not present, set overflow to "constrain".
if (!overflow.has_value())
overflow = "constrain"sv;
// 2. Assert: overflow is either "constrain" or "reject".
VERIFY(overflow == "constrain"sv || overflow == "reject"sv);
Optional<TemporalTime> result;
// 3. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalTime]] internal slot, then
if (is<PlainTime>(item_object)) {
// i. Return item.
return &static_cast<PlainTime&>(item_object);
}
// b. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(item_object)) {
auto& zoned_date_time = static_cast<ZonedDateTime&>(item_object);
// i. Let instant be ! CreateTemporalInstant(item.[[Nanoseconds]]).
auto* instant = create_temporal_instant(vm, zoned_date_time.nanoseconds()).release_value();
// ii. Set plainDateTime to ? BuiltinTimeZoneGetPlainDateTimeFor(item.[[TimeZone]], instant, item.[[Calendar]]).
auto* plain_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &zoned_date_time.time_zone(), *instant, zoned_date_time.calendar()));
// iii. Return ! CreateTemporalTime(plainDateTime.[[ISOHour]], plainDateTime.[[ISOMinute]], plainDateTime.[[ISOSecond]], plainDateTime.[[ISOMillisecond]], plainDateTime.[[ISOMicrosecond]], plainDateTime.[[ISONanosecond]]).
return TRY(create_temporal_time(vm, plain_date_time->iso_hour(), plain_date_time->iso_minute(), plain_date_time->iso_second(), plain_date_time->iso_millisecond(), plain_date_time->iso_microsecond(), plain_date_time->iso_nanosecond()));
}
// c. If item has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(item_object)) {
auto& plain_date_time = static_cast<PlainDateTime&>(item_object);
// i. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]]).
return TRY(create_temporal_time(vm, plain_date_time.iso_hour(), plain_date_time.iso_minute(), plain_date_time.iso_second(), plain_date_time.iso_millisecond(), plain_date_time.iso_microsecond(), plain_date_time.iso_nanosecond()));
}
// d. Let calendar be ? GetTemporalCalendarWithISODefault(item).
auto* calendar = TRY(get_temporal_calendar_with_iso_default(vm, item_object));
// e. If ? ToString(calendar) is not "iso8601", then
auto calendar_identifier = TRY(Value(calendar).to_string(vm));
if (calendar_identifier != "iso8601"sv) {
// i. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, calendar_identifier);
}
// f. Let result be ? ToTemporalTimeRecord(item).
auto unregulated_result = TRY(to_temporal_time_record(vm, item_object));
// g. Set result to ? RegulateTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], overflow).
result = TRY(regulate_time(vm, *unregulated_result.hour, *unregulated_result.minute, *unregulated_result.second, *unregulated_result.millisecond, *unregulated_result.microsecond, *unregulated_result.nanosecond, *overflow));
}
// 4. Else,
else {
// a. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// b. Let result be ? ParseTemporalTimeString(string).
result = TRY(parse_temporal_time_string(vm, string));
// c. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
VERIFY(is_valid_time(result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond));
// d. If result.[[Calendar]] is not one of undefined or "iso8601", then
if (result->calendar.has_value() && *result->calendar != "iso8601"sv) {
// i. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidCalendarIdentifier, *result->calendar);
}
}
// 5. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
return MUST(create_temporal_time(vm, result->hour, result->minute, result->second, result->millisecond, result->microsecond, result->nanosecond));
}
// 4.5.3 RegulateTime ( hour, minute, second, millisecond, microsecond, nanosecond, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulatetime
ThrowCompletionOr<TemporalTime> regulate_time(VM& vm, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, StringView overflow)
{
// 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond are integers.
VERIFY(trunc(hour) == hour && trunc(minute) == minute && trunc(second) == second && trunc(millisecond) == millisecond && trunc(microsecond) == microsecond && trunc(nanosecond) == nanosecond);
// 2. Assert: overflow is either "constrain" or "reject".
// NOTE: Asserted by the VERIFY_NOT_REACHED at the end
// 3. If overflow is "constrain", then
if (overflow == "constrain"sv) {
// a. Return ! ConstrainTime(hour, minute, second, millisecond, microsecond, nanosecond).
return constrain_time(hour, minute, second, millisecond, microsecond, nanosecond);
}
// 4. Else,
else {
// a. Assert: overflow is "reject".
VERIFY(overflow == "reject"sv);
// b. 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::TemporalInvalidPlainTime);
// c. Return the Record { [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
return TemporalTime { .hour = static_cast<u8>(hour), .minute = static_cast<u8>(minute), .second = static_cast<u8>(second), .millisecond = static_cast<u16>(millisecond), .microsecond = static_cast<u16>(microsecond), .nanosecond = static_cast<u16>(nanosecond) };
}
}
// 4.5.4 IsValidTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-isvalidtime
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
{
// 1. If hour < 0 or hour > 23, then
if (hour > 23) {
// a. Return false.
return false;
}
// 2. If minute < 0 or minute > 59, then
if (minute > 59) {
// a. Return false.
return false;
}
// 3. If second < 0 or second > 59, then
if (second > 59) {
// a. Return false.
return false;
}
// 4. If millisecond < 0 or millisecond > 999, then
if (millisecond > 999) {
// a. Return false.
return false;
}
// 5. If microsecond < 0 or microsecond > 999, then
if (microsecond > 999) {
// a. Return false.
return false;
}
// 6. If nanosecond < 0 or nanosecond > 999, then
if (nanosecond > 999) {
// a. Return false.
return false;
}
// 7. Return true.
return true;
}
// 4.5.5 BalanceTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-balancetime
DaysAndTime balance_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, and nanosecond are integers.
VERIFY(hour == trunc(hour) && minute == trunc(minute) && second == trunc(second) && millisecond == trunc(millisecond) && microsecond == trunc(microsecond) && nanosecond == trunc(nanosecond));
// 2. Set microsecond to microsecond + floor(nanosecond / 1000).
microsecond += floor(nanosecond / 1000);
// 3. Set nanosecond to nanosecond modulo 1000.
nanosecond = modulo(nanosecond, 1000);
// 4. Set millisecond to millisecond + floor(microsecond / 1000).
millisecond += floor(microsecond / 1000);
// 5. Set microsecond to microsecond modulo 1000.
microsecond = modulo(microsecond, 1000);
// 6. Set second to second + floor(millisecond / 1000).
second += floor(millisecond / 1000);
// 7. Set millisecond to millisecond modulo 1000.
millisecond = modulo(millisecond, 1000);
// 8. Set minute to minute + floor(second / 60).
minute += floor(second / 60);
// 9. Set second to second modulo 60.
second = modulo(second, 60);
// 10. Set hour to hour + floor(minute / 60).
hour += floor(minute / 60);
// 11. Set minute to minute modulo 60.
minute = modulo(minute, 60);
// 12. Let days be floor(hour / 24).
auto days = floor(hour / 24);
// 13. Set hour to hour modulo 24.
hour = modulo(hour, 24);
// 14. Return the Record { [[Days]]: days, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
return DaysAndTime {
.days = static_cast<i32>(days),
.hour = static_cast<u8>(hour),
.minute = static_cast<u8>(minute),
.second = static_cast<u8>(second),
.millisecond = static_cast<u16>(millisecond),
.microsecond = static_cast<u16>(microsecond),
.nanosecond = static_cast<u16>(nanosecond),
};
}
// 4.5.6 ConstrainTime ( hour, minute, second, millisecond, microsecond, nanosecond ), https://tc39.es/proposal-temporal/#sec-temporal-constraintime
TemporalTime constrain_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, and nanosecond are integers.
// 2. Set hour to the result of clamping hour between 0 and 23.
hour = clamp(hour, 0, 23);
// 3. Set minute to the result of clamping minute between 0 and 59.
minute = clamp(minute, 0, 59);
// 4. Set second to the result of clamping second between 0 and 59.
second = clamp(second, 0, 59);
// 5. Set millisecond to the result of clamping millisecond between 0 and 999.
millisecond = clamp(millisecond, 0, 999);
// 6. Set microsecond to the result of clamping microsecond between 0 and 999.
microsecond = clamp(microsecond, 0, 999);
// 7. Set nanosecond to the result of clamping nanosecond between 0 and 999.
nanosecond = clamp(nanosecond, 0, 999);
// 8. Return the Record { [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
return TemporalTime { .hour = static_cast<u8>(hour), .minute = static_cast<u8>(minute), .second = static_cast<u8>(second), .millisecond = static_cast<u16>(millisecond), .microsecond = static_cast<u16>(microsecond), .nanosecond = static_cast<u16>(nanosecond) };
}
// 4.5.7 CreateTemporalTime ( hour, minute, second, millisecond, microsecond, nanosecond [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltime
ThrowCompletionOr<PlainTime*> create_temporal_time(VM& vm, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond are integers.
// 2. 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::TemporalInvalidPlainTime);
// 3. If newTarget is not present, set newTarget to %Temporal.PlainTime%.
if (!new_target)
new_target = realm.intrinsics().temporal_plain_time_constructor();
// 4. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainTime.prototype%", « [[InitializedTemporalTime]], [[ISOHour]], [[ISOMinute]], [[ISOSecond]], [[ISOMillisecond]], [[ISOMicrosecond]], [[ISONanosecond]], [[Calendar]] »).
// 5. Set object.[[ISOHour]] to hour.
// 6. Set object.[[ISOMinute]] to minute.
// 7. Set object.[[ISOSecond]] to second.
// 8. Set object.[[ISOMillisecond]] to millisecond.
// 9. Set object.[[ISOMicrosecond]] to microsecond.
// 10. Set object.[[ISONanosecond]] to nanosecond.
// 11. Set object.[[Calendar]] to ! GetISO8601Calendar().
auto object = TRY(ordinary_create_from_constructor<PlainTime>(vm, *new_target, &Intrinsics::temporal_plain_time_prototype, hour, minute, second, millisecond, microsecond, nanosecond, *get_iso8601_calendar(vm)));
// 12. Return object.
return object.ptr();
}
// 4.5.8 ToTemporalTimeRecord ( temporalTimeLike [ , completeness ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimerecord
ThrowCompletionOr<TemporalTimeLikeRecord> to_temporal_time_record(VM& vm, Object const& temporal_time_like, ToTemporalTimeRecordCompleteness completeness)
{
// 1. If completeness is not present, set completeness to complete.
// 2. Let partial be ? PrepareTemporalFields(temporalTimeLike, « "hour", "microsecond", "millisecond", "minute", "nanosecond", "second" », partial).
auto* partial = TRY(prepare_temporal_fields(vm, temporal_time_like,
{ "hour"_string,
"microsecond"_string,
"millisecond"_string,
"minute"_string,
"nanosecond"_string,
"second"_string },
PrepareTemporalFieldsPartial {}));
TemporalTimeLikeRecord result;
// 3. If completeness is complete, then
if (completeness == ToTemporalTimeRecordCompleteness::Complete) {
// a. Let result be a new TemporalTimeLike Record with each field set to 0.
result = TemporalTimeLikeRecord { 0, 0, 0, 0, 0, 0 };
}
// 4. Else,
else {
// a. Let result be a new TemporalTimeLike Record with each field set to undefined.
result = TemporalTimeLikeRecord {};
}
// 5. Let hourDesc be OrdinaryGetOwnProperty(partial, "hour").
auto hour_desc = MUST(partial->Object::internal_get_own_property(vm.names.hour));
// 6. If hourDesc is not undefined, then
if (hour_desc.has_value()) {
// a. Assert: hourDesc is a data Property Descriptor.
VERIFY(hour_desc->is_data_descriptor());
// b. Set result.[[Hour]] to (hourDesc.[[Value]]).
result.hour = hour_desc->value->as_double();
}
// 7. Let minuteDesc be OrdinaryGetOwnProperty(partial, "minute").
auto minute_desc = MUST(partial->Object::internal_get_own_property(vm.names.minute));
// 8. If minuteDesc is not undefined, then
if (minute_desc.has_value()) {
// a. Assert: minuteDesc is a data Property Descriptor.
VERIFY(minute_desc->is_data_descriptor());
// b. Set result.[[Minute]] to (minuteDesc.[[Value]]).
result.minute = minute_desc->value->as_double();
}
// 9. Let secondDesc be OrdinaryGetOwnProperty(partial, "second").
auto second_desc = MUST(partial->Object::internal_get_own_property(vm.names.second));
// 10. If secondDesc is not undefined, then
if (second_desc.has_value()) {
// a. Assert: secondDesc is a data Property Descriptor.
VERIFY(second_desc->is_data_descriptor());
// b. Set result.[[Second]] to (secondDesc.[[Value]]).
result.second = second_desc->value->as_double();
}
// 11. Let millisecondDesc be OrdinaryGetOwnProperty(partial, "millisecond").
auto millisecond_desc = MUST(partial->Object::internal_get_own_property(vm.names.millisecond));
// 12. If millisecondDesc is not undefined, then
if (millisecond_desc.has_value()) {
// a. Assert: millisecondDesc is a data Property Descriptor.
VERIFY(millisecond_desc->is_data_descriptor());
// b. Set result.[[Millisecond]] to (millisecondDesc.[[Value]]).
result.millisecond = millisecond_desc->value->as_double();
}
// 13. Let microsecondDesc be OrdinaryGetOwnProperty(partial, "microsecond").
auto microsecond_desc = MUST(partial->Object::internal_get_own_property(vm.names.microsecond));
// 14. If microsecondDesc is not undefined, then
if (microsecond_desc.has_value()) {
// a. Assert: microsecondDesc is a data Property Descriptor.
VERIFY(microsecond_desc->is_data_descriptor());
// b. Set result.[[Microsecond]] to (microsecondDesc.[[Value]]).
result.microsecond = microsecond_desc->value->as_double();
}
// 15. Let nanosecondDesc be OrdinaryGetOwnProperty(partial, "nanosecond").
auto nanosecond_desc = MUST(partial->Object::internal_get_own_property(vm.names.nanosecond));
// 16. If nanosecondDesc is not undefined, then
if (nanosecond_desc.has_value()) {
// a. Assert: nanosecondDesc is a data Property Descriptor.
VERIFY(nanosecond_desc->is_data_descriptor());
// b. Set result.[[Nanosecond]] to (nanosecondDesc.[[Value]]).
result.nanosecond = nanosecond_desc->value->as_double();
}
// 17. Return result.
return result;
}
// 4.5.9 TemporalTimeToString ( hour, minute, second, millisecond, microsecond, nanosecond, precision ), https://tc39.es/proposal-temporal/#sec-temporal-temporaltimetostring
ThrowCompletionOr<String> temporal_time_to_string(VM& vm, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<StringView, u8> const& precision)
{
// 1. Assert: hour, minute, second, millisecond, microsecond and nanosecond are integers.
// 2. Let hour be ToZeroPaddedDecimalString(hour, 2).
// 3. Let minute be ToZeroPaddedDecimalString(minute, 2).
// 4. Let seconds be ! FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision).
auto seconds = MUST_OR_THROW_OOM(format_seconds_string_part(vm, second, millisecond, microsecond, nanosecond, precision));
// 5. Return the string-concatenation of hour, the code unit 0x003A (COLON), minute, and seconds.
return TRY_OR_THROW_OOM(vm, String::formatted("{:02}:{:02}{}", hour, minute, seconds));
}
// 4.5.10 CompareTemporalTime ( h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, ns2 ), https://tc39.es/proposal-temporal/#sec-temporal-comparetemporaltime
i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2)
{
// 1. Assert: h1, min1, s1, ms1, mus1, ns1, h2, min2, s2, ms2, mus2, and ns2 are integers.
// 2. If h1 > h2, return 1.
if (hour1 > hour2)
return 1;
// 3. If h1 < h2, return -1.
if (hour1 < hour2)
return -1;
// 4. If min1 > min2, return 1.
if (minute1 > minute2)
return 1;
// 5. If min1 < min2, return -1.
if (minute1 < minute2)
return -1;
// 6. If s1 > s2, return 1.
if (second1 > second2)
return 1;
// 7. If s1 < s2, return -1.
if (second1 < second2)
return -1;
// 8. If ms1 > ms2, return 1.
if (millisecond1 > millisecond2)
return 1;
// 9. If ms1 < ms2, return -1.
if (millisecond1 < millisecond2)
return -1;
// 10. If mus1 > mus2, return 1.
if (microsecond1 > microsecond2)
return 1;
// 11. If mus1 < mus2, return -1.
if (microsecond1 < microsecond2)
return -1;
// 12. If ns1 > ns2, return 1.
if (nanosecond1 > nanosecond2)
return 1;
// 13. If ns1 < ns2, return -1.
if (nanosecond1 < nanosecond2)
return -1;
// 14. Return 0.
return 0;
}
// 4.5.11 AddTime ( hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, nanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-addtime
DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, nanosecond, hours, minutes, seconds, milliseconds, microseconds, and nanoseconds are integers.
VERIFY(hours == trunc(hours) && minutes == trunc(minutes) && seconds == trunc(seconds) && milliseconds == trunc(milliseconds) && microseconds == trunc(microseconds) && nanoseconds == trunc(nanoseconds));
// 2. Assert: IsValidTime(hour, minute, second, millisecond, microsecond, nanosecond) is true.
VERIFY(is_valid_time(hour, minute, second, millisecond, microsecond, nanosecond));
// 3. Let hour be hour + hours.
auto hour_ = hour + hours;
// 4. Let minute be minute + minutes.
auto minute_ = minute + minutes;
// 5. Let second be second + seconds.
auto second_ = second + seconds;
// 6. Let millisecond be millisecond + milliseconds.
auto millisecond_ = millisecond + milliseconds;
// 7. Let microsecond be microsecond + microseconds.
auto microsecond_ = microsecond + microseconds;
// 8. Let nanosecond be nanosecond + nanoseconds.
auto nanosecond_ = nanosecond + nanoseconds;
// 9. Return ! BalanceTime(hour, minute, second, millisecond, microsecond, nanosecond).
return balance_time(hour_, minute_, second_, millisecond_, microsecond_, nanosecond_);
}
// 4.5.12 RoundTime ( hour, minute, second, millisecond, microsecond, nanosecond, increment, unit, roundingMode [ , dayLengthNs ] ), https://tc39.es/proposal-temporal/#sec-temporal-roundtime
DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length_ns)
{
// 1. Assert: hour, minute, second, millisecond, microsecond, nanosecond, and increment are integers.
// 2. Let fractionalSecond be nanosecond × 10-9 + microsecond × 10-6 + millisecond × 10-3 + second.
double fractional_second = nanosecond * 0.000000001 + microsecond * 0.000001 + millisecond * 0.001 + second;
double quantity;
// 3. If unit is "day", then
if (unit == "day"sv) {
// a. If dayLengthNs is not present, set dayLengthNs to nsPerDay.
if (!day_length_ns.has_value())
day_length_ns = ns_per_day;
// b. Let quantity be (((((hour × 60 + minute) × 60 + second) × 1000 + millisecond) × 1000 + microsecond) × 1000 + nanosecond) / dayLengthNs.
quantity = (((((hour * 60.0 + minute) * 60.0 + second) * 1000.0 + millisecond) * 1000.0 + microsecond) * 1000.0 + nanosecond) / *day_length_ns;
}
// 4. Else if unit is "hour", then
else if (unit == "hour"sv) {
// a. Let quantity be (fractionalSecond / 60 + minute) / 60 + hour.
quantity = (fractional_second / 60.0 + minute) / 60.0 + hour;
}
// 5. Else if unit is "minute", then
else if (unit == "minute"sv) {
// a. Let quantity be fractionalSecond / 60 + minute.
quantity = fractional_second / 60.0 + minute;
}
// 6. Else if unit is "second", then
else if (unit == "second"sv) {
// a. Let quantity be fractionalSecond.
quantity = fractional_second;
}
// 7. Else if unit is "millisecond", then
else if (unit == "millisecond"sv) {
// a. Let quantity be nanosecond × 10-6 + microsecond × 10-3 + millisecond.
quantity = nanosecond * 0.000001 + 0.001 * microsecond + millisecond;
}
// 8. Else if unit is "microsecond", then
else if (unit == "microsecond"sv) {
// a. Let quantity be nanosecond × 10-3 + microsecond.
quantity = nanosecond * 0.001 + microsecond;
}
// 9. Else,
else {
// a. Assert: unit is "nanosecond".
VERIFY(unit == "nanosecond"sv);
// b. Let quantity be nanosecond.
quantity = nanosecond;
}
// 10. Let result be RoundNumberToIncrement(quantity, increment, roundingMode).
auto result = round_number_to_increment(quantity, increment, rounding_mode);
// If unit is "day", then
if (unit == "day"sv) {
// a. Return the Record { [[Days]]: result, [[Hour]]: 0, [[Minute]]: 0, [[Second]]: 0, [[Millisecond]]: 0, [[Microsecond]]: 0, [[Nanosecond]]: 0 }.
return DaysAndTime { .days = (i32)result, .hour = 0, .minute = 0, .second = 0, .millisecond = 0, .microsecond = 0, .nanosecond = 0 };
}
// 12. If unit is "hour", then
if (unit == "hour"sv) {
// a. Return ! BalanceTime(result, 0, 0, 0, 0, 0).
return balance_time(result, 0, 0, 0, 0, 0);
}
// 13. If unit is "minute", then
if (unit == "minute"sv) {
// a. Return ! BalanceTime(hour, result, 0, 0, 0, 0).
return balance_time(hour, result, 0, 0, 0, 0);
}
// 14. If unit is "second", then
if (unit == "second"sv) {
// a. Return ! BalanceTime(hour, minute, result, 0, 0, 0).
return balance_time(hour, minute, result, 0, 0, 0);
}
// 15. If unit is "millisecond", then
if (unit == "millisecond"sv) {
// a. Return ! BalanceTime(hour, minute, second, result, 0, 0).
return balance_time(hour, minute, second, result, 0, 0);
}
// 16. If unit is "microsecond", then
if (unit == "microsecond"sv) {
// a. Return ! BalanceTime(hour, minute, second, millisecond, result, 0).
return balance_time(hour, minute, second, millisecond, result, 0);
}
// 17. Assert: unit is "nanosecond".
VERIFY(unit == "nanosecond"sv);
// 18. Return ! BalanceTime(hour, minute, second, millisecond, microsecond, result).
return balance_time(hour, minute, second, millisecond, microsecond, result);
}
// 4.5.13 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_time(VM& vm, DifferenceOperation operation, PlainTime const& temporal_time, Value other_value, Value options_value)
{
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalTime(other).
auto* other = TRY(to_temporal_time(vm, other_value));
// 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », "nanosecond", "hour").
auto settings = TRY(get_difference_settings(vm, operation, options_value, UnitGroup::Time, {}, { "nanosecond"sv }, "hour"sv));
// 4. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
auto result = difference_time(vm, temporal_time.iso_hour(), temporal_time.iso_minute(), temporal_time.iso_second(), temporal_time.iso_millisecond(), temporal_time.iso_microsecond(), temporal_time.iso_nanosecond(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond());
// 5. Set result to (! RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]])).[[DurationRecord]].
auto rounded_result = MUST(round_duration(vm, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode)).duration_record;
// 6. Set result to ! BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], settings.[[LargestUnit]]).
result = MUST(balance_duration(vm, 0, rounded_result.hours, rounded_result.minutes, rounded_result.seconds, rounded_result.milliseconds, rounded_result.microseconds, Crypto::SignedBigInteger { rounded_result.nanoseconds }, settings.largest_unit));
// 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
// 4.5.14 AddDurationToOrSubtractDurationFromPlainTime ( operation, temporalTime, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaintime
ThrowCompletionOr<PlainTime*> add_duration_to_or_subtract_duration_from_plain_time(VM& vm, ArithmeticOperation operation, PlainTime const& temporal_time, Value temporal_duration_like)
{
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == ArithmeticOperation::Subtract ? -1 : 1;
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
auto duration = TRY(to_temporal_duration_record(vm, temporal_duration_like));
// 3. Let result be ! AddTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]]).
auto result = add_time(temporal_time.iso_hour(), temporal_time.iso_minute(), temporal_time.iso_second(), temporal_time.iso_millisecond(), temporal_time.iso_microsecond(), temporal_time.iso_nanosecond(), sign * duration.hours, sign * duration.minutes, sign * duration.seconds, sign * duration.milliseconds, sign * duration.microseconds, sign * duration.nanoseconds);
// 4. Assert: IsValidTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]) is true.
VERIFY(is_valid_time(result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
// 5. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
return MUST(create_temporal_time(vm, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
}
}

View file

@ -1,96 +0,0 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/VM.h>
namespace JS::Temporal {
class PlainTime final : public Object {
JS_OBJECT(PlainTime, Object);
GC_DECLARE_ALLOCATOR(PlainTime);
public:
virtual ~PlainTime() override = default;
[[nodiscard]] u8 iso_hour() const { return m_iso_hour; }
[[nodiscard]] u8 iso_minute() const { return m_iso_minute; }
[[nodiscard]] u8 iso_second() const { return m_iso_second; }
[[nodiscard]] u16 iso_millisecond() const { return m_iso_millisecond; }
[[nodiscard]] u16 iso_microsecond() const { return m_iso_microsecond; }
[[nodiscard]] u16 iso_nanosecond() const { return m_iso_nanosecond; }
[[nodiscard]] Calendar const& calendar() const { return m_calendar; }
[[nodiscard]] Calendar& calendar() { return m_calendar; }
private:
PlainTime(u8 iso_hour, u8 iso_minute, u8 iso_second, u16 iso_millisecond, u16 iso_microsecond, u16 iso_nanosecond, Calendar& calendar, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 4.4 Properties of Temporal.PlainTime Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plaintime-instances
u8 m_iso_hour { 0 }; // [[ISOHour]]
u8 m_iso_minute { 0 }; // [[ISOMinute]]
u8 m_iso_second { 0 }; // [[ISOSecond]]
u16 m_iso_millisecond { 0 }; // [[ISOMillisecond]]
u16 m_iso_microsecond { 0 }; // [[ISOMicrosecond]]
u16 m_iso_nanosecond { 0 }; // [[ISONanosecond]]
GC::Ref<Calendar> m_calendar; // [[Calendar]] (always the built-in ISO 8601 calendar)
};
struct DaysAndTime {
i32 days;
u8 hour;
u8 minute;
u8 second;
u16 millisecond;
u16 microsecond;
u16 nanosecond;
};
struct TemporalTimeLikeRecord {
Optional<double> hour;
Optional<double> minute;
Optional<double> second;
Optional<double> millisecond;
Optional<double> microsecond;
Optional<double> nanosecond;
};
// Table 4: TemporalTimeLike Record Fields, https://tc39.es/proposal-temporal/#table-temporal-temporaltimelike-properties
template<typename StructT, typename ValueT>
struct TemporalTimeLikeRecordField {
ValueT StructT::*field_name { nullptr };
PropertyKey property_name;
};
enum class ToTemporalTimeRecordCompleteness {
Partial,
Complete,
};
TimeDurationRecord difference_time(VM&, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
ThrowCompletionOr<PlainTime*> to_temporal_time(VM&, Value item, Optional<StringView> overflow = {});
ThrowCompletionOr<TemporalTime> regulate_time(VM&, double hour, double minute, double second, double millisecond, double microsecond, double nanosecond, StringView overflow);
bool is_valid_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
DaysAndTime balance_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
TemporalTime constrain_time(double hour, double minute, double second, double millisecond, double microsecond, double nanosecond);
ThrowCompletionOr<PlainTime*> create_temporal_time(VM&, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<TemporalTimeLikeRecord> to_temporal_time_record(VM&, Object const& temporal_time_like, ToTemporalTimeRecordCompleteness = ToTemporalTimeRecordCompleteness::Complete);
ThrowCompletionOr<String> temporal_time_to_string(VM&, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant<StringView, u8> const& precision);
i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2);
DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional<double> day_length_ns = {});
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_time(VM&, DifferenceOperation, PlainTime const&, Value other, Value options);
ThrowCompletionOr<PlainTime*> add_duration_to_or_subtract_duration_from_plain_time(VM&, ArithmeticOperation, PlainTime const&, Value temporal_duration_like);
}

View file

@ -1,117 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainTimeConstructor);
// 4.1 The Temporal.PlainTime Constructor, https://tc39.es/proposal-temporal/#sec-temporal-plaintime-constructor
PlainTimeConstructor::PlainTimeConstructor(Realm& realm)
: NativeFunction(realm.vm().names.PlainTime.as_string(), realm.intrinsics().function_prototype())
{
}
void PlainTimeConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 4.2.1 Temporal.PlainTime.prototype, https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_plain_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(0), Attribute::Configurable);
}
// 4.1.1 Temporal.PlainTime ( [ hour [ , minute [ , second [ , millisecond [ , microsecond [ , nanosecond ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime
ThrowCompletionOr<Value> PlainTimeConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainTime");
}
// 4.1.1 Temporal.PlainTime ( [ hour [ , minute [ , second [ , millisecond [ , microsecond [ , nanosecond ] ] ] ] ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime
ThrowCompletionOr<GC::Ref<Object>> PlainTimeConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Let hour be ? ToIntegerWithTruncation(hour).
auto hour = TRY(to_integer_with_truncation(vm, vm.argument(0), ErrorType::TemporalInvalidPlainTime));
// 3. Let minute be ? ToIntegerWithTruncation(hour).
auto minute = TRY(to_integer_with_truncation(vm, vm.argument(1), ErrorType::TemporalInvalidPlainTime));
// 4. Let second be ? ToIntegerWithTruncation(hour).
auto second = TRY(to_integer_with_truncation(vm, vm.argument(2), ErrorType::TemporalInvalidPlainTime));
// 5. Let millisecond be ? ToIntegerWithTruncation(hour).
auto millisecond = TRY(to_integer_with_truncation(vm, vm.argument(3), ErrorType::TemporalInvalidPlainTime));
// 6. Let microsecond be ? ToIntegerWithTruncation(hour).
auto microsecond = TRY(to_integer_with_truncation(vm, vm.argument(4), ErrorType::TemporalInvalidPlainTime));
// 7. Let nanosecond be ? ToIntegerWithTruncation(hour).
auto nanosecond = TRY(to_integer_with_truncation(vm, vm.argument(5), ErrorType::TemporalInvalidPlainTime));
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to CreateTemporalTime will immediately check that these values are valid
// ISO values (for hours: 0 - 23, for minutes and seconds: 0 - 59, milliseconds, microseconds, and nanoseconds: 0 - 999) all of which
// are subsets of this check.
if (!AK::is_within_range<u8>(hour) || !AK::is_within_range<u8>(minute) || !AK::is_within_range<u8>(second) || !AK::is_within_range<u16>(millisecond) || !AK::is_within_range<u16>(microsecond) || !AK::is_within_range<u16>(nanosecond))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainTime);
// 8. Return ? CreateTemporalTime(hour, minute, second, millisecond, microsecond, nanosecond, NewTarget).
return *TRY(create_temporal_time(vm, hour, minute, second, millisecond, microsecond, nanosecond, &new_target));
}
// 4.2.2 Temporal.PlainTime.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.from
JS_DEFINE_NATIVE_FUNCTION(PlainTimeConstructor::from)
{
// 1. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 2. Let overflow be ? ToTemporalOverflow(options).
auto overflow = TRY(to_temporal_overflow(vm, options));
auto item = vm.argument(0);
// 3. If Type(item) is Object and item has an [[InitializedTemporalTime]] internal slot, then
if (item.is_object() && is<PlainTime>(item.as_object())) {
auto& plain_time = static_cast<PlainTime&>(item.as_object());
// a. Return ! CreateTemporalTime(item.[[ISOHour]], item.[[ISOMinute]], item.[[ISOSecond]], item.[[ISOMillisecond]], item.[[ISOMicrosecond]], item.[[ISONanosecond]]).
return MUST(create_temporal_time(vm, plain_time.iso_hour(), plain_time.iso_minute(), plain_time.iso_second(), plain_time.iso_millisecond(), plain_time.iso_microsecond(), plain_time.iso_nanosecond()));
}
// 4. Return ? ToTemporalTime(item, overflow).
return TRY(to_temporal_time(vm, item, overflow));
}
// 4.2.3 Temporal.PlainTime.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.compare
JS_DEFINE_NATIVE_FUNCTION(PlainTimeConstructor::compare)
{
// 1. Set one to ? ToTemporalTime(one).
auto* one = TRY(to_temporal_time(vm, vm.argument(0)));
// 2. Set two to ? ToTemporalTime(two).
auto* two = TRY(to_temporal_time(vm, vm.argument(1)));
// 3. Return 𝔽(! CompareTemporalTime(one.[[ISOHour]], one.[[ISOMinute]], one.[[ISOSecond]], one.[[ISOMillisecond]], one.[[ISOMicrosecond]], one.[[ISONanosecond]], two.[[ISOHour]], two.[[ISOMinute]], two.[[ISOSecond]], two.[[ISOMillisecond]], two.[[ISOMicrosecond]], two.[[ISONanosecond]])).
return Value(compare_temporal_time(one->iso_hour(), one->iso_minute(), one->iso_second(), one->iso_millisecond(), one->iso_microsecond(), one->iso_nanosecond(), two->iso_hour(), two->iso_minute(), two->iso_second(), two->iso_millisecond(), two->iso_microsecond(), two->iso_nanosecond()));
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class PlainTimeConstructor final : public NativeFunction {
JS_OBJECT(PlainTimeConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(PlainTimeConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~PlainTimeConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit PlainTimeConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
JS_DECLARE_NATIVE_FUNCTION(compare);
};
}

View file

@ -1,518 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.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/PlainTime.h>
#include <LibJS/Runtime/Temporal/PlainTimePrototype.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainTimePrototype);
// 4.3 Properties of the Temporal.PlainTime Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-plaintime-prototype-object
PlainTimePrototype::PlainTimePrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void PlainTimePrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 4.3.2 Temporal.PlainTime.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.PlainTime"_string), Attribute::Configurable);
define_native_accessor(realm, vm.names.calendar, calendar_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);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.add, add, 1, attr);
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
define_native_function(realm, vm.names.with, with, 1, attr);
define_native_function(realm, vm.names.until, until, 1, attr);
define_native_function(realm, vm.names.since, since, 1, attr);
define_native_function(realm, vm.names.round, round, 1, attr);
define_native_function(realm, vm.names.equals, equals, 1, attr);
define_native_function(realm, vm.names.toPlainDateTime, to_plain_date_time, 1, attr);
define_native_function(realm, vm.names.toZonedDateTime, to_zoned_date_time, 1, attr);
define_native_function(realm, vm.names.getISOFields, get_iso_fields, 0, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
}
// 4.3.3 get Temporal.PlainTime.prototype.calendar, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.calendar
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::calendar_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return temporalTime.[[Calendar]].
return Value(&temporal_time->calendar());
}
// 4.3.4 get Temporal.PlainTime.prototype.hour, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.hour
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::hour_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(temporalTime.[[ISOHour]]).
return Value(temporal_time->iso_hour());
}
// 4.3.5 get Temporal.PlainTime.prototype.minute, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.minute
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::minute_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(temporalTime.[[ISOMinute]]).
return Value(temporal_time->iso_minute());
}
// 4.3.6 get Temporal.PlainTime.prototype.second, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.second
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::second_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(temporalTime.[[ISOSecond]]).
return Value(temporal_time->iso_second());
}
// 4.3.7 get Temporal.PlainTime.prototype.millisecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.millisecond
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::millisecond_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(temporalTime.[[ISOMillisecond]]).
return Value(temporal_time->iso_millisecond());
}
// 4.3.8 get Temporal.PlainTime.prototype.microsecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.microsecond
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::microsecond_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(temporalTime.[[ISOMicrosecond]]).
return Value(temporal_time->iso_microsecond());
}
// 4.3.9 get Temporal.PlainTime.prototype.nanosecond, https://tc39.es/proposal-temporal/#sec-get-temporal.plaintime.prototype.nanosecond
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::nanosecond_getter)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return 𝔽(temporalTime.[[ISONanosecond]]).
return Value(temporal_time->iso_nanosecond());
}
// 4.3.10 Temporal.PlainTime.prototype.add ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.add
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::add)
{
auto temporal_duration_like = vm.argument(0);
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromPlainTime(add, temporalTime, temporalDurationLike).
return TRY(add_duration_to_or_subtract_duration_from_plain_time(vm, ArithmeticOperation::Add, temporal_time, temporal_duration_like));
}
// 4.3.11 Temporal.PlainTime.prototype.subtract ( temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::subtract)
{
auto temporal_duration_like = vm.argument(0);
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromPlainTime(subtract, temporalTime, temporalDurationLike).
return TRY(add_duration_to_or_subtract_duration_from_plain_time(vm, ArithmeticOperation::Subtract, temporal_time, temporal_duration_like));
}
// 4.3.12 Temporal.PlainTime.prototype.with ( temporalTimeLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.with
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::with)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
auto temporal_time_like_argument = vm.argument(0);
// 3. If Type(temporalTimeLike) is not Object, then
if (!temporal_time_like_argument.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, temporal_time_like_argument.to_string_without_side_effects());
}
auto& temporal_time_like = temporal_time_like_argument.as_object();
// 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalTimeLike).
TRY(reject_object_with_calendar_or_time_zone(vm, temporal_time_like));
// 5. Let partialTime be ? ToTemporalTimeRecord(temporalTimeLike, partial).
auto partial_time = TRY(to_temporal_time_record(vm, temporal_time_like, ToTemporalTimeRecordCompleteness::Partial));
// 6. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 7. Let overflow be ? ToTemporalOverflow(options).
auto overflow = TRY(to_temporal_overflow(vm, options));
// 8. If partialTime.[[Hour]] is not undefined, then
// a. Let hour be partialTime.[[Hour]].
// 9. Else,
// a. Let hour be temporalTime.[[ISOHour]].
auto hour = partial_time.hour.value_or(temporal_time->iso_hour());
// 10. If partialTime.[[Minute]] is not undefined, then
// a. Let minute be partialTime.[[Minute]].
// 11. Else,
// a. Let minute be temporalTime.[[ISOMinute]].
auto minute = partial_time.minute.value_or(temporal_time->iso_minute());
// 12. If partialTime.[[Second]] is not undefined, then
// a. Let second be partialTime.[[Second]].
// 13. Else,
// a. Let second be temporalTime.[[ISOSecond]].
auto second = partial_time.second.value_or(temporal_time->iso_second());
// 14. If partialTime.[[Millisecond]] is not undefined, then
// a. Let millisecond be partialTime.[[Millisecond]].
// 15. Else,
// a. Let millisecond be temporalTime.[[ISOMillisecond]].
auto millisecond = partial_time.millisecond.value_or(temporal_time->iso_millisecond());
// 16. If partialTime.[[Microsecond]] is not undefined, then
// a. Let microsecond be partialTime.[[Microsecond]].
// 17. Else,
// a. Let microsecond be temporalTime.[[ISOMicrosecond]].
auto microsecond = partial_time.microsecond.value_or(temporal_time->iso_microsecond());
// 18. If partialTime.[[Nanosecond]] is not undefined, then
// a. Let nanosecond be partialTime.[[Nanosecond]].
// 19. Else,
// a. Let nanosecond be temporalTime.[[ISONanosecond]].
auto nanosecond = partial_time.nanosecond.value_or(temporal_time->iso_nanosecond());
// 20. Let result be ? RegulateTime(hour, minute, second, millisecond, microsecond, nanosecond, overflow).
auto result = TRY(regulate_time(vm, hour, minute, second, millisecond, microsecond, nanosecond, overflow));
// 21. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
return MUST(create_temporal_time(vm, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
}
// 4.3.13 Temporal.PlainTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.until
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::until)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainTime(until, temporalTime, other, options).
return TRY(difference_temporal_plain_time(vm, DifferenceOperation::Until, temporal_time, other, options));
}
// 4.3.14 Temporal.PlainTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::since)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainTime(since, temporalTime, other, options).
return TRY(difference_temporal_plain_time(vm, DifferenceOperation::Since, temporal_time, other, options));
}
// 4.3.15 Temporal.PlainTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::round)
{
auto& realm = *vm.current_realm();
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. If roundTo is undefined, then
if (vm.argument(0).is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::TemporalMissingOptionsObject);
}
Object* round_to;
// 4. If Type(roundTo) is String, then
if (vm.argument(0).is_string()) {
// a. Let paramString be roundTo.
// b. Set roundTo to OrdinaryObjectCreate(null).
round_to = Object::create(realm, nullptr);
// c. Perform ! CreateDataPropertyOrThrow(roundTo, "smallestUnit", paramString).
MUST(round_to->create_data_property_or_throw(vm.names.smallestUnit, vm.argument(0)));
}
// 5. Else,
else {
// a. Set roundTo to ? GetOptionsObject(roundTo).
round_to = TRY(get_options_object(vm, vm.argument(0)));
}
// 6. Let smallestUnit be ? GetTemporalUnit(roundTo, "smallestUnit", time, required).
auto smallest_unit = TRY(get_temporal_unit(vm, *round_to, vm.names.smallestUnit, UnitGroup::Time, TemporalUnitRequired {}));
// 7. Let roundingMode be ? ToTemporalRoundingMode(roundTo, "halfExpand").
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *round_to, "halfExpand"sv));
// 8. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// 9. Assert maximum is not undefined.
VERIFY(maximum.has_value());
// 10. Let roundingIncrement be ? ToTemporalRoundingIncrement(roundTo).
auto rounding_increment = TRY(to_temporal_rounding_increment(vm, *round_to));
// 11. Perform ? ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false).
TRY(validate_temporal_rounding_increment(vm, rounding_increment, *maximum, false));
// 12. Let result be ! RoundTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], roundingIncrement, smallestUnit, roundingMode).
auto result = round_time(temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), rounding_increment, *smallest_unit, rounding_mode);
// 13. Return ! CreateTemporalTime(result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]]).
return MUST(create_temporal_time(vm, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond));
}
// 4.3.16 Temporal.PlainTime.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::equals)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Set other to ? ToTemporalTime(other).
auto* other = TRY(to_temporal_time(vm, vm.argument(0)));
// 4. If temporalTime.[[ISOHour]] ≠ other.[[ISOHour]], return false.
if (temporal_time->iso_hour() != other->iso_hour())
return Value(false);
// 5. If temporalTime.[[ISOMinute]] ≠ other.[[ISOMinute]], return false.
if (temporal_time->iso_minute() != other->iso_minute())
return Value(false);
// 6. If temporalTime.[[ISOSecond]] ≠ other.[[ISOSecond]], return false.
if (temporal_time->iso_second() != other->iso_second())
return Value(false);
// 7. If temporalTime.[[ISOMillisecond]] ≠ other.[[ISOMillisecond]], return false.
if (temporal_time->iso_millisecond() != other->iso_millisecond())
return Value(false);
// 8. If temporalTime.[[ISOMicrosecond]] ≠ other.[[ISOMicrosecond]], return false.
if (temporal_time->iso_microsecond() != other->iso_microsecond())
return Value(false);
// 9. If temporalTime.[[ISONanosecond]] ≠ other.[[ISONanosecond]], return false.
if (temporal_time->iso_nanosecond() != other->iso_nanosecond())
return Value(false);
// 10. Return true.
return Value(true);
}
// 4.3.17 Temporal.PlainTime.prototype.toPlainDateTime ( temporalDate ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.toplaindatetime
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_plain_date_time)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Set temporalDate to ? ToTemporalDate(temporalDate).
auto* temporal_date = TRY(to_temporal_date(vm, vm.argument(0)));
// 4. Return ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]).
return TRY(create_temporal_date_time(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), temporal_date->calendar()));
}
// 4.3.18 Temporal.PlainTime.prototype.toZonedDateTime ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tozoneddatetime
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_zoned_date_time)
{
auto item = vm.argument(0);
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. If Type(item) is not Object, then
if (!item.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, item);
}
// 4. Let temporalDateLike be ? Get(item, "plainDate").
auto temporal_date_like = TRY(item.as_object().get(vm.names.plainDate));
// 5. If temporalDateLike is undefined, then
if (temporal_date_like.is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.plainDate.as_string());
}
// 6. Let temporalDate be ? ToTemporalDate(temporalDateLike).
auto* temporal_date = TRY(to_temporal_date(vm, temporal_date_like));
// 7. Let temporalTimeZoneLike be ? Get(item, "timeZone").
auto temporal_time_zone_like = TRY(item.as_object().get(vm.names.timeZone));
// 8. If temporalTimeZoneLike is undefined, then
if (temporal_time_zone_like.is_undefined()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::MissingRequiredProperty, vm.names.timeZone.as_string());
}
// 9. Let timeZone be ? ToTemporalTimeZone(temporalTimeZoneLike).
auto* time_zone = TRY(to_temporal_time_zone(vm, temporal_time_zone_like));
// 10. Let temporalDateTime be ? CreateTemporalDateTime(temporalDate.[[ISOYear]], temporalDate.[[ISOMonth]], temporalDate.[[ISODay]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], temporalDate.[[Calendar]]).
auto* temporal_date_time = TRY(create_temporal_date_time(vm, temporal_date->iso_year(), temporal_date->iso_month(), temporal_date->iso_day(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), temporal_date->calendar()));
// 11. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, temporalDateTime, "compatible").
auto instant = TRY(builtin_time_zone_get_instant_for(vm, time_zone, *temporal_date_time, "compatible"sv));
// 12. Return ! CreateTemporalZonedDateTime(instant.[[Nanoseconds]], timeZone, temporalDate.[[Calendar]]).
return MUST(create_temporal_zoned_date_time(vm, instant->nanoseconds(), *time_zone, temporal_date->calendar()));
}
// 4.3.19 Temporal.PlainTime.prototype.getISOFields ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.getisofields
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::get_iso_fields)
{
auto& realm = *vm.current_realm();
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Let fields be OrdinaryObjectCreate(%Object.prototype%).
auto fields = Object::create(realm, realm.intrinsics().object_prototype());
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", temporalTime.[[Calendar]]).
MUST(fields->create_data_property_or_throw(vm.names.calendar, Value(&temporal_time->calendar())));
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoHour", 𝔽(temporalTime.[[ISOHour]])).
MUST(fields->create_data_property_or_throw(vm.names.isoHour, Value(temporal_time->iso_hour())));
// 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMicrosecond", 𝔽(temporalTime.[[ISOMicrosecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMicrosecond, Value(temporal_time->iso_microsecond())));
// 7. Perform ! CreateDataPropertyOrThrow(fields, "isoMillisecond", 𝔽(temporalTime.[[ISOMillisecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMillisecond, Value(temporal_time->iso_millisecond())));
// 8. Perform ! CreateDataPropertyOrThrow(fields, "isoMinute", 𝔽(temporalTime.[[ISOMinute]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMinute, Value(temporal_time->iso_minute())));
// 9. Perform ! CreateDataPropertyOrThrow(fields, "isoNanosecond", 𝔽(temporalTime.[[ISONanosecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoNanosecond, Value(temporal_time->iso_nanosecond())));
// 10. Perform ! CreateDataPropertyOrThrow(fields, "isoSecond", 𝔽(temporalTime.[[ISOSecond]])).
MUST(fields->create_data_property_or_throw(vm.names.isoSecond, Value(temporal_time->iso_second())));
// 11. Return fields.
return fields;
}
// 4.3.20 Temporal.PlainTime.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_string)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let precision be ? ToSecondsStringPrecisionRecord(options).
auto precision = TRY(to_seconds_string_precision_record(vm, *options));
// 5. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(vm, *options, "trunc"sv));
// 6. Let roundResult be ! RoundTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], precision.[[Increment]], precision.[[Unit]], roundingMode).
auto round_result = round_time(temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), precision.increment, precision.unit, rounding_mode);
// 7. Return ! TemporalTimeToString(roundResult.[[Hour]], roundResult.[[Minute]], roundResult.[[Second]], roundResult.[[Millisecond]], roundResult.[[Microsecond]], roundResult.[[Nanosecond]], precision.[[Precision]]).
auto string = MUST_OR_THROW_OOM(temporal_time_to_string(vm, round_result.hour, round_result.minute, round_result.second, round_result.millisecond, round_result.microsecond, round_result.nanosecond, precision.precision));
return PrimitiveString::create(vm, move(string));
}
// 4.3.21 Temporal.PlainTime.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tolocalestring
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_locale_string)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return ! TemporalTimeToString(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], "auto").
auto string = MUST_OR_THROW_OOM(temporal_time_to_string(vm, temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), "auto"sv));
return PrimitiveString::create(vm, move(string));
}
// 4.3.22 Temporal.PlainTime.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::to_json)
{
// 1. Let temporalTime be the this value.
// 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]).
auto temporal_time = TRY(typed_this_object(vm));
// 3. Return ! TemporalTimeToString(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], "auto").
auto string = MUST_OR_THROW_OOM(temporal_time_to_string(vm, temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->iso_nanosecond(), "auto"sv));
return PrimitiveString::create(vm, move(string));
}
// 4.3.23 Temporal.PlainTime.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::value_of)
{
// 1. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.PlainTime", "a primitive value");
}
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/PlainTime.h>
namespace JS::Temporal {
class PlainTimePrototype final : public PrototypeObject<PlainTimePrototype, PlainTime> {
JS_PROTOTYPE_OBJECT(PlainTimePrototype, PlainTime, Temporal.PlainTime);
GC_DECLARE_ALLOCATOR(PlainTimePrototype);
public:
virtual void initialize(Realm&) override;
virtual ~PlainTimePrototype() override = default;
private:
explicit PlainTimePrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(calendar_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(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(until);
JS_DECLARE_NATIVE_FUNCTION(since);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date_time);
JS_DECLARE_NATIVE_FUNCTION(to_zoned_date_time);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
};
}

View file

@ -1,394 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/PlainDate.h>
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(PlainYearMonth);
// 9 Temporal.PlainYearMonth Objects, https://tc39.es/proposal-temporal/#sec-temporal-plainyearmonth-objects
PlainYearMonth::PlainYearMonth(i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_iso_year(iso_year)
, m_iso_month(iso_month)
, m_iso_day(iso_day)
, m_calendar(calendar)
{
}
void PlainYearMonth::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_calendar);
}
// 9.5.1 ToTemporalYearMonth ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalyearmonth
ThrowCompletionOr<PlainYearMonth*> to_temporal_year_month(VM& vm, Value item, Object const* options)
{
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
// 3. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalYearMonth]] internal slot, then
if (is<PlainYearMonth>(item_object)) {
// i. Return item.
return static_cast<PlainYearMonth*>(&item_object);
}
// b. Let calendar be ? GetTemporalCalendarWithISODefault(item).
auto* calendar = TRY(get_temporal_calendar_with_iso_default(vm, item_object));
// c. Let fieldNames be ? CalendarFields(calendar, « "month", "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, *calendar, { "month"sv, "monthCode"sv, "year"sv }));
// d. Let fields be ? PrepareTemporalFields(item, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, item_object, field_names, Vector<StringView> {}));
// e. Return ? CalendarYearMonthFromFields(calendar, fields, options).
return calendar_year_month_from_fields(vm, *calendar, *fields, options);
}
// 4. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// 5. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// 6. Let result be ? ParseTemporalYearMonthString(string).
auto result = TRY(parse_temporal_year_month_string(vm, string));
// 7. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, result.calendar.has_value() ? PrimitiveString::create(vm, *result.calendar) : js_undefined()));
// 8. Set result to ? CreateTemporalYearMonth(result.[[Year]], result.[[Month]], calendar, result.[[Day]]).
auto* creation_result = TRY(create_temporal_year_month(vm, result.year, result.month, *calendar, result.day));
// 9. NOTE: The following operation is called without options, in order for the calendar to store a canonical value in the [[ISODay]] internal slot of the result.
// 10. Return ? CalendarYearMonthFromFields(calendar, result).
return calendar_year_month_from_fields(vm, *calendar, *creation_result);
}
// 9.5.2 RegulateISOYearMonth ( year, month, overflow ), https://tc39.es/proposal-temporal/#sec-temporal-regulateisoyearmonth
ThrowCompletionOr<ISOYearMonth> regulate_iso_year_month(VM& vm, double year, double month, StringView overflow)
{
// 1. Assert: year and month are integers.
VERIFY(year == trunc(year) && month == trunc(month));
// 2. Assert: overflow is either "constrain" or "reject".
// NOTE: Asserted by the VERIFY_NOT_REACHED at the end
// 3. If overflow is "constrain", then
if (overflow == "constrain"sv) {
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat `year` (a double) as normal integer from this point onwards.
// This does not change the exposed behavior as the subsequent call to CreateTemporalYearMonth will check that its value is a valid ISO
// values (for years: -273975 - 273975) which is a subset of this check.
// If RegulateISOYearMonth is ever used outside ISOYearMonthFromFields, this may need to be changed.
if (!AK::is_within_range<i32>(year))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
// a. Set month to the result of clamping month between 1 and 12.
month = clamp(month, 1, 12);
// b. Return the Record { [[Year]]: year, [[Month]]: month }.
return ISOYearMonth { .year = static_cast<i32>(year), .month = static_cast<u8>(month), .reference_iso_day = 0 };
}
// 4. Else,
else {
// a. Assert: overflow is "reject".
VERIFY(overflow == "reject"sv);
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to IsValidISOMonth and subsequent call to CreateTemporalDateTime will check
// that these values are valid ISO values (for years: -273975 - 273975, for months: 1 - 12) all of which are subsets of this check.
if (!AK::is_within_range<i32>(year) || !AK::is_within_range<u8>(month))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
// b. If month < 1 or month > 12, throw a RangeError exception.
if (month < 1 || month > 12)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
// c. Return the Record { [[Year]]: year, [[Month]]: month }.
return ISOYearMonth { .year = static_cast<i32>(year), .month = static_cast<u8>(month), .reference_iso_day = 0 };
}
}
// 9.5.3 ISOYearMonthWithinLimits ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-isoyearmonthwithinlimits
bool iso_year_month_within_limits(i32 year, u8 month)
{
// 1. Assert: year and month are integers.
// 2. If year < -271821 or year > 275760, then
if (year < -271821 || year > 275760) {
// a. Return false.
return false;
}
// 3. If year is -271821 and month < 4, then
if (year == -271821 && month < 4) {
// a. Return false.
return false;
}
// 4. If year is 275760 and month > 9, then
if (year == 275760 && month > 9) {
// a. Return false.
return false;
}
// 5. Return true.
return true;
}
// 9.5.4 BalanceISOYearMonth ( year, month ), https://tc39.es/proposal-temporal/#sec-temporal-balanceisoyearmonth
ISOYearMonth balance_iso_year_month(double year, double month)
{
// 1. Assert: year and month are integers.
VERIFY(year == trunc(year) && month == trunc(month));
// 2. Set year to year + floor((month - 1) / 12).
year += floor((month - 1) / 12);
// 3. Set month to ((month - 1) modulo 12) + 1.
month = modulo(month - 1, 12) + 1;
// 4. Return the Record { [[Year]]: year, [[Month]]: month }.
return ISOYearMonth { .year = static_cast<i32>(year), .month = static_cast<u8>(month), .reference_iso_day = 0 };
}
// 9.5.5 CreateTemporalYearMonth ( isoYear, isoMonth, calendar, referenceISODay [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalyearmonth
ThrowCompletionOr<PlainYearMonth*> create_temporal_year_month(VM& vm, i32 iso_year, u8 iso_month, Object& calendar, u8 reference_iso_day, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: isoYear, isoMonth, and referenceISODay are integers.
// 2. Assert: Type(calendar) is Object.
// 3. If IsValidISODate(isoYear, isoMonth, referenceISODay) is false, throw a RangeError exception.
if (!is_valid_iso_date(iso_year, iso_month, reference_iso_day))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
// 4. If ! ISOYearMonthWithinLimits(isoYear, isoMonth) is false, throw a RangeError exception.
if (!iso_year_month_within_limits(iso_year, iso_month))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
// 5. If newTarget is not present, set newTarget to %Temporal.PlainYearMonth%.
if (!new_target)
new_target = realm.intrinsics().temporal_plain_year_month_constructor();
// 6. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.PlainYearMonth.prototype%", « [[InitializedTemporalYearMonth]], [[ISOYear]], [[ISOMonth]], [[ISODay]], [[Calendar]] »).
// 7. Set object.[[ISOYear]] to isoYear.
// 8. Set object.[[ISOMonth]] to isoMonth.
// 9. Set object.[[Calendar]] to calendar.
// 10. Set object.[[ISODay]] to referenceISODay.
auto object = TRY(ordinary_create_from_constructor<PlainYearMonth>(vm, *new_target, &Intrinsics::temporal_plain_year_month_prototype, iso_year, iso_month, reference_iso_day, calendar));
// 11. Return object.
return object.ptr();
}
// 9.5.6 TemporalYearMonthToString ( yearMonth, showCalendar ), https://tc39.es/proposal-temporal/#sec-temporal-temporalyearmonthtostring
ThrowCompletionOr<String> temporal_year_month_to_string(VM& vm, PlainYearMonth& year_month, StringView show_calendar)
{
// 1. Assert: Type(yearMonth) is Object.
// 2. Assert: yearMonth has an [[InitializedTemporalYearMonth]] internal slot.
// 3. Let year be ! PadISOYear(yearMonth.[[ISOYear]]).
// 4. Let month be ToZeroPaddedDecimalString(yearMonth.[[ISOMonth]], 2).
// 5. Let result be the string-concatenation of year, the code unit 0x002D (HYPHEN-MINUS), and month.
auto result = TRY_OR_THROW_OOM(vm, String::formatted("{}-{:02}", MUST_OR_THROW_OOM(pad_iso_year(vm, year_month.iso_year())), year_month.iso_month()));
// 6. Let calendarID be ? ToString(yearMonth.[[Calendar]]).
auto calendar_id = TRY(Value(&year_month.calendar()).to_string(vm));
// 7. If showCalendar is one of "always" or "critical", or if calendarID is not "iso8601", then
if (show_calendar.is_one_of("always"sv, "critical"sv) || calendar_id != "iso8601") {
// a. Let day be ToZeroPaddedDecimalString(yearMonth.[[ISODay]], 2).
// b. Set result to the string-concatenation of result, the code unit 0x002D (HYPHEN-MINUS), and day.
result = TRY_OR_THROW_OOM(vm, String::formatted("{}-{:02}", result, year_month.iso_day()));
}
// 8. Let calendarString be ! FormatCalendarAnnotation(calendarID, showCalendar).
auto calendar_string = MUST_OR_THROW_OOM(format_calendar_annotation(vm, calendar_id, show_calendar));
// 9. Set result to the string-concatenation of result and calendarString.
// 10. Return result.
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}", result, calendar_string));
}
// 9.5.7 DifferenceTemporalPlainYearMonth ( operation, yearMonth, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplainyearmonth
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_year_month(VM& vm, DifferenceOperation operation, PlainYearMonth& year_month, Value other_value, Value options_value)
{
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalYearMonth(other).
auto* other = TRY(to_temporal_year_month(vm, other_value));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month.calendar();
// 4. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception.
if (!TRY(calendar_equals(vm, calendar, other->calendar())))
return vm.throw_completion<RangeError>(ErrorType::TemporalDifferentCalendars);
// 5. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
auto resolved_options = TRY(TRY(get_options_object(vm, options_value))->snapshot_own_properties(vm, nullptr));
// 6. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, date, « "week", "day" », "month", "year").
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Date, { "week"sv, "day"sv }, { "month"sv }, "year"sv));
// 7. If yearMonth.[[ISOYear]] = other.[[ISOYear]] and yearMonth.[[ISOMonth]] = other.[[ISOMonth]] and yearMonth.[[ISODay]] = other.[[ISODay]], then
if (year_month.iso_year() == other->iso_year() && year_month.iso_month() == other->iso_month() && year_month.iso_day() == other->iso_day()) {
// a. Return ! CreateTemporalDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0));
}
// 8. Perform ! CreateDataPropertyOrThrow(resolvedOptions, "largestUnit", settings.[[LargestUnit]]).
MUST(resolved_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, settings.largest_unit)));
// 9. Let calendarRec be ? CreateCalendarMethodsRecord(calendar, « dateAdd, dateFromFields, dateUntil, fields »).
// FIXME: The type of calendar in PlainYearMonth does not align with latest spec
auto calendar_record = TRY(create_calendar_methods_record(vm, GC::Ref<Object> { calendar }, { { CalendarMethod::DateAdd, CalendarMethod::DateFromFields, CalendarMethod::DateUntil, CalendarMethod::Fields } }));
// 10. Let fieldNames be ? CalendarFields(calendarRec, « "monthCode", "year" »).
// FIXME: Pass through calendar record
auto field_names = TRY(calendar_fields(vm, calendar, { "monthCode"sv, "year"sv }));
// 11. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
auto* this_fields = TRY(prepare_temporal_fields(vm, year_month, field_names, Vector<StringView> {}));
// 12. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1)));
// 13. Let thisDate be ? CalendarDateFromFields(calendarRec, thisFields).
// FIXME: Pass through calendar record
auto* this_date = TRY(calendar_date_from_fields(vm, calendar, *this_fields));
// 14. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
auto* other_fields = TRY(prepare_temporal_fields(vm, *other, field_names, Vector<StringView> {}));
// 15. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1)));
// 16. Let otherDate be ? CalendarDateFromFields(calendarRec, otherFields).
// FIXME: Pass through calendar record
auto* other_date = TRY(calendar_date_from_fields(vm, calendar, *other_fields));
// 17. Perform ! CreateDataPropertyOrThrow(resolvedOptions, "largestUnit", settings.[[LargestUnit]]).
MUST(resolved_options->create_data_property_or_throw(vm.names.largestUnit, PrimitiveString::create(vm, settings.largest_unit)));
// 18. Let result be ? CalendarDateUntil(calendarRec, thisDate, otherDate, resolvedOptions).
auto result = TRY(calendar_date_until(vm, calendar_record, this_date, other_date, *resolved_options));
// 19. If settings.[[SmallestUnit]] is not "month" or settings.[[RoundingIncrement]] ≠ 1, then
if (settings.smallest_unit != "month"sv || settings.rounding_increment != 1) {
// a. Let roundRecord be ? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], thisDate, calendarRec).
auto round_record = TRY(round_duration(vm, result->years(), result->months(), 0, 0, 0, 0, 0, 0, 0, 0, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, this_date, calendar_record));
// b. Let roundResult be roundRecord.[[DurationRecord]].
auto round_result = round_record.duration_record;
// FIXME: c. Set result to ? BalanceDateDurationRelative(roundResult.[[Years]], roundResult.[[Months]], 0, 0, settings.[[LargestUnit]], settings.[[SmallestUnit]], thisDate, calendarRec).
result = MUST(create_temporal_duration(vm, round_result.years, round_result.months, 0, 0, 0, 0, 0, 0, 0, 0));
}
// 20. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(vm, sign * result->years(), sign * result->months(), 0, 0, 0, 0, 0, 0, 0, 0));
}
// 9.5.8 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplainyearmonth
ThrowCompletionOr<PlainYearMonth*> add_duration_to_or_subtract_duration_from_plain_year_month(VM& vm, ArithmeticOperation operation, PlainYearMonth& year_month, Value temporal_duration_like, Value options_value)
{
auto& realm = *vm.current_realm();
// 1. Let duration be ? ToTemporalDuration(temporalDurationLike).
auto duration = TRY(to_temporal_duration(vm, temporal_duration_like));
// 2. If operation is subtract, then
if (operation == ArithmeticOperation::Subtract) {
// a. Set duration to ! CreateNegatedTemporalDuration(duration).
duration = create_negated_temporal_duration(vm, *duration);
}
// 3. Let balanceResult be ? BalanceDuration(duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "day").
auto balance_result = TRY(balance_duration(vm, duration->days(), duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), Crypto::SignedBigInteger { duration->nanoseconds() }, "day"sv));
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, options_value));
// 5. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month.calendar();
// 6. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "monthCode"sv, "year"sv }));
// 7. Let fields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, year_month, field_names, Vector<StringView> {}));
// 8. Set sign to ! DurationSign(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0).
auto sign = duration_sign(duration->years(), duration->months(), duration->weeks(), balance_result.days, 0, 0, 0, 0, 0, 0);
double day;
// 9. If sign < 0, then
if (sign < 0) {
// a. Let day be ? CalendarDaysInMonth(calendar, yearMonth).
day = TRY(calendar_days_in_month(vm, calendar, year_month));
}
// 10. Else,
else {
// a. Let day be 1.
day = 1;
}
// 11. Perform ! CreateDataPropertyOrThrow(fields, "day", 𝔽(day)).
MUST(fields->create_data_property_or_throw(vm.names.day, Value(day)));
// 12. Let date be ? CalendarDateFromFields(calendar, fields).
auto* date = TRY(calendar_date_from_fields(vm, calendar, *fields));
// 13. Let durationToAdd be ! CreateTemporalDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], balanceResult.[[Days]], 0, 0, 0, 0, 0, 0).
auto duration_to_add = MUST(create_temporal_duration(vm, duration->years(), duration->months(), duration->weeks(), balance_result.days, 0, 0, 0, 0, 0, 0));
// 14. Let optionsCopy be OrdinaryObjectCreate(null).
auto options_copy = Object::create(realm, nullptr);
// 15. Let entries be ? EnumerableOwnPropertyNames(options, key+value).
auto entries = TRY(options->enumerable_own_property_names(Object::PropertyKind::KeyAndValue));
// 16. For each element entry of entries, do
for (auto& entry : entries) {
auto key = MUST(entry.as_array().get_without_side_effects(0).to_property_key(vm));
auto value = entry.as_array().get_without_side_effects(1);
// a. Perform ! CreateDataPropertyOrThrow(optionsCopy, entry[0], entry[1]).
MUST(options_copy->create_data_property_or_throw(key, value));
}
// 17. Let addedDate be ? CalendarDateAdd(calendar, date, durationToAdd, options).
auto* added_date = TRY(calendar_date_add(vm, calendar, date, *duration_to_add, options));
// 18. Let addedDateFields be ? PrepareTemporalFields(addedDate, fieldNames, «»).
auto* added_date_fields = TRY(prepare_temporal_fields(vm, *added_date, field_names, Vector<StringView> {}));
// 19. Return ? CalendarYearMonthFromFields(calendar, addedDateFields, optionsCopy).
return calendar_year_month_from_fields(vm, calendar, *added_date_fields, options_copy);
}
}

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
namespace JS::Temporal {
class PlainYearMonth final : public Object {
JS_OBJECT(PlainYearMonth, Object);
GC_DECLARE_ALLOCATOR(PlainYearMonth);
public:
virtual ~PlainYearMonth() override = default;
[[nodiscard]] i32 iso_year() const { return m_iso_year; }
[[nodiscard]] u8 iso_month() const { return m_iso_month; }
[[nodiscard]] u8 iso_day() const { return m_iso_day; }
[[nodiscard]] Object const& calendar() const { return m_calendar; }
[[nodiscard]] Object& calendar() { return m_calendar; }
private:
PlainYearMonth(i32 iso_year, u8 iso_month, u8 iso_day, Object& calendar, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 9.4 Properties of Temporal.PlainYearMonth Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-plainyearmonth-instances
i32 m_iso_year { 0 }; // [[ISOYear]]
u8 m_iso_month { 0 }; // [[ISOMonth]]
u8 m_iso_day { 0 }; // [[ISODay]]
GC::Ref<Object> m_calendar; // [[Calendar]]
};
struct ISOYearMonth {
i32 year;
u8 month;
u8 reference_iso_day;
};
ThrowCompletionOr<PlainYearMonth*> to_temporal_year_month(VM&, Value item, Object const* options = nullptr);
ThrowCompletionOr<ISOYearMonth> regulate_iso_year_month(VM&, double year, double month, StringView overflow);
bool iso_year_month_within_limits(i32 year, u8 month);
ISOYearMonth balance_iso_year_month(double year, double month);
ThrowCompletionOr<PlainYearMonth*> create_temporal_year_month(VM&, i32 iso_year, u8 iso_month, Object& calendar, u8 reference_iso_day, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<String> temporal_year_month_to_string(VM&, PlainYearMonth&, StringView show_calendar);
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_plain_year_month(VM&, DifferenceOperation, PlainYearMonth&, Value other, Value options);
ThrowCompletionOr<PlainYearMonth*> add_duration_to_or_subtract_duration_from_plain_year_month(VM&, ArithmeticOperation, PlainYearMonth&, Value temporal_duration_like, Value options_value);
}

View file

@ -1,124 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#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/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 [ , calendarLike [ , referenceISODay ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth
ThrowCompletionOr<Value> PlainYearMonthConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.PlainYearMonth");
}
// 9.1.1 Temporal.PlainYearMonth ( isoYear, isoMonth [ , calendarLike [ , 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_like = 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 y = TRY(to_integer_with_truncation(vm, iso_year, ErrorType::TemporalInvalidPlainYearMonth));
// 4. Let m be ? ToIntegerWithTruncation(isoMonth).
auto m = TRY(to_integer_with_truncation(vm, iso_month, ErrorType::TemporalInvalidPlainYearMonth));
// 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, calendar_like));
// 6. Let ref be ? ToIntegerWithTruncation(referenceISODay).
auto ref = TRY(to_integer_with_truncation(vm, reference_iso_day, ErrorType::TemporalInvalidPlainYearMonth));
// IMPLEMENTATION DEFINED: This is an optimization that allows us to treat these doubles as normal integers from this point onwards.
// This does not change the exposed behavior as the call to CreateTemporalYearMonth will immediately check that these values are valid
// ISO values (for years: -273975 - 273975, for months: 1 - 12, for days: 1 - 31) all of which are subsets of this check.
if (!AK::is_within_range<i32>(y) || !AK::is_within_range<u8>(m) || !AK::is_within_range<u8>(ref))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidPlainYearMonth);
// 7. Return ? CreateTemporalYearMonth(y, m, calendar, ref, NewTarget).
return *TRY(create_temporal_year_month(vm, y, m, *calendar, ref, &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. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
auto item = vm.argument(0);
// 2. If Type(item) is Object and item has an [[InitializedTemporalYearMonth]] internal slot, then
if (item.is_object() && is<PlainYearMonth>(item.as_object())) {
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
auto& plain_year_month_object = static_cast<PlainYearMonth&>(item.as_object());
// b. Return ! CreateTemporalYearMonth(item.[[ISOYear]], item.[[ISOMonth]], item.[[Calendar]], item.[[ISODay]]).
return MUST(create_temporal_year_month(vm, plain_year_month_object.iso_year(), plain_year_month_object.iso_month(), plain_year_month_object.calendar(), plain_year_month_object.iso_day()));
}
// 3. Return ? ToTemporalYearMonth(item, options).
return TRY(to_temporal_year_month(vm, item, options));
}
// 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(one).
auto* two = TRY(to_temporal_year_month(vm, vm.argument(1)));
// 3. Return 𝔽(! CompareISODate(one.[[ISOYear]], one.[[ISOMonth]], one.[[ISODay]], two.[[ISOYear]], two.[[ISOMonth]], two.[[ISODay]])).
return Value(compare_iso_date(one->iso_year(), one->iso_month(), one->iso_day(), two->iso_year(), two->iso_month(), two->iso_day()));
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.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);
};
}

View file

@ -1,463 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Duration.h>
#include <LibJS/Runtime/Temporal/PlainYearMonth.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[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype-@@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.calendar, calendar_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.calendarId, calendar_id_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);
define_native_accessor(realm, vm.names.era, era_getter, {}, Attribute::Configurable);
define_native_accessor(realm, vm.names.eraYear, era_year_getter, {}, Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.with, with, 1, attr);
define_native_function(realm, vm.names.add, add, 1, attr);
define_native_function(realm, vm.names.subtract, subtract, 1, attr);
define_native_function(realm, vm.names.until, until, 1, attr);
define_native_function(realm, vm.names.since, since, 1, attr);
define_native_function(realm, vm.names.equals, equals, 1, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
define_native_function(realm, vm.names.toPlainDate, to_plain_date, 1, attr);
define_native_function(realm, vm.names.getISOFields, get_iso_fields, 0, attr);
}
// 9.3.3 get Temporal.PlainYearMonth.prototype.calendar, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.calendar
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::calendar_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 Value(&year_month->calendar());
}
// 9.3.4 get Temporal.PlainYearMonth.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.year
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::year_getter)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return 𝔽(? CalendarYear(calendar, yearMonth)).
return Value(TRY(calendar_year(vm, calendar, year_month)));
}
// 9.3.5 get Temporal.PlainYearMonth.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.month
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::month_getter)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return 𝔽(? CalendarMonth(calendar, yearMonth)).
return Value(TRY(calendar_month(vm, calendar, year_month)));
}
// 9.3.6 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. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return ? CalendarMonthCode(calendar, yearMonth).
return PrimitiveString::create(vm, TRY(calendar_month_code(vm, calendar, year_month)));
}
// 9.3.7 get Temporal.PlainYearMonth.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.daysinyear
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::days_in_year_getter)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return 𝔽(? CalendarDaysInYear(calendar, yearMonth)).
return TRY(calendar_days_in_year(vm, calendar, year_month));
}
// 9.3.8 get Temporal.PlainYearMonth.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.daysinmonth
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::days_in_month_getter)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return 𝔽(? CalendarDaysInMonth(calendar, yearMonth)).
return TRY(calendar_days_in_month(vm, calendar, year_month));
}
// 9.3.9 get Temporal.PlainYearMonth.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.monthsinyear
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::months_in_year_getter)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return 𝔽(? CalendarMonthsInYear(calendar, yearMonth)).
return TRY(calendar_months_in_year(vm, calendar, year_month));
}
// 9.3.10 get Temporal.PlainYearMonth.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.plainyearmonth.prototype.inleapyear
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::in_leap_year_getter)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 4. Return ? CalendarInLeapYear(calendar, yearMonth).
return Value(TRY(calendar_in_leap_year(vm, calendar, year_month)));
}
// 15.6.9.2 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 plain_year_month = TRY(typed_this_object(vm));
// 3. Let calendar be plainYearMonth.[[Calendar]].
auto& calendar = plain_year_month->calendar();
// 4. Return ? CalendarEra(calendar, plainYearMonth).
return TRY(calendar_era(vm, calendar, plain_year_month));
}
// 15.6.9.3 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 plain_year_month = TRY(typed_this_object(vm));
// 3. Let calendar be plainYearMonth.[[Calendar]].
auto& calendar = plain_year_month->calendar();
// 4. Return ? CalendarEraYear(calendar, plainYearMonth).
return TRY(calendar_era_year(vm, calendar, plain_year_month));
}
// 9.3.11 Temporal.PlainYearMonth.prototype.with ( temporalYearMonthLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.with
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::with)
{
auto temporal_year_month_like = vm.argument(0);
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. If Type(temporalYearMonthLike) is not Object, then
if (!temporal_year_month_like.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, temporal_year_month_like.to_string_without_side_effects());
}
// 4. Perform ? RejectObjectWithCalendarOrTimeZone(temporalYearMonthLike).
TRY(reject_object_with_calendar_or_time_zone(vm, temporal_year_month_like.as_object()));
// 5. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 6. Let fieldNames be ? CalendarFields(calendar, « "month", "monthCode", "year" »).
auto field_names = TRY(calendar_fields(vm, calendar, { "month"sv, "monthCode"sv, "year"sv }));
// 7. Let partialYearMonth be ? PrepareTemporalFields(temporalYearMonthLike, fieldNames, partial).
auto* partial_year_month = TRY(prepare_temporal_fields(vm, temporal_year_month_like.as_object(), field_names, PrepareTemporalFieldsPartial {}));
// 8. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 9. Let fields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, year_month, field_names, Vector<StringView> {}));
// 10. Set fields to ? CalendarMergeFields(calendar, fields, partialYearMonth).
fields = TRY(calendar_merge_fields(vm, calendar, *fields, *partial_year_month));
// 11. Set fields to ? PrepareTemporalFields(fields, fieldNames, «»).
fields = TRY(prepare_temporal_fields(vm, *fields, field_names, Vector<StringView> {}));
// 12. Return ? CalendarYearMonthFromFields(calendar, fields, options).
return TRY(calendar_year_month_from_fields(vm, calendar, *fields, options));
}
// 9.3.12 Temporal.PlainYearMonth.prototype.add ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.add
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::add)
{
auto temporal_duration_like = vm.argument(0);
auto options = vm.argument(1);
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromPlainYearMonth(add, yearMonth, temporalDurationLike, options).
return TRY(add_duration_to_or_subtract_duration_from_plain_year_month(vm, ArithmeticOperation::Add, year_month, temporal_duration_like, options));
}
// 9.3.13 Temporal.PlainYearMonth.prototype.subtract ( temporalDurationLike [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.subtract
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::subtract)
{
auto temporal_duration_like = vm.argument(0);
auto options = vm.argument(1);
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Return ? AddDurationToOrSubtractDurationFromPlainYearMonth(add, yearMonth, temporalDurationLike, options).
return TRY(add_duration_to_or_subtract_duration_from_plain_year_month(vm, ArithmeticOperation::Subtract, year_month, temporal_duration_like, options));
}
// 9.3.14 Temporal.PlainYearMonth.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.until
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::until)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainYearMonth(until, yearMonth, other, options).
return TRY(difference_temporal_plain_year_month(vm, DifferenceOperation::Until, year_month, other, options));
}
// 9.3.15 Temporal.PlainYearMonth.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.since
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::since)
{
auto other = vm.argument(0);
auto options = vm.argument(1);
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Return ? DifferenceTemporalPlainYearMonth(since, yearMonth, other, options).
return TRY(difference_temporal_plain_year_month(vm, DifferenceOperation::Since, year_month, other, options));
}
// 9.3.16 Temporal.PlainYearMonth.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::equals)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Set other to ? ToTemporalYearMonth(other).
auto* other = TRY(to_temporal_year_month(vm, vm.argument(0)));
// 4. If yearMonth.[[ISOYear]] ≠ other.[[ISOYear]], return false.
if (year_month->iso_year() != other->iso_year())
return Value(false);
// 5. If yearMonth.[[ISOMonth]] ≠ other.[[ISOMonth]], return false.
if (year_month->iso_month() != other->iso_month())
return Value(false);
// 6. If yearMonth.[[ISODay]] ≠ other.[[ISODay]], return false.
if (year_month->iso_day() != other->iso_day())
return Value(false);
// 7. Return ? CalendarEquals(yearMonth.[[Calendar]], other.[[Calendar]]).
return Value(TRY(calendar_equals(vm, year_month->calendar(), other->calendar())));
}
// 9.3.17 Temporal.PlainYearMonth.prototype.toString ( [ options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::to_string)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(0)));
// 4. Let showCalendar be ? ToCalendarNameOption(options).
auto show_calendar = TRY(to_calendar_name_option(vm, *options));
// 5. Return ? TemporalYearMonthToString(yearMonth, showCalendar).
return PrimitiveString::create(vm, TRY(temporal_year_month_to_string(vm, year_month, show_calendar)));
}
// 9.3.18 Temporal.PlainYearMonth.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.tolocalestring
// NOTE: This is the minimum toLocaleString implementation for engines without ECMA-402.
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::to_locale_string)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Return ? TemporalYearMonthToString(yearMonth, "auto").
return PrimitiveString::create(vm, TRY(temporal_year_month_to_string(vm, year_month, "auto"sv)));
}
// 9.3.19 Temporal.PlainYearMonth.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::to_json)
{
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Return ? TemporalYearMonthToString(yearMonth, "auto").
return PrimitiveString::create(vm, TRY(temporal_year_month_to_string(vm, year_month, "auto"sv)));
}
// 9.3.20 Temporal.PlainYearMonth.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.valueof
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::value_of)
{
// 1. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.PlainYearMonth", "a primitive value");
}
// 9.3.21 Temporal.PlainYearMonth.prototype.toPlainDate ( item ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.toplaindate
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::to_plain_date)
{
auto& realm = *vm.current_realm();
auto item = vm.argument(0);
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. If Type(item) is not Object, then
if (!item.is_object()) {
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::NotAnObject, item);
}
// 4. Let calendar be yearMonth.[[Calendar]].
auto& calendar = year_month->calendar();
// 5. Let receiverFieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto receiver_field_names = TRY(calendar_fields(vm, calendar, { "monthCode"sv, "year"sv }));
// 6. Let fields be ? PrepareTemporalFields(yearMonth, receiverFieldNames, «»).
auto* fields = TRY(prepare_temporal_fields(vm, year_month, receiver_field_names, Vector<StringView> {}));
// 7. Let inputFieldNames be ? CalendarFields(calendar, « "day" »).
auto input_field_names = TRY(calendar_fields(vm, calendar, { "day"sv }));
// 8. Let inputFields be ? PrepareTemporalFields(item, inputFieldNames, «»).
auto* input_fields = TRY(prepare_temporal_fields(vm, item.as_object(), input_field_names, Vector<StringView> {}));
// 9. Let mergedFields be ? CalendarMergeFields(calendar, fields, inputFields).
auto* merged_fields = TRY(calendar_merge_fields(vm, calendar, *fields, *input_fields));
// 10. Let mergedFieldNames be MergeLists(receiverFieldNames, inputFieldNames).
auto merged_field_names = merge_lists(receiver_field_names, input_field_names);
// 11. Set mergedFields to ? PrepareTemporalFields(mergedFields, mergedFieldNames, «»).
merged_fields = TRY(prepare_temporal_fields(vm, *merged_fields, merged_field_names, Vector<StringView> {}));
// 12. Let options be OrdinaryObjectCreate(null).
auto options = Object::create(realm, nullptr);
// 13. Perform ! CreateDataPropertyOrThrow(options, "overflow", "reject").
MUST(options->create_data_property_or_throw(vm.names.overflow, PrimitiveString::create(vm, vm.names.reject.as_string())));
// 14. Return ? CalendarDateFromFields(calendar, mergedFields, options).
return TRY(calendar_date_from_fields(vm, calendar, *merged_fields, options));
}
// 9.3.22 Temporal.PlainYearMonth.prototype.getISOFields ( ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.getisofields
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::get_iso_fields)
{
auto& realm = *vm.current_realm();
// 1. Let yearMonth be the this value.
// 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// 3. Let fields be OrdinaryObjectCreate(%Object.prototype%).
auto fields = Object::create(realm, realm.intrinsics().object_prototype());
// 4. Perform ! CreateDataPropertyOrThrow(fields, "calendar", yearMonth.[[Calendar]]).
MUST(fields->create_data_property_or_throw(vm.names.calendar, Value(&year_month->calendar())));
// 5. Perform ! CreateDataPropertyOrThrow(fields, "isoDay", 𝔽(yearMonth.[[ISODay]])).
MUST(fields->create_data_property_or_throw(vm.names.isoDay, Value(year_month->iso_day())));
// 6. Perform ! CreateDataPropertyOrThrow(fields, "isoMonth", 𝔽(yearMonth.[[ISOMonth]])).
MUST(fields->create_data_property_or_throw(vm.names.isoMonth, Value(year_month->iso_month())));
// 7. Perform ! CreateDataPropertyOrThrow(fields, "isoYear", 𝔽(yearMonth.[[ISOYear]])).
MUST(fields->create_data_property_or_throw(vm.names.isoYear, Value(year_month->iso_year())));
// 8. Return fields.
return fields;
}
// 9.3.3 get Temporal.PlainYearMonth.prototype.calendarId
JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::calendar_id_getter)
{
// Step 1: Let yearMonth be the this value
// Step 2: Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]).
auto year_month = TRY(typed_this_object(vm));
// Step 3: Return yearMonth.[[Calendar]].identifier
auto& calendar = static_cast<Calendar&>(year_month->calendar());
return PrimitiveString::create(vm, calendar.identifier());
}
}

View file

@ -1,50 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.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_getter);
JS_DECLARE_NATIVE_FUNCTION(calendar_id_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);
JS_DECLARE_NATIVE_FUNCTION(era_getter);
JS_DECLARE_NATIVE_FUNCTION(era_year_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(until);
JS_DECLARE_NATIVE_FUNCTION(since);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
};
}

View file

@ -1,22 +1,13 @@
/* /*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
* *
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/CalendarConstructor.h>
#include <LibJS/Runtime/Temporal/DurationConstructor.h> #include <LibJS/Runtime/Temporal/DurationConstructor.h>
#include <LibJS/Runtime/Temporal/InstantConstructor.h>
#include <LibJS/Runtime/Temporal/Now.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>
#include <LibJS/Runtime/Temporal/Temporal.h> #include <LibJS/Runtime/Temporal/Temporal.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS::Temporal { namespace JS::Temporal {
@ -34,21 +25,11 @@ void Temporal::initialize(Realm& realm)
auto& vm = this->vm(); auto& vm = this->vm();
// 1.1.1 Temporal [ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal-@@tostringtag // 1.1.1 Temporal [ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal-%symbol.tostringtag%
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal"_string), Attribute::Configurable); define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal"_string), Attribute::Configurable);
u8 attr = Attribute::Writable | Attribute::Configurable; u8 attr = Attribute::Writable | Attribute::Configurable;
define_direct_property(vm.names.Now, realm.create<Now>(realm), attr);
define_intrinsic_accessor(vm.names.Calendar, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_calendar_constructor(); });
define_intrinsic_accessor(vm.names.Duration, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_duration_constructor(); }); define_intrinsic_accessor(vm.names.Duration, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_duration_constructor(); });
define_intrinsic_accessor(vm.names.Instant, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_instant_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(); });
define_intrinsic_accessor(vm.names.TimeZone, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_time_zone_constructor(); });
define_intrinsic_accessor(vm.names.ZonedDateTime, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_zoned_date_time_constructor(); });
} }
} }

View file

@ -1,648 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/Time.h>
#include <AK/TypeCasts.h>
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Iterator.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
#include <LibJS/Runtime/Temporal/TimeZoneMethods.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibUnicode/TimeZone.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(TimeZone);
// 11 Temporal.TimeZone Objects, https://tc39.es/proposal-temporal/#sec-temporal-timezone-objects
TimeZone::TimeZone(Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
{
}
// 11.1.1 IsAvailableTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sec-isavailabletimezonename
bool is_available_time_zone_name(StringView time_zone)
{
// 1. Let timeZones be AvailableTimeZones().
auto const& time_zones = Unicode::available_time_zones();
// 2. For each String candidate in timeZones, do
for (auto const& candidate : time_zones) {
// a. If timeZone is an ASCII-case-insensitive match for candidate, return true.
if (time_zone.equals_ignoring_ascii_case(candidate))
return true;
}
// 3. Return false.
return false;
}
// 6.4.2 CanonicalizeTimeZoneName ( timeZone ), https://tc39.es/ecma402/#sec-canonicalizetimezonename
// 11.1.2 CanonicalizeTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sec-canonicalizetimezonename
// 15.1.2 CanonicalizeTimeZoneName ( timeZone ), https://tc39.es/proposal-temporal/#sup-canonicalizetimezonename
ThrowCompletionOr<String> canonicalize_time_zone_name(VM&, StringView time_zone)
{
auto const& time_zones = Unicode::available_time_zones();
// 1. Let ianaTimeZone be the String value of the Zone or Link name of the IANA Time Zone Database that is an
// ASCII-case-insensitive match of timeZone as described in 6.1.
auto it = time_zones.find_if([&](auto const& candidate) {
return time_zone.equals_ignoring_ascii_case(candidate);
});
VERIFY(it != time_zones.end());
// 2. If ianaTimeZone is a Link name, let ianaTimeZone be the String value of the corresponding Zone name as specified
// in the file backward of the IANA Time Zone Database.
auto iana_time_zone = Unicode::resolve_primary_time_zone(*it).value_or_lazy_evaluated([&]() {
return MUST(String::from_utf8(time_zone));
});
// 3. If ianaTimeZone is one of "Etc/UTC", "Etc/GMT", or "GMT", return "UTC".
if (iana_time_zone.is_one_of("Etc/UTC"sv, "Etc/GMT"sv, "GMT"sv))
return "UTC"_string;
// 4. Return ianaTimeZone.
return iana_time_zone;
}
// 11.6.1 CreateTemporalTimeZone ( identifier [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporaltimezone
ThrowCompletionOr<TimeZone*> create_temporal_time_zone(VM& vm, String identifier, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. If newTarget is not present, set newTarget to %Temporal.TimeZone%.
if (!new_target)
new_target = realm.intrinsics().temporal_time_zone_constructor();
// 2. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.TimeZone.prototype%", « [[InitializedTemporalTimeZone]], [[Identifier]], [[OffsetNanoseconds]] »).
auto object = TRY(ordinary_create_from_constructor<TimeZone>(vm, *new_target, &Intrinsics::temporal_time_zone_prototype));
// 3. If IsTimeZoneOffsetString(identifier) is true, then
if (is_time_zone_offset_string(identifier)) {
// a. Let offsetNanosecondsResult be ParseTimeZoneOffsetString(identifier).
auto offset_nanoseconds_result = parse_time_zone_offset_string(identifier);
// b. Set object.[[Identifier]] to ! FormatTimeZoneOffsetString(offsetNanosecondsResult).
object->set_identifier(MUST_OR_THROW_OOM(format_time_zone_offset_string(vm, offset_nanoseconds_result)));
// c. Set object.[[OffsetNanoseconds]] to offsetNanosecondsResult.
object->set_offset_nanoseconds(offset_nanoseconds_result);
}
// 4. Else,
else {
// a. Assert: ! CanonicalizeTimeZoneName(identifier) is identifier.
VERIFY(MUST_OR_THROW_OOM(canonicalize_time_zone_name(vm, identifier)) == identifier);
// b. Set object.[[Identifier]] to identifier.
object->set_identifier(move(identifier));
// c. Set object.[[OffsetNanoseconds]] to undefined.
// NOTE: No-op.
}
// 5. Return object.
return object.ptr();
}
// 11.6.2 GetISOPartsFromEpoch ( epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-getisopartsfromepoch
ISODateTime get_iso_parts_from_epoch(VM& vm, Crypto::SignedBigInteger const& epoch_nanoseconds)
{
// 1. Assert: ! IsValidEpochNanoseconds((epochNanoseconds)) is true.
VERIFY(is_valid_epoch_nanoseconds(BigInt::create(vm, epoch_nanoseconds)));
// 2. Let remainderNs be epochNanoseconds modulo 10^6.
auto remainder_ns_bigint = modulo(epoch_nanoseconds, Crypto::UnsignedBigInteger { 1'000'000 });
auto remainder_ns = remainder_ns_bigint.to_double();
// 3. Let epochMilliseconds be 𝔽((epochNanoseconds - remainderNs) / 10^6).
auto epoch_milliseconds_bigint = epoch_nanoseconds.minus(remainder_ns_bigint).divided_by(Crypto::UnsignedBigInteger { 1'000'000 }).quotient;
auto epoch_milliseconds = epoch_milliseconds_bigint.to_double();
// 4. Let year be (! YearFromTime(epochMilliseconds)).
auto year = year_from_time(epoch_milliseconds);
// 5. Let month be (! MonthFromTime(epochMilliseconds)) + 1.
auto month = static_cast<u8>(month_from_time(epoch_milliseconds) + 1);
// 6. Let day be (! DateFromTime(epochMilliseconds)).
auto day = date_from_time(epoch_milliseconds);
// 7. Let hour be (! HourFromTime(epochMilliseconds)).
auto hour = hour_from_time(epoch_milliseconds);
// 8. Let minute be (! MinFromTime(epochMilliseconds)).
auto minute = min_from_time(epoch_milliseconds);
// 9. Let second be (! SecFromTime(epochMilliseconds)).
auto second = sec_from_time(epoch_milliseconds);
// 10. Let millisecond be (! msFromTime(epochMilliseconds)).
auto millisecond = ms_from_time(epoch_milliseconds);
// 11. Let microsecond be floor(remainderNs / 1000).
auto microsecond = floor(remainder_ns / 1000);
// 12. Assert: microsecond < 1000.
VERIFY(microsecond < 1000);
// 13. Let nanosecond be remainderNs modulo 1000.
auto nanosecond = modulo(remainder_ns, 1000);
// 14. Return the Record { [[Year]]: year, [[Month]]: month, [[Day]]: day, [[Hour]]: hour, [[Minute]]: minute, [[Second]]: second, [[Millisecond]]: millisecond, [[Microsecond]]: microsecond, [[Nanosecond]]: nanosecond }.
return { .year = year, .month = month, .day = day, .hour = hour, .minute = minute, .second = second, .millisecond = millisecond, .microsecond = static_cast<u16>(microsecond), .nanosecond = static_cast<u16>(nanosecond) };
}
// 11.6.3 GetNamedTimeZoneNextTransition ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezonenexttransition
BigInt* get_named_time_zone_next_transition(VM&, [[maybe_unused]] StringView time_zone_identifier, [[maybe_unused]] BigInt const& epoch_nanoseconds)
{
// The implementation-defined abstract operation GetNamedTimeZoneNextTransition takes arguments timeZoneIdentifier (a String) and epochNanoseconds (a BigInt) and returns a BigInt or null.
// The returned value t represents the number of nanoseconds since the Unix epoch in UTC that corresponds to the first time zone transition after epochNanoseconds in the IANA time zone identified by timeZoneIdentifier. The operation returns null if no such transition exists for which t ≤ (nsMaxInstant).
// Given the same values of epochNanoseconds and timeZoneIdentifier, the result must be the same for the lifetime of the surrounding agent.
// TODO: Implement this
return nullptr;
}
// 11.6.4 GetNamedTimeZonePreviousTransition ( timeZoneIdentifier, epochNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-getianatimezoneprevioustransition
BigInt* get_named_time_zone_previous_transition(VM&, [[maybe_unused]] StringView time_zone_identifier, [[maybe_unused]] BigInt const& epoch_nanoseconds)
{
// The implementation-defined abstract operation GetNamedTimeZonePreviousTransition takes arguments timeZoneIdentifier (a String) and epochNanoseconds (a BigInt) and returns a BigInt or null.
// The returned value t represents the number of nanoseconds since the Unix epoch in UTC that corresponds to the last time zone transition before epochNanoseconds in the IANA time zone identified by timeZoneIdentifier. The operation returns null if no such transition exists for which t ≥ (nsMinInstant).
// Given the same values of epochNanoseconds and timeZoneIdentifier, the result must be the same for the lifetime of the surrounding agent.
// TODO: Implement this
return nullptr;
}
// 11.6.5 FormatTimeZoneOffsetString ( offsetNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-formattimezoneoffsetstring
ThrowCompletionOr<String> format_time_zone_offset_string(VM& vm, double offset_nanoseconds)
{
auto offset = static_cast<i64>(offset_nanoseconds);
// 1. Assert: offsetNanoseconds is an integer.
VERIFY(offset == offset_nanoseconds);
StringBuilder builder;
// 2. If offsetNanoseconds ≥ 0, let sign be "+"; otherwise, let sign be "-".
if (offset >= 0)
builder.append('+');
else
builder.append('-');
// 3. Let _offsetNanoseconds_ be abs(_offsetNanoseconds_).
offset = AK::abs(offset);
// 4. Let nanoseconds be offsetNanoseconds modulo 10^9.
auto nanoseconds = offset % 1000000000;
// 5. Let seconds be floor(offsetNanoseconds / 10^9) modulo 60.
auto seconds = (offset / 1000000000) % 60;
// 6. Let minutes be floor(offsetNanoseconds / (6 × 10^10)) modulo 60.
auto minutes = (offset / 60000000000) % 60;
// 7. Let hours be floor(offsetNanoseconds / (3.6 × 10^12)).
auto hours = offset / 3600000000000;
// 8. Let h be ToZeroPaddedDecimalString(hours, 2).
builder.appendff("{:02}", hours);
// 9. Let m be ToZeroPaddedDecimalString(minutes, 2).
builder.appendff(":{:02}", minutes);
// 10. Let s be ToZeroPaddedDecimalString(seconds, 2).
// NOTE: Handled by steps 11 & 12
// 11. If nanoseconds ≠ 0, then
if (nanoseconds != 0) {
// a. Let fraction be ToZeroPaddedDecimalString(nanoseconds, 9).
auto fraction = TRY_OR_THROW_OOM(vm, String::formatted("{:09}", nanoseconds));
// b. Set fraction to the longest possible substring of fraction starting at position 0 and not ending with the code unit 0x0030 (DIGIT ZERO).
fraction = TRY_OR_THROW_OOM(vm, fraction.trim("0"sv, TrimMode::Right));
// c. Let post be the string-concatenation of the code unit 0x003A (COLON), s, the code unit 0x002E (FULL STOP), and fraction.
builder.appendff(":{:02}.{}", seconds, fraction);
}
// 12. Else if seconds ≠ 0, then
else if (seconds != 0) {
// a. Let post be the string-concatenation of the code unit 0x003A (COLON) and s.
builder.appendff(":{:02}", seconds);
}
// 13. Else,
// a. Let post be the empty String.
// 14. Return the string-concatenation of sign, h, the code unit 0x003A (COLON), m, and post.
return TRY_OR_THROW_OOM(vm, builder.to_string());
}
// 11.6.6 FormatISOTimeZoneOffsetString ( offsetNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-formatisotimezoneoffsetstring
ThrowCompletionOr<String> format_iso_time_zone_offset_string(VM& vm, double offset_nanoseconds)
{
// 1. Assert: offsetNanoseconds is an integer.
VERIFY(trunc(offset_nanoseconds) == offset_nanoseconds);
// 2. Set offsetNanoseconds to RoundNumberToIncrement(offsetNanoseconds, 60 × 10^9, "halfExpand").
offset_nanoseconds = round_number_to_increment(offset_nanoseconds, 60000000000, "halfExpand"sv);
// 3. If offsetNanoseconds ≥ 0, let sign be "+"; otherwise, let sign be "-".
auto sign = offset_nanoseconds >= 0 ? "+"sv : "-"sv;
// 4. Set offsetNanoseconds to abs(offsetNanoseconds).
offset_nanoseconds = fabs(offset_nanoseconds);
// 5. Let minutes be offsetNanoseconds / (60 × 10^9) modulo 60.
auto minutes = fmod(offset_nanoseconds / 60000000000, 60);
// 6. Let hours be floor(offsetNanoseconds / (3600 × 10^9)).
auto hours = floor(offset_nanoseconds / 3600000000000);
// 7. Let h be ToZeroPaddedDecimalString(hours, 2).
// 8. Let m be ToZeroPaddedDecimalString(minutes, 2).
// 9. Return the string-concatenation of sign, h, the code unit 0x003A (COLON), and m.
return TRY_OR_THROW_OOM(vm, String::formatted("{}{:02}:{:02}", sign, (u32)hours, (u32)minutes));
}
// 11.6.7 ToTemporalTimeZone ( temporalTimeZoneLike ), https://tc39.es/proposal-temporal/#sec-temporal-totemporaltimezone
ThrowCompletionOr<Object*> to_temporal_time_zone(VM& vm, Value temporal_time_zone_like)
{
// 1. If Type(temporalTimeZoneLike) is Object, then
if (temporal_time_zone_like.is_object()) {
// a. If temporalTimeZoneLike has an [[InitializedTemporalTimeZone]] internal slot, then
if (is<TimeZone>(temporal_time_zone_like.as_object())) {
// i. Return temporalTimeZoneLike.
return &temporal_time_zone_like.as_object();
}
// b. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(temporal_time_zone_like.as_object())) {
auto& zoned_date_time = static_cast<ZonedDateTime&>(temporal_time_zone_like.as_object());
// i. Return temporalTimeZoneLike.[[TimeZone]].
return &zoned_date_time.time_zone();
}
// c. If temporalTimeZoneLike has an [[InitializedTemporalCalendar]] internal slot, throw a RangeError exception.
if (is<Calendar>(temporal_time_zone_like.as_object()))
return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedCalendarObject);
// d. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike.
if (!TRY(temporal_time_zone_like.as_object().has_property(vm.names.timeZone)))
return &temporal_time_zone_like.as_object();
// e. Set temporalTimeZoneLike to ? Get(temporalTimeZoneLike, "timeZone").
temporal_time_zone_like = TRY(temporal_time_zone_like.as_object().get(vm.names.timeZone));
// f. If Type(temporalTimeZoneLike) is Object, then
if (temporal_time_zone_like.is_object()) {
// i. If temporalTimeZoneLike has an [[InitializedTemporalCalendar]] internal slot, throw a RangeError exception.
if (is<Calendar>(temporal_time_zone_like.as_object()))
return vm.throw_completion<RangeError>(ErrorType::TemporalUnexpectedCalendarObject);
// ii. If ? HasProperty(temporalTimeZoneLike, "timeZone") is false, return temporalTimeZoneLike.
if (!TRY(temporal_time_zone_like.as_object().has_property(vm.names.timeZone)))
return &temporal_time_zone_like.as_object();
}
}
// 2. Let identifier be ? ToString(temporalTimeZoneLike).
auto identifier = TRY(temporal_time_zone_like.to_string(vm));
// 3. Let parseResult be ? ParseTemporalTimeZoneString(identifier).
auto parse_result = TRY(parse_temporal_time_zone_string(vm, identifier));
// 4. If parseResult.[[Name]] is not undefined, then
if (parse_result.name.has_value()) {
// a. Let name be parseResult.[[Name]].
auto name = parse_result.name.release_value();
// b. If IsTimeZoneOffsetString(name) is false, then
if (!is_time_zone_offset_string(name)) {
// i. If IsAvailableTimeZoneName(name) is false, throw a RangeError exception.
if (!is_available_time_zone_name(name))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, name);
// ii. Set name to ! CanonicalizeTimeZoneName(name).
name = MUST_OR_THROW_OOM(canonicalize_time_zone_name(vm, name));
}
// c. Return ! CreateTemporalTimeZone(name).
return MUST_OR_THROW_OOM(create_temporal_time_zone(vm, move(name)));
}
// 5. If parseResult.[[Z]] is true, return ! CreateTemporalTimeZone("UTC").
if (parse_result.z)
return MUST_OR_THROW_OOM(create_temporal_time_zone(vm, "UTC"_string));
// 6. Return ! CreateTemporalTimeZone(parseResult.[[OffsetString]]).
return MUST_OR_THROW_OOM(create_temporal_time_zone(vm, parse_result.offset_string.release_value()));
}
// 11.5.19 GetOffsetNanosecondsFor ( timeZoneRec, instant ), https://tc39.es/proposal-temporal/#sec-temporal-getoffsetnanosecondsfor
ThrowCompletionOr<double> get_offset_nanoseconds_for(VM& vm, TimeZoneMethods const& time_zone_record, Instant const& instant)
{
// 1. Let offsetNanoseconds be ? TimeZoneMethodsRecordCall(timeZoneRec, GET-OFFSET-NANOSECONDS-FOR, « instant »).
auto offset_nanoseconds_value = TRY(time_zone_methods_record_call(vm, time_zone_record, TimeZoneMethod::GetOffsetNanosecondsFor, { { &instant } }));
// 2. If TimeZoneMethodsRecordIsBuiltin(timeZoneRec), return (offsetNanoseconds).
if (time_zone_methods_record_is_builtin(time_zone_record))
return offset_nanoseconds_value.as_double();
// 3. If Type(offsetNanoseconds) is not Number, throw a TypeError exception.
if (!offset_nanoseconds_value.is_number())
return vm.throw_completion<TypeError>(ErrorType::IsNotA, "Offset nanoseconds value", "number");
// 4. If IsIntegralNumber(offsetNanoseconds) is false, throw a RangeError exception.
if (!offset_nanoseconds_value.is_integral_number())
return vm.throw_completion<RangeError>(ErrorType::IsNotAn, "Offset nanoseconds value", "integral number");
// 5. Set offsetNanoseconds to (offsetNanoseconds).
auto offset_nanoseconds = offset_nanoseconds_value.as_double();
// 6. If abs(offsetNanoseconds) ≥ nsPerDay, throw a RangeError exception.
if (fabs(offset_nanoseconds) >= ns_per_day)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidOffsetNanosecondsValue);
// 7. Return offsetNanoseconds.
return offset_nanoseconds;
}
// 11.6.9 BuiltinTimeZoneGetOffsetStringFor ( timeZone, instant ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetoffsetstringfor
ThrowCompletionOr<String> builtin_time_zone_get_offset_string_for(VM& vm, Value time_zone, Instant& instant)
{
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 1. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
// 2. Return ! FormatTimeZoneOffsetString(offsetNanoseconds).
return MUST_OR_THROW_OOM(format_time_zone_offset_string(vm, offset_nanoseconds));
}
// 11.6.10 BuiltinTimeZoneGetPlainDateTimeFor ( timeZone, instant, calendar ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetplaindatetimefor
ThrowCompletionOr<PlainDateTime*> builtin_time_zone_get_plain_date_time_for(VM& vm, Value time_zone, Instant& instant, Object& calendar)
{
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// 1. Assert: instant has an [[InitializedTemporalInstant]] internal slot.
// 2. Let offsetNanoseconds be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, instant));
// 3. Let result be ! GetISOPartsFromEpoch((instant.[[Nanoseconds]])).
auto result = get_iso_parts_from_epoch(vm, instant.nanoseconds().big_integer());
// 4. Set result to BalanceISODateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]] + offsetNanoseconds).
result = balance_iso_date_time(result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond + offset_nanoseconds);
// 5. Return ? CreateTemporalDateTime(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], calendar).
return create_temporal_date_time(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, calendar);
}
// 11.6.11 BuiltinTimeZoneGetInstantFor ( timeZone, dateTime, disambiguation ), https://tc39.es/proposal-temporal/#sec-temporal-builtintimezonegetinstantfor
ThrowCompletionOr<GC::Ref<Instant>> builtin_time_zone_get_instant_for(VM& vm, Value time_zone, PlainDateTime& date_time, StringView disambiguation)
{
// 1. Assert: dateTime has an [[InitializedTemporalDateTime]] internal slot.
// 2. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime).
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor, TimeZoneMethod::GetPossibleInstantsFor } }));
auto possible_instants = TRY(get_possible_instants_for(vm, time_zone_record, date_time));
// 3. Return ? DisambiguatePossibleInstants(possibleInstants, timeZone, dateTime, disambiguation).
return disambiguate_possible_instants(vm, possible_instants, time_zone_record, date_time, disambiguation);
}
// 11.6.12 DisambiguatePossibleInstants ( possibleInstants, timeZone, dateTime, disambiguation ), https://tc39.es/proposal-temporal/#sec-temporal-disambiguatepossibleinstants
ThrowCompletionOr<GC::Ref<Instant>> disambiguate_possible_instants(VM& vm, GC::MarkedVector<GC::Ref<Instant>> const& possible_instants, TimeZoneMethods const& time_zone_record, PlainDateTime& date_time, StringView disambiguation)
{
// 1. Assert: TimeZoneMethodsRecordHasLookedUp(timeZoneRec, GET-POSSIBLE-INSTANTS-FOR) is true.
VERIFY(time_zone_methods_record_has_looked_up(time_zone_record, TimeZoneMethod::GetPossibleInstantsFor));
// 2. Assert: If possibleInstants is empty, and disambiguation is not "reject", TimeZoneMethodsRecordHasLookedUp(timeZoneRec, GET-OFFSET-NANOSECONDS-FOR) is true.
if (possible_instants.is_empty() && disambiguation != "reject"sv)
VERIFY(time_zone_methods_record_has_looked_up(time_zone_record, TimeZoneMethod::GetOffsetNanosecondsFor));
// 3. Let n be possibleInstants's length.
auto n = possible_instants.size();
// 4. If n = 1, then
if (n == 1) {
// a. Return possibleInstants[0].
return possible_instants[0];
}
// 5. If n ≠ 0, then
if (n != 0) {
// a. If disambiguation is "earlier" or "compatible", then
if (disambiguation.is_one_of("earlier"sv, "compatible"sv)) {
// i. Return possibleInstants[0].
return possible_instants[0];
}
// b. If disambiguation is "later", then
if (disambiguation == "later"sv) {
// i. Return possibleInstants[n - 1].
return possible_instants[n - 1];
}
// c. Assert: disambiguation is "reject".
VERIFY(disambiguation == "reject"sv);
// d. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalDisambiguatePossibleInstantsRejectMoreThanOne);
}
// 6. Assert: n = 0.
VERIFY(n == 0);
// 7. If disambiguation is "reject", then
if (disambiguation == "reject"sv) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalDisambiguatePossibleInstantsRejectZero);
}
// 8. Let epochNanoseconds be GetUTCEpochNanoseconds(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
auto epoch_nanoseconds = get_utc_epoch_nanoseconds(date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond());
// 9. Let dayBeforeNs be epochNanoseconds - (nsPerDay).
auto day_before_ns = BigInt::create(vm, epoch_nanoseconds.minus(ns_per_day_bigint));
// 10. If IsValidEpochNanoseconds(dayBeforeNs) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(day_before_ns))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 11. Let dayBefore be ! CreateTemporalInstant(dayBeforeNs).
auto* day_before = MUST(create_temporal_instant(vm, day_before_ns));
// 12. Let dayAfterNs be epochNanoseconds + (nsPerDay).
auto day_after_ns = BigInt::create(vm, epoch_nanoseconds.plus(ns_per_day_bigint));
// 13. If IsValidEpochNanoseconds(dayAfterNs) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(day_after_ns))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 14. Let dayAfter be ! CreateTemporalInstant(dayAfterNs).
auto* day_after = MUST(create_temporal_instant(vm, day_after_ns));
// 15. Let offsetBefore be ? GetOffsetNanosecondsFor(timeZoneRec, dayBefore).
auto offset_before = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *day_before));
// 16. Let offsetAfter be ? GetOffsetNanosecondsFor(timeZoneRec, dayAfter).
auto offset_after = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *day_after));
// 17. Let nanoseconds be offsetAfter - offsetBefore.
auto nanoseconds = offset_after - offset_before;
// 18. If disambiguation is "earlier", then
if (disambiguation == "earlier"sv) {
// a. Let earlier be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, -nanoseconds, undefined).
auto earlier = TRY(add_date_time(vm, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), date_time.calendar(), 0, 0, 0, 0, 0, 0, 0, 0, 0, -nanoseconds, nullptr));
// b. Let earlierDateTime be ! CreateTemporalDateTime(earlier.[[Year]], earlier.[[Month]], earlier.[[Day]], earlier.[[Hour]], earlier.[[Minute]], earlier.[[Second]], earlier.[[Millisecond]], earlier.[[Microsecond]], earlier.[[Nanosecond]], dateTime.[[Calendar]]).
auto* earlier_date_time = MUST(create_temporal_date_time(vm, earlier.year, earlier.month, earlier.day, earlier.hour, earlier.minute, earlier.second, earlier.millisecond, earlier.microsecond, earlier.nanosecond, date_time.calendar()));
// c. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, earlierDateTime).
auto possible_instants_ = TRY(get_possible_instants_for(vm, time_zone_record, *earlier_date_time));
// d. If possibleInstants is empty, throw a RangeError exception.
if (possible_instants_.is_empty())
return vm.throw_completion<RangeError>(ErrorType::TemporalDisambiguatePossibleInstantsEarlierZero);
// e. Return possibleInstants[0].
return possible_instants_[0];
}
// 19. Assert: disambiguation is "compatible" or "later".
VERIFY(disambiguation.is_one_of("compatible"sv, "later"sv));
// 20. Let later be ? AddDateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], dateTime.[[Calendar]], 0, 0, 0, 0, 0, 0, 0, 0, 0, nanoseconds, undefined).
auto later = TRY(add_date_time(vm, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), date_time.calendar(), 0, 0, 0, 0, 0, 0, 0, 0, 0, nanoseconds, nullptr));
// 21. Let laterDateTime be ! CreateTemporalDateTime(later.[[Year]], later.[[Month]], later.[[Day]], later.[[Hour]], later.[[Minute]], later.[[Second]], later.[[Millisecond]], later.[[Microsecond]], later.[[Nanosecond]], dateTime.[[Calendar]]).
auto* later_date_time = MUST(create_temporal_date_time(vm, later.year, later.month, later.day, later.hour, later.minute, later.second, later.millisecond, later.microsecond, later.nanosecond, date_time.calendar()));
// 22. Set possibleInstants to ? GetPossibleInstantsFor(timeZone, laterDateTime).
auto possible_instants_ = TRY(get_possible_instants_for(vm, time_zone_record, *later_date_time));
// 23. Set n to possibleInstants's length.
n = possible_instants_.size();
// 24. If n = 0, throw a RangeError exception.
if (n == 0)
return vm.throw_completion<RangeError>(ErrorType::TemporalDisambiguatePossibleInstantsZero);
// 25. Return possibleInstants[n - 1].
return possible_instants_[n - 1];
}
// 11.5.24 GetPossibleInstantsFor ( timeZoneRec, dateTime ), https://tc39.es/proposal-temporal/#sec-temporal-getpossibleinstantsfor
ThrowCompletionOr<GC::MarkedVector<GC::Ref<Instant>>> get_possible_instants_for(VM& vm, TimeZoneMethods const& time_zone_record, PlainDateTime const& date_time)
{
// 1. Let possibleInstants be ? TimeZoneMethodsRecordCall(timeZoneRec, GET-POSSIBLE-INSTANTS-FOR, « dateTime »).
auto possible_instants = TRY(time_zone_methods_record_call(vm, time_zone_record, TimeZoneMethod::GetPossibleInstantsFor, { { &date_time } }));
// 2. If TimeZoneMethodsRecordIsBuiltin(timeZoneRec), return ! CreateListFromArrayLike(possibleInstants, « Object »).
if (time_zone_methods_record_is_builtin(time_zone_record)) {
auto list = GC::MarkedVector<GC::Ref<Instant>> { vm.heap() };
(void)MUST(create_list_from_array_like(vm, possible_instants, [&list](auto value) -> ThrowCompletionOr<void> {
list.append(verify_cast<Instant>(value.as_object()));
return {};
}));
return list;
}
// 3. Let iteratorRecord be ? GetIterator(possibleInstants, SYNC).
auto iterator = TRY(get_iterator(vm, possible_instants, IteratorHint::Sync));
// 4. Let list be a new empty List.
auto list = GC::MarkedVector<GC::Ref<Instant>> { vm.heap() };
// 5. Repeat,
while (true) {
// a. Let value be ? IteratorStepValue(iteratorRecord).
auto value = TRY(iterator_step_value(vm, iterator));
// b. If value is DONE, then
if (!value.has_value()) {
// i. Let numResults be list's length.
auto num_results = list.size();
// ii. If numResults > 1, then
if (num_results > 1) {
// 1. Let epochNs be a new empty List.
// 2. For each value instant in list, do
// a. Append instant.[[EpochNanoseconds]] to the end of the List epochNs.
// FIXME: spec bug? shouldn't [[EpochNanoseconds]] just be called [[Nanoseconds]]?
// 3. Let min be the least element of the List epochNs.
// 4. Let max be the greatest element of the List epochNs.
auto const* min = &list.first()->nanoseconds().big_integer();
auto const* max = &list.first()->nanoseconds().big_integer();
for (auto it = list.begin() + 1; it != list.end(); ++it) {
auto const& value = it->ptr()->nanoseconds().big_integer();
if (value < *min)
min = &value;
else if (value > *max)
max = &value;
}
// 5. If abs((max - min)) > nsPerDay, throw a RangeError exception.
if (max->minus(*min).unsigned_value() > ns_per_day_bigint.unsigned_value())
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidDuration);
}
// iii. Return list.
return list;
}
// c. If value is not an Object or value does not have an [[InitializedTemporalInstant]] internal slot, then
if (!value->is_object() || !is<Instant>(value->as_object())) {
// i. Let completion be ThrowCompletion(a newly created TypeError object).
auto completion = vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Temporal.Instant");
// ii. Return ? IteratorClose(iteratorRecord, completion).
return iterator_close(vm, iterator, move(completion));
}
// d. Append value to the end of the List list.
list.append(verify_cast<Instant>(value->as_object()));
}
// 7. Return list.
return { move(list) };
}
// 11.6.14 TimeZoneEquals ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal-timezoneequals
ThrowCompletionOr<bool> time_zone_equals(VM& vm, Object& one, Object& two)
{
// 1. If one and two are the same Object value, return true.
if (&one == &two)
return true;
// 2. Let timeZoneOne be ? ToString(one).
auto time_zone_one = TRY(Value(&one).to_string(vm));
// 3. Let timeZoneTwo be ? ToString(two).
auto time_zone_two = TRY(Value(&two).to_string(vm));
// 4. If timeZoneOne is timeZoneTwo, return true.
if (time_zone_one == time_zone_two)
return true;
// 5. Return false.
return false;
}
}

View file

@ -1,57 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibGC/MarkedVector.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
namespace JS::Temporal {
class TimeZone final : public Object {
JS_OBJECT(TimeZone, Object);
GC_DECLARE_ALLOCATOR(TimeZone);
public:
// Needs to store values in the range -8.64 * 10^13 to 8.64 * 10^13
using OffsetType = double;
virtual ~TimeZone() override = default;
[[nodiscard]] String const& identifier() const { return m_identifier; }
[[nodiscard]] Optional<OffsetType> const& offset_nanoseconds() const { return m_offset_nanoseconds; }
void set_identifier(String identifier) { m_identifier = move(identifier); }
void set_offset_nanoseconds(OffsetType offset_nanoseconds) { m_offset_nanoseconds = offset_nanoseconds; }
private:
explicit TimeZone(Object& prototype);
// 11.5 Properties of Temporal.TimeZone Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-timezone-instances
String m_identifier; // [[Identifier]]
Optional<OffsetType> m_offset_nanoseconds; // [[OffsetNanoseconds]]
};
bool is_available_time_zone_name(StringView time_zone);
ThrowCompletionOr<String> canonicalize_time_zone_name(VM&, StringView time_zone);
ThrowCompletionOr<TimeZone*> create_temporal_time_zone(VM&, String identifier, FunctionObject const* new_target = nullptr);
ISODateTime get_iso_parts_from_epoch(VM&, Crypto::SignedBigInteger const& epoch_nanoseconds);
BigInt* get_named_time_zone_next_transition(VM&, StringView time_zone_identifier, BigInt const& epoch_nanoseconds);
BigInt* get_named_time_zone_previous_transition(VM&, StringView time_zone_identifier, BigInt const& epoch_nanoseconds);
ThrowCompletionOr<String> format_time_zone_offset_string(VM&, double offset_nanoseconds);
ThrowCompletionOr<String> format_iso_time_zone_offset_string(VM&, double offset_nanoseconds);
ThrowCompletionOr<Object*> to_temporal_time_zone(VM&, Value temporal_time_zone_like);
ThrowCompletionOr<double> get_offset_nanoseconds_for(VM& vm, TimeZoneMethods const& time_zone_record, Instant const& instant);
ThrowCompletionOr<String> builtin_time_zone_get_offset_string_for(VM&, Value time_zone, Instant&);
ThrowCompletionOr<PlainDateTime*> builtin_time_zone_get_plain_date_time_for(VM&, Value time_zone, Instant&, Object& calendar);
ThrowCompletionOr<GC::Ref<Instant>> builtin_time_zone_get_instant_for(VM&, Value time_zone, PlainDateTime&, StringView disambiguation);
ThrowCompletionOr<GC::Ref<Instant>> disambiguate_possible_instants(VM&, GC::MarkedVector<GC::Ref<Instant>> const& possible_instants, TimeZoneMethods const&, PlainDateTime&, StringView disambiguation);
ThrowCompletionOr<GC::MarkedVector<GC::Ref<Instant>>> get_possible_instants_for(VM&, TimeZoneMethods const&, PlainDateTime const&);
ThrowCompletionOr<bool> time_zone_equals(VM&, Object& one, Object& two);
}

View file

@ -1,80 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZoneConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(TimeZoneConstructor);
// 11.2 The Temporal.TimeZone Constructor, https://tc39.es/proposal-temporal/#sec-temporal-timezone-constructor
TimeZoneConstructor::TimeZoneConstructor(Realm& realm)
: NativeFunction(realm.vm().names.TimeZone.as_string(), realm.intrinsics().function_prototype())
{
}
void TimeZoneConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 11.3.1 Temporal.TimeZone.prototype, https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_time_zone_prototype(), 0);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function(realm, vm.names.from, from, 1, attr);
define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
}
// 11.2.1 Temporal.TimeZone ( identifier ), https://tc39.es/proposal-temporal/#sec-temporal.timezone
ThrowCompletionOr<Value> TimeZoneConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.TimeZone");
}
// 11.2.1 Temporal.TimeZone ( identifier ), https://tc39.es/proposal-temporal/#sec-temporal.timezone
ThrowCompletionOr<GC::Ref<Object>> TimeZoneConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Set identifier to ? ToString(identifier).
auto identifier = TRY(vm.argument(0).to_string(vm));
// 3. If IsTimeZoneOffsetString(identifier) is false, then
if (!is_time_zone_offset_string(identifier)) {
// a. If IsAvailableTimeZoneName(identifier) is false, then
if (!is_available_time_zone_name(identifier)) {
// i. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, identifier);
}
// b. Set identifier to ! CanonicalizeTimeZoneName(identifier).
identifier = MUST_OR_THROW_OOM(canonicalize_time_zone_name(vm, identifier));
}
// 4. Return ? CreateTemporalTimeZone(identifier, NewTarget).
return *TRY(create_temporal_time_zone(vm, move(identifier), &new_target));
}
// 11.3.2 Temporal.TimeZone.from ( item ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.from
JS_DEFINE_NATIVE_FUNCTION(TimeZoneConstructor::from)
{
auto item = vm.argument(0);
// 1. Return ? ToTemporalTimeZone(item).
return TRY(to_temporal_time_zone(vm, item));
}
}

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class TimeZoneConstructor final : public NativeFunction {
JS_OBJECT(TimeZoneConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(TimeZoneConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~TimeZoneConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit TimeZoneConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
};
}

View file

@ -1,134 +0,0 @@
/*
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZoneMethods.h>
namespace JS::Temporal {
// 11.5.2 CreateTimeZoneMethodsRecord ( timeZone, methods ), https://tc39.es/proposal-temporal/#sec-temporal-createtimezonemethodsrecord
ThrowCompletionOr<TimeZoneMethods> create_time_zone_methods_record(VM& vm, Variant<String, GC::Ref<Object>> time_zone, ReadonlySpan<TimeZoneMethod> methods)
{
// 1. Let record be the Time Zone Methods Record { [[Receiver]]: timeZone, [[GetOffsetNanosecondsFor]]: undefined, [[GetPossibleInstantsFor]]: undefined }.
TimeZoneMethods record {
.receiver = move(time_zone),
.get_offset_nanoseconds_for = nullptr,
.get_possible_instants_for = nullptr,
};
// 2. For each element methodName in methods, do
for (TimeZoneMethod method_name : methods) {
// a. Perform ? TimeZoneMethodsRecordLookup(record, methodName).
TRY(time_zone_methods_record_lookup(vm, record, method_name));
}
// 3. Return record.
return record;
}
// 11.5.3 TimeZoneMethodsRecordLookup ( timeZoneRec, methodName ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordlookup
ThrowCompletionOr<void> time_zone_methods_record_lookup(VM& vm, TimeZoneMethods& time_zone_record, TimeZoneMethod method_name)
{
auto& realm = *vm.current_realm();
// 1. Assert: TimeZoneMethodsRecordHasLookedUp(timeZoneRec, methodName) is false.
// 2. If methodName is GET-OFFSET-NANOSECONDS-FOR, then
// a. If timeZoneRec.[[Receiver]] is a String, then
// i. Set timeZoneRec.[[GetOffsetNanosecondsFor]] to %Temporal.TimeZone.prototype.getOffsetNanosecondsFor%.
// b. Else,
// i. Set timeZoneRec.[[GetOffsetNanosecondsFor]] to ? GetMethod(timeZoneRec.[[Receiver]], "getOffsetNanosecondsFor").
// ii. If timeZoneRec.[[GetOffsetNanosecondsFor]] is undefined, throw a TypeError exception.
// 3. Else if methodName is GET-POSSIBLE-INSTANTS-FOR, then
// a. If timeZoneRec.[[Receiver]] is a String, then
// i. Set timeZoneRec.[[GetPossibleInstantsFor]] to %Temporal.TimeZone.prototype.getPossibleInstantsFor%.
// b. Else,
// i. Set timeZoneRec.[[GetPossibleInstantsFor]] to ? GetMethod(timeZoneRec.[[Receiver]], "getPossibleInstantsFor").
// ii. If timeZoneRec.[[GetPossibleInstantsFor]] is undefined, throw a TypeError exception.
switch (method_name) {
#define __JS_ENUMERATE(PascalName, camelName, snake_name) \
case TimeZoneMethod::PascalName: { \
VERIFY(!time_zone_record.snake_name); \
if (time_zone_record.receiver.has<String>()) { \
const auto& time_zone_prototype = *realm.intrinsics().temporal_time_zone_prototype(); \
time_zone_record.snake_name = time_zone_prototype.get_without_side_effects(vm.names.camelName).as_function(); \
} else { \
Value time_zone { time_zone_record.receiver.get<GC::Ref<Object>>() }; \
time_zone_record.snake_name = TRY(time_zone.get_method(vm, vm.names.camelName)); \
if (!time_zone_record.snake_name) \
return vm.throw_completion<TypeError>(ErrorType::IsUndefined, #camelName##sv); \
} \
break; \
}
JS_ENUMERATE_TIME_ZONE_METHODS
#undef __JS_ENUMERATE
}
// 4. Return UNUSED.
return {};
}
// 11.5.4 TimeZoneMethodsRecordHasLookedUp ( timeZoneRec, methodName ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordhaslookedup
bool time_zone_methods_record_has_looked_up(TimeZoneMethods const& time_zone_record, TimeZoneMethod method_name)
{
// 1. If methodName is GET-OFFSET-NANOSECONDS-FOR, then
// a. Let method be timeZoneRec.[[GetOffsetNanosecondsFor]].
// 2. Else if methodName is GET-POSSIBLE-INSTANTS-FOR, then
// a. Let method be timeZoneRec.[[GetPossibleInstantsFor]].
// 3. If method is undefined, return false.
// 4. Return true.
switch (method_name) {
#define __JS_ENUMERATE(PascalName, camelName, snake_name) \
case TimeZoneMethod::PascalName: { \
return time_zone_record.snake_name != nullptr; \
}
JS_ENUMERATE_TIME_ZONE_METHODS
#undef __JS_ENUMERATE
}
VERIFY_NOT_REACHED();
}
// 11.5.5 TimeZoneMethodsRecordIsBuiltin ( timeZoneRec ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordisbuiltin
bool time_zone_methods_record_is_builtin(TimeZoneMethods const& time_zone_record)
{
// 1. If timeZoneRec.[[Receiver]] is a String, return true.
if (time_zone_record.receiver.has<String>())
return true;
// 2. Return false.
return false;
}
// 11.5.6 TimeZoneMethodsRecordCall ( timeZoneRec, methodName, arguments ), https://tc39.es/proposal-temporal/#sec-temporal-timezonemethodsrecordcall
ThrowCompletionOr<Value> time_zone_methods_record_call(VM& vm, TimeZoneMethods const& time_zone_record, TimeZoneMethod method_name, ReadonlySpan<Value> arguments)
{
// 1. Assert: TimeZoneMethodsRecordHasLookedUp(timeZoneRec, methodName) is true.
VERIFY(time_zone_methods_record_has_looked_up(time_zone_record, method_name));
// 2. Let receiver be timeZoneRec.[[Receiver]].
// 3. If TimeZoneMethodsRecordIsBuiltin(timeZoneRec) is true, then
// a. Set receiver to ! CreateTemporalTimeZone(timeZoneRec.[[Receiver]]).
GC::Ptr<Object> receiver;
if (time_zone_methods_record_is_builtin(time_zone_record))
receiver = MUST(create_temporal_time_zone(vm, time_zone_record.receiver.get<String>()));
else
receiver = time_zone_record.receiver.get<GC::Ref<Object>>();
// 4. If methodName is GET-OFFSET-NANOSECONDS-FOR, then
// a. Return ? Call(timeZoneRec.[[GetOffsetNanosecondsFor]], receiver, arguments).
// 5. If methodName is GET-POSSIBLE-INSTANTS-FOR, then
// a. Return ? Call(timeZoneRec.[[GetPossibleInstantsFor]], receiver, arguments).
switch (method_name) {
#define __JS_ENUMERATE(PascalName, camelName, snake_name) \
case TimeZoneMethod::PascalName: { \
return TRY(call(vm, time_zone_record.snake_name, receiver, arguments)); \
}
JS_ENUMERATE_TIME_ZONE_METHODS
#undef __JS_ENUMERATE
}
VERIFY_NOT_REACHED();
}
}

View file

@ -1,44 +0,0 @@
/*
* Copyright (c) 2024, Shannon Booth <shannon@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/String.h>
#include <LibGC/Ptr.h>
#include <LibJS/Forward.h>
namespace JS::Temporal {
// 11.5.1 Time Zone Methods Records, https://tc39.es/proposal-temporal/#sec-temporal-time-zone-methods-records
struct TimeZoneMethods {
// The time zone object, or a string indicating a built-in time zone.
Variant<String, GC::Ref<Object>> receiver; // [[Reciever]]
// The time zone's getOffsetNanosecondsFor method. For a built-in time zone this is always %Temporal.TimeZone.prototype.getOffsetNanosecondsFor%.
GC::Ptr<FunctionObject> get_offset_nanoseconds_for; // [[GetOffsetNanosecondsFor]]
// The time zone's getPossibleInstantsFor method. For a built-in time zone this is always %Temporal.TimeZone.prototype.getPossibleInstantsFor%.
GC::Ptr<FunctionObject> get_possible_instants_for; // [[GetPossibleInstantsFor]]
};
#define JS_ENUMERATE_TIME_ZONE_METHODS \
__JS_ENUMERATE(GetOffsetNanosecondsFor, getOffsetNanosecondsFor, get_offset_nanoseconds_for) \
__JS_ENUMERATE(GetPossibleInstantsFor, getPossibleInstantsFor, get_possible_instants_for)
enum class TimeZoneMethod {
#define __JS_ENUMERATE(PascalName, camelName, snake_name) \
PascalName,
JS_ENUMERATE_TIME_ZONE_METHODS
#undef __JS_ENUMERATE
};
ThrowCompletionOr<void> time_zone_methods_record_lookup(VM&, TimeZoneMethods&, TimeZoneMethod);
ThrowCompletionOr<TimeZoneMethods> create_time_zone_methods_record(VM&, Variant<String, GC::Ref<Object>> time_zone, ReadonlySpan<TimeZoneMethod>);
bool time_zone_methods_record_has_looked_up(TimeZoneMethods const&, TimeZoneMethod);
bool time_zone_methods_record_is_builtin(TimeZoneMethods const&);
ThrowCompletionOr<Value> time_zone_methods_record_call(VM&, TimeZoneMethods const&, TimeZoneMethod, ReadonlySpan<Value> arguments);
}

View file

@ -1,253 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/Array.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/TimeZonePrototype.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(TimeZonePrototype);
// 11.4 Properties of the Temporal.TimeZone Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-timezone-prototype-object
TimeZonePrototype::TimeZonePrototype(Realm& realm)
: PrototypeObject(realm.intrinsics().object_prototype())
{
}
void TimeZonePrototype::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_accessor(realm, vm.names.id, id_getter, {}, Attribute::Configurable);
define_native_function(realm, vm.names.getOffsetNanosecondsFor, get_offset_nanoseconds_for, 1, attr);
define_native_function(realm, vm.names.getOffsetStringFor, get_offset_string_for, 1, attr);
define_native_function(realm, vm.names.getPlainDateTimeFor, get_plain_date_time_for, 1, attr);
define_native_function(realm, vm.names.getInstantFor, get_instant_for, 1, attr);
define_native_function(realm, vm.names.getPossibleInstantsFor, get_possible_instants_for, 1, attr);
define_native_function(realm, vm.names.getNextTransition, get_next_transition, 1, attr);
define_native_function(realm, vm.names.getPreviousTransition, get_previous_transition, 1, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toJSON, to_json, 0, attr);
// 11.4.2 Temporal.TimeZone.prototype[ @@toStringTag ], https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype-@@tostringtag
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.TimeZone"_string), Attribute::Configurable);
}
// 11.4.3 get Temporal.TimeZone.prototype.id, https://tc39.es/proposal-temporal/#sec-get-temporal.timezone.prototype.id
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::id_getter)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Return timeZone.[[Identifier]].
return PrimitiveString::create(vm, time_zone->identifier());
}
// 11.4.4 Temporal.TimeZone.prototype.getOffsetNanosecondsFor ( instant ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getoffsetnanosecondsfor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_offset_nanoseconds_for)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set instant to ? ToTemporalInstant(instant).
auto* instant = TRY(to_temporal_instant(vm, vm.argument(0)));
// 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return 𝔽(timeZone.[[OffsetNanoseconds]]).
if (time_zone->offset_nanoseconds().has_value())
return Value(*time_zone->offset_nanoseconds());
// 5. Return 𝔽(GetNamedTimeZoneOffsetNanoseconds(timeZone.[[Identifier]], instant.[[Nanoseconds]])).
auto offset = get_named_time_zone_offset_nanoseconds(time_zone->identifier(), instant->nanoseconds().big_integer());
return Value(static_cast<double>(offset.offset.to_nanoseconds()));
}
// 11.4.5 Temporal.TimeZone.prototype.getOffsetStringFor ( instant ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getoffsetstringfor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_offset_string_for)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set instant to ? ToTemporalInstant(instant).
auto* instant = TRY(to_temporal_instant(vm, vm.argument(0)));
// 4. Return ? BuiltinTimeZoneGetOffsetStringFor(timeZone, instant).
auto offset_string = TRY(builtin_time_zone_get_offset_string_for(vm, time_zone, *instant));
return PrimitiveString::create(vm, move(offset_string));
}
// 11.4.6 Temporal.TimeZone.prototype.getPlainDateTimeFor ( instant [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getplaindatetimefor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_plain_date_time_for)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set instant to ? ToTemporalInstant(instant).
auto* instant = TRY(to_temporal_instant(vm, vm.argument(0)));
// 4. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, vm.argument(1)));
// 5. Return ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
return TRY(builtin_time_zone_get_plain_date_time_for(vm, time_zone, *instant, *calendar));
}
// 11.4.7 Temporal.TimeZone.prototype.getInstantFor ( dateTime [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getinstantfor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_instant_for)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set dateTime to ? ToTemporalDateTime(dateTime).
auto* date_time = TRY(to_temporal_date_time(vm, vm.argument(0)));
// 4. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, vm.argument(1)));
// 5. Let disambiguation be ? ToTemporalDisambiguation(options).
auto disambiguation = TRY(to_temporal_disambiguation(vm, options));
// 6. Return ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
return TRY(builtin_time_zone_get_instant_for(vm, time_zone, *date_time, disambiguation));
}
// 11.4.8 Temporal.TimeZone.prototype.getPossibleInstantsFor ( dateTime ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getpossibleinstantsfor
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_possible_instants_for)
{
auto& realm = *vm.current_realm();
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimezone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set dateTime to ? ToTemporalDateTime(dateTime).
auto* date_time = TRY(to_temporal_date_time(vm, vm.argument(0)));
Vector<Crypto::SignedBigInteger> possible_epoch_nanoseconds;
// 4. If timeZone.[[OffsetNanoseconds]] is not undefined, then
if (time_zone->offset_nanoseconds().has_value()) {
// a. Let epochNanoseconds be GetUTCEpochNanoseconds(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
auto epoch_nanoseconds = get_utc_epoch_nanoseconds(date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond());
// b. Let possibleEpochNanoseconds be « epochNanoseconds - (timeZone.[[OffsetNanoseconds]]) ».
possible_epoch_nanoseconds.append(epoch_nanoseconds.minus(Crypto::SignedBigInteger { *time_zone->offset_nanoseconds() }));
}
// 5. Else,
else {
// a. Let possibleEpochNanoseconds be GetNamedTimeZoneEpochNanoseconds(timeZone.[[Identifier]], dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]]).
possible_epoch_nanoseconds = get_named_time_zone_epoch_nanoseconds(time_zone->identifier(), date_time->iso_year(), date_time->iso_month(), date_time->iso_day(), date_time->iso_hour(), date_time->iso_minute(), date_time->iso_second(), date_time->iso_millisecond(), date_time->iso_microsecond(), date_time->iso_nanosecond());
}
// 6. Let possibleInstants be a new empty List.
auto possible_instants = GC::MarkedVector<Value> { vm.heap() };
// 7. For each value epochNanoseconds in possibleEpochNanoseconds, do
for (auto& epoch_nanoseconds : possible_epoch_nanoseconds) {
// a. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// b. Let instant be ! CreateTemporalInstant(epochNanoseconds).
auto epoch_nanoseconds_bigint = BigInt::create(vm, move(epoch_nanoseconds));
auto* instant = MUST(create_temporal_instant(vm, epoch_nanoseconds_bigint));
// c. Append instant to possibleInstants.
possible_instants.append(instant);
}
// 8. Return CreateArrayFromList(possibleInstants).
return Array::create_from(realm, possible_instants);
}
// 11.4.9 Temporal.TimeZone.prototype.getNextTransition ( startingPoint ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getnexttransition
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_next_transition)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set startingPoint to ? ToTemporalInstant(startingPoint).
auto* starting_point = TRY(to_temporal_instant(vm, vm.argument(0)));
// 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return null.
if (!time_zone->offset_nanoseconds().has_value())
return js_null();
// 5. Let transition be GetNamedTimeZoneNextTransition(timeZone.[[Identifier]], startingPoint.[[Nanoseconds]]).
auto* transition = get_named_time_zone_next_transition(vm, time_zone->identifier(), starting_point->nanoseconds());
// 6. If transition is null, return null.
if (!transition)
return js_null();
// 7. Return ! CreateTemporalInstant(transition).
return MUST(create_temporal_instant(vm, *transition));
}
// 11.4.10 Temporal.TimeZone.prototype.getPreviousTransition ( startingPoint ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.getprevioustransition
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::get_previous_transition)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Set startingPoint to ? ToTemporalInstant(startingPoint).
auto* starting_point = TRY(to_temporal_instant(vm, vm.argument(0)));
// 4. If timeZone.[[OffsetNanoseconds]] is not undefined, return null.
if (!time_zone->offset_nanoseconds().has_value())
return js_null();
// 5. Let transition be GetNamedTimeZonePreviousTransition(timeZone.[[Identifier]], startingPoint.[[Nanoseconds]]).
auto* transition = get_named_time_zone_previous_transition(vm, time_zone->identifier(), starting_point->nanoseconds());
// 6. If transition is null, return null.
if (!transition)
return js_null();
// 7. Return ! CreateTemporalInstant(transition).
return MUST(create_temporal_instant(vm, *transition));
}
// 11.4.11 Temporal.TimeZone.prototype.toString ( ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.tostring
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::to_string)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Return timeZone.[[Identifier]].
return PrimitiveString::create(vm, time_zone->identifier());
}
// 11.4.12 Temporal.TimeZone.prototype.toJSON ( ), https://tc39.es/proposal-temporal/#sec-temporal.timezone.prototype.tojson
JS_DEFINE_NATIVE_FUNCTION(TimeZonePrototype::to_json)
{
// 1. Let timeZone be the this value.
// 2. Perform ? RequireInternalSlot(timeZone, [[InitializedTemporalTimeZone]]).
auto time_zone = TRY(typed_this_object(vm));
// 3. Return ? ToString(timeZone).
return PrimitiveString::create(vm, TRY(Value(time_zone).to_string(vm)));
}
}

View file

@ -1,37 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
namespace JS::Temporal {
class TimeZonePrototype final : public PrototypeObject<TimeZonePrototype, TimeZone> {
JS_PROTOTYPE_OBJECT(TimeZonePrototype, TimeZone, Temporal.TimeZone);
GC_DECLARE_ALLOCATOR(TimeZonePrototype);
public:
virtual void initialize(Realm&) override;
virtual ~TimeZonePrototype() override = default;
private:
explicit TimeZonePrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(id_getter);
JS_DECLARE_NATIVE_FUNCTION(get_offset_nanoseconds_for);
JS_DECLARE_NATIVE_FUNCTION(get_offset_string_for);
JS_DECLARE_NATIVE_FUNCTION(get_plain_date_time_for);
JS_DECLARE_NATIVE_FUNCTION(get_instant_for);
JS_DECLARE_NATIVE_FUNCTION(get_possible_instants_for);
JS_DECLARE_NATIVE_FUNCTION(get_next_transition);
JS_DECLARE_NATIVE_FUNCTION(get_previous_transition);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
};
}

View file

@ -1,652 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021-2023, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/AbstractOperations.h>
#include <LibJS/Runtime/Date.h>
#include <LibJS/Runtime/GlobalObject.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/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(ZonedDateTime);
// 6 Temporal.ZonedDateTime Objects, https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-objects
ZonedDateTime::ZonedDateTime(BigInt const& nanoseconds, Object& time_zone, Object& calendar, Object& prototype)
: Object(ConstructWithPrototypeTag::Tag, prototype)
, m_nanoseconds(nanoseconds)
, m_time_zone(time_zone)
, m_calendar(calendar)
{
}
void ZonedDateTime::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_nanoseconds);
visitor.visit(m_time_zone);
visitor.visit(m_calendar);
}
// 6.5.1 InterpretISODateTimeOffset ( year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour ), https://tc39.es/proposal-temporal/#sec-temporal-interpretisodatetimeoffset
ThrowCompletionOr<BigInt const*> interpret_iso_date_time_offset(VM& vm, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, OffsetBehavior offset_behavior, double offset_nanoseconds, Value time_zone, StringView disambiguation, StringView offset_option, MatchBehavior match_behavior)
{
// 1. Let calendar be ! GetISO8601Calendar().
auto* calendar = get_iso8601_calendar(vm);
// 2. Let dateTime be ? CreateTemporalDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, calendar).
auto* date_time = TRY(create_temporal_date_time(vm, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, *calendar));
// 3. If offsetBehaviour is wall or offsetOption is "ignore", then
if (offset_behavior == OffsetBehavior::Wall || offset_option == "ignore"sv) {
// a. Let instant be ? BuiltinTimeZoneGetInstantFor(timeZone, dateTime, disambiguation).
auto instant = TRY(builtin_time_zone_get_instant_for(vm, time_zone, *date_time, disambiguation));
// b. Return instant.[[Nanoseconds]].
return &instant->nanoseconds();
}
// 4. If offsetBehaviour is exact or offsetOption is "use", then
if (offset_behavior == OffsetBehavior::Exact || offset_option == "use"sv) {
// a. Let epochNanoseconds be GetUTCEpochNanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond).
auto epoch_nanoseconds = get_utc_epoch_nanoseconds(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
// b. Set epochNanoseconds to epochNanoseconds - (offsetNanoseconds).
epoch_nanoseconds = epoch_nanoseconds.minus(Crypto::SignedBigInteger { offset_nanoseconds });
// c. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// d. Return epochNanoseconds.
return BigInt::create(vm, move(epoch_nanoseconds)).ptr();
}
// 5. Assert: offsetBehaviour is option.
VERIFY(offset_behavior == OffsetBehavior::Option);
// 6. Assert: offsetOption is "prefer" or "reject".
VERIFY(offset_option.is_one_of("prefer"sv, "reject"sv));
// 7. Let possibleInstants be ? GetPossibleInstantsFor(timeZone, dateTime).
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone.as_object() }, { { TimeZoneMethod::GetPossibleInstantsFor, TimeZoneMethod::GetOffsetNanosecondsFor } }));
auto possible_instants = TRY(get_possible_instants_for(vm, time_zone_record, *date_time));
// 8. For each element candidate of possibleInstants, do
for (auto candidate : possible_instants) {
// a. Let candidateNanoseconds be ? GetOffsetNanosecondsFor(timeZone, candidate).
auto candidate_nanoseconds = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *candidate));
// b. If candidateNanoseconds = offsetNanoseconds, then
if (candidate_nanoseconds == offset_nanoseconds) {
// i. Return candidate.[[Nanoseconds]].
return &candidate->nanoseconds();
}
// c. If matchBehaviour is match minutes, then
if (match_behavior == MatchBehavior::MatchMinutes) {
// i. Let roundedCandidateNanoseconds be RoundNumberToIncrement(candidateNanoseconds, 60 × 10^9, "halfExpand").
auto rounded_candidate_nanoseconds = round_number_to_increment(candidate_nanoseconds, 60000000000, "halfExpand"sv);
// ii. If roundedCandidateNanoseconds = offsetNanoseconds, then
if (rounded_candidate_nanoseconds == offset_nanoseconds) {
// 1. Return candidate.[[Nanoseconds]].
return &candidate->nanoseconds();
}
}
}
// 9. If offsetOption is "reject", throw a RangeError exception.
if (offset_option == "reject"sv)
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidZonedDateTimeOffset);
// 10. Let instant be ? DisambiguatePossibleInstants(possibleInstants, timeZone, dateTime, disambiguation).
auto instant = TRY(disambiguate_possible_instants(vm, possible_instants, time_zone_record, *date_time, disambiguation));
// 11. Return instant.[[Nanoseconds]].
return &instant->nanoseconds();
}
// 6.5.2 ToTemporalZonedDateTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalzoneddatetime
ThrowCompletionOr<ZonedDateTime*> to_temporal_zoned_date_time(VM& vm, Value item, Object const* options)
{
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
// 3. Let offsetBehaviour be option.
auto offset_behavior = OffsetBehavior::Option;
// 4. Let matchBehaviour be match exactly.
auto match_behavior = MatchBehavior::MatchExactly;
Object* calendar = nullptr;
Object* time_zone = nullptr;
Optional<String> offset_string;
ISODateTime result;
// 5. If Type(item) is Object, then
if (item.is_object()) {
auto& item_object = item.as_object();
// a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(item_object)) {
// i. Return item.
return &static_cast<ZonedDateTime&>(item_object);
}
// b. Let calendar be ? GetTemporalCalendarWithISODefault(item).
calendar = TRY(get_temporal_calendar_with_iso_default(vm, item_object));
// c. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »).
auto field_names = TRY(calendar_fields(vm, *calendar, { "day"sv, "hour"sv, "microsecond"sv, "millisecond"sv, "minute"sv, "month"sv, "monthCode"sv, "nanosecond"sv, "second"sv, "year"sv }));
// d. Append "timeZone" to fieldNames.
field_names.append("timeZone"_string);
// e. Append "offset" to fieldNames.
field_names.append("offset"_string);
// f. Let fields be ? PrepareTemporalFields(item, fieldNames, « "timeZone" »).
auto* fields = TRY(prepare_temporal_fields(vm, item_object, field_names, Vector<StringView> { "timeZone"sv }));
// g. Let timeZone be ! Get(fields, "timeZone").
auto time_zone_value = MUST(fields->get(vm.names.timeZone));
// h. Set timeZone to ? ToTemporalTimeZone(timeZone).
time_zone = TRY(to_temporal_time_zone(vm, time_zone_value));
// i. Let offsetString be ! Get(fields, "offset").
auto offset_string_value = MUST(fields->get(vm.names.offset));
// j. Assert: offsetString is a String or undefined.
VERIFY(offset_string_value.is_string() || offset_string_value.is_undefined());
// k. If offsetString is undefined, then
if (offset_string_value.is_undefined()) {
// i. Set offsetBehaviour to wall.
offset_behavior = OffsetBehavior::Wall;
} else {
// NOTE: Not in the spec, since it directly assigns to offsetString in step i, but we can't do it there as it's a type mismatch.
offset_string = offset_string_value.as_string().utf8_string();
}
// l. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, options).
result = TRY(interpret_temporal_date_time_fields(vm, *calendar, *fields, options));
}
// 6. Else,
else {
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// b. Let string be ? ToString(item).
auto string = TRY(item.to_string(vm));
// c. Let result be ? ParseTemporalZonedDateTimeString(string).
result = TRY(parse_temporal_zoned_date_time_string(vm, string));
// d. Let timeZoneName be result.[[TimeZone]].[[Name]].
auto time_zone_name = result.time_zone.name;
// e. Assert: timeZoneName is not undefined.
VERIFY(time_zone_name.has_value());
// f. If IsTimeZoneOffsetString(timeZoneName) is false, then
if (!is_time_zone_offset_string(*time_zone_name)) {
// i. If IsAvailableTimeZoneName(timeZoneName) is false, throw a RangeError exception.
if (!is_available_time_zone_name(*time_zone_name))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, *time_zone_name);
// ii. Set timeZoneName to ! CanonicalizeTimeZoneName(timeZoneName).
time_zone_name = MUST_OR_THROW_OOM(canonicalize_time_zone_name(vm, *time_zone_name));
}
// g. Let offsetString be result.[[TimeZone]].[[OffsetString]].
offset_string = move(result.time_zone.offset_string);
// h. If result.[[TimeZone]].[[Z]] is true, then
if (result.time_zone.z) {
// i. Set offsetBehaviour to exact.
offset_behavior = OffsetBehavior::Exact;
}
// i. Else if offsetString is undefined, then
else if (!offset_string.has_value()) {
// i. Set offsetBehaviour to wall.
offset_behavior = OffsetBehavior::Wall;
}
// j. Let timeZone be ! CreateTemporalTimeZone(timeZoneName).
time_zone = MUST_OR_THROW_OOM(create_temporal_time_zone(vm, time_zone_name.release_value()));
// k. Let calendar be ? ToTemporalCalendarWithISODefault(result.[[Calendar]]).
auto temporal_calendar_like = result.calendar.has_value()
? PrimitiveString::create(vm, result.calendar.value())
: js_undefined();
calendar = TRY(to_temporal_calendar_with_iso_default(vm, temporal_calendar_like));
// l. Set matchBehaviour to match minutes.
match_behavior = MatchBehavior::MatchMinutes;
}
// 7. Let offsetNanoseconds be 0.
double offset_nanoseconds = 0;
// 8. If offsetBehaviour is option, then
if (offset_behavior == OffsetBehavior::Option) {
// a. If IsTimeZoneOffsetString(offsetString) is false, throw a RangeError exception.
if (!is_time_zone_offset_string(*offset_string))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidTimeZoneName, *offset_string);
// a. Set offsetNanoseconds to ? ParseTimeZoneOffsetString(offsetString).
offset_nanoseconds = parse_time_zone_offset_string(*offset_string);
}
// 9. Let disambiguation be ? ToTemporalDisambiguation(options).
auto disambiguation = TRY(to_temporal_disambiguation(vm, options));
// 10. Let offsetOption be ? ToTemporalOffset(options, "reject").
auto offset_option = TRY(to_temporal_offset(vm, options, "reject"sv));
// 11. Let epochNanoseconds be ? InterpretISODateTimeOffset(result.[[Year]], result.[[Month]], result.[[Day]], result.[[Hour]], result.[[Minute]], result.[[Second]], result.[[Millisecond]], result.[[Microsecond]], result.[[Nanosecond]], offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour).
auto* epoch_nanoseconds = TRY(interpret_iso_date_time_offset(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, offset_behavior, offset_nanoseconds, time_zone, disambiguation, offset_option, match_behavior));
// 12. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
return MUST(create_temporal_zoned_date_time(vm, *epoch_nanoseconds, *time_zone, *calendar));
}
// 6.5.3 CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalzoneddatetime
ThrowCompletionOr<ZonedDateTime*> create_temporal_zoned_date_time(VM& vm, BigInt const& epoch_nanoseconds, Object& time_zone, Object& calendar, FunctionObject const* new_target)
{
auto& realm = *vm.current_realm();
// 1. Assert: ! IsValidEpochNanoseconds(epochNanoseconds) is true.
VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds));
// 2. If newTarget is not present, set newTarget to %Temporal.ZonedDateTime%.
if (!new_target)
new_target = realm.intrinsics().temporal_zoned_date_time_constructor();
// 3. Let object be ? OrdinaryCreateFromConstructor(newTarget, "%Temporal.ZonedDateTime.prototype%", « [[InitializedTemporalZonedDateTime]], [[Nanoseconds]], [[TimeZone]], [[Calendar]] »).
// 4. Set object.[[Nanoseconds]] to epochNanoseconds.
// 5. Set object.[[TimeZone]] to timeZone.
// 6. Set object.[[Calendar]] to calendar.
auto object = TRY(ordinary_create_from_constructor<ZonedDateTime>(vm, *new_target, &Intrinsics::temporal_time_zone_prototype, epoch_nanoseconds, time_zone, calendar));
// 7. Return object.
return object.ptr();
}
// 6.5.4 TemporalZonedDateTimeToString ( zonedDateTime, precision, showCalendar, showTimeZone, showOffset [ , increment, unit, roundingMode ] ), https://tc39.es/proposal-temporal/#sec-temporal-temporalzoneddatetimetostring
ThrowCompletionOr<String> temporal_zoned_date_time_to_string(VM& vm, ZonedDateTime& zoned_date_time, Variant<StringView, u8> const& precision, StringView show_calendar, StringView show_time_zone, StringView show_offset, Optional<u64> increment, Optional<StringView> unit, Optional<StringView> rounding_mode)
{
// 1. If increment is not present, set increment to 1.
if (!increment.has_value())
increment = 1;
// 2. If unit is not present, set unit to "nanosecond".
if (!unit.has_value())
unit = "nanosecond"sv;
// 3. If roundingMode is not present, set roundingMode to "trunc".
if (!rounding_mode.has_value())
rounding_mode = "trunc"sv;
// 4. Let ns be ! RoundTemporalInstant(zonedDateTime.[[Nanoseconds]], increment, unit, roundingMode).
auto* ns = round_temporal_instant(vm, zoned_date_time.nanoseconds(), *increment, *unit, *rounding_mode);
// 5. Let timeZone be zonedDateTime.[[TimeZone]].
auto& time_zone = zoned_date_time.time_zone();
// 6. Let instant be ! CreateTemporalInstant(ns).
auto* instant = MUST(create_temporal_instant(vm, *ns));
// 7. Let isoCalendar be ! GetISO8601Calendar().
auto* iso_calendar = get_iso8601_calendar(vm);
// 8. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, isoCalendar).
auto* temporal_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &time_zone, *instant, *iso_calendar));
// 9. Let dateTimeString be ! TemporalDateTimeToString(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], isoCalendar, precision, "never").
auto date_time_string = MUST_OR_THROW_OOM(temporal_date_time_to_string(vm, temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day(), temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond(), iso_calendar, precision, "never"sv));
String offset_string;
// 10. If showOffset is "never", then
if (show_offset == "never"sv) {
// a. Let offsetString be the empty String.
offset_string = {};
}
// 11. Else,
else {
auto time_zone_record = TRY(create_time_zone_methods_record(vm, GC::Ref<Object> { time_zone }, { { TimeZoneMethod::GetOffsetNanosecondsFor } }));
// a. Let offsetNs be ? GetOffsetNanosecondsFor(timeZone, instant).
auto offset_ns = TRY(get_offset_nanoseconds_for(vm, time_zone_record, *instant));
// b. Let offsetString be ! FormatISOTimeZoneOffsetString(offsetNs).
offset_string = MUST_OR_THROW_OOM(format_iso_time_zone_offset_string(vm, offset_ns));
}
String time_zone_string;
// 12. If showTimeZone is "never", then
if (show_time_zone == "never"sv) {
// a. Let timeZoneString be the empty String.
time_zone_string = {};
}
// 13. Else,
else {
// a. Let timeZoneID be ? ToString(timeZone).
auto time_zone_id = TRY(Value(&time_zone).to_string(vm));
// b. If showTimeZone is "critical", let flag be "!"; else let flag be the empty String.
auto flag = show_time_zone == "critical"sv ? "!"sv : ""sv;
// c. Let timeZoneString be the string-concatenation of the code unit 0x005B (LEFT SQUARE BRACKET), flag, timeZoneID, and the code unit 0x005D (RIGHT SQUARE BRACKET).
time_zone_string = TRY_OR_THROW_OOM(vm, String::formatted("[{}{}]", flag, time_zone_id));
}
// 14. Let calendarString be ? MaybeFormatCalendarAnnotation(zonedDateTime.[[Calendar]], showCalendar).
auto calendar_string = TRY(maybe_format_calendar_annotation(vm, &zoned_date_time.calendar(), show_calendar));
// 15. Return the string-concatenation of dateTimeString, offsetString, timeZoneString, and calendarString.
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}{}{}", date_time_string, offset_string, time_zone_string, calendar_string));
}
// 6.5.5 AddZonedDateTime ( epochNanoseconds, timeZone, calendar, years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-addzoneddatetime
ThrowCompletionOr<BigInt*> add_zoned_date_time(VM& vm, BigInt const& epoch_nanoseconds, Value time_zone, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options)
{
// 1. If options is not present, set options to undefined.
// 2. Assert: Type(options) is Object or Undefined.
// 3. If all of years, months, weeks, and days are 0, then
if (years == 0 && months == 0 && weeks == 0 && days == 0) {
// a. Return ? AddInstant(epochNanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
return add_instant(vm, epoch_nanoseconds, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
// 4. Let instant be ! CreateTemporalInstant(epochNanoseconds).
auto* instant = MUST(create_temporal_instant(vm, epoch_nanoseconds));
// 5. Let temporalDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, instant, calendar).
auto* temporal_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, time_zone, *instant, calendar));
// 6. Let datePart be ! CreateTemporalDate(temporalDateTime.[[ISOYear]], temporalDateTime.[[ISOMonth]], temporalDateTime.[[ISODay]], calendar).
auto* date_part = MUST(create_temporal_date(vm, temporal_date_time->iso_year(), temporal_date_time->iso_month(), temporal_date_time->iso_day(), calendar));
// 7. Let dateDuration be ! CreateTemporalDuration(years, months, weeks, days, 0, 0, 0, 0, 0, 0).
auto date_duration = MUST(create_temporal_duration(vm, years, months, weeks, days, 0, 0, 0, 0, 0, 0));
// 8. Let addedDate be ? CalendarDateAdd(calendar, datePart, dateDuration, options).
auto* added_date = TRY(calendar_date_add(vm, calendar, date_part, *date_duration, options));
// 9. Let intermediateDateTime be ? CreateTemporalDateTime(addedDate.[[ISOYear]], addedDate.[[ISOMonth]], addedDate.[[ISODay]], temporalDateTime.[[ISOHour]], temporalDateTime.[[ISOMinute]], temporalDateTime.[[ISOSecond]], temporalDateTime.[[ISOMillisecond]], temporalDateTime.[[ISOMicrosecond]], temporalDateTime.[[ISONanosecond]], calendar).
auto* intermediate_date_time = TRY(create_temporal_date_time(vm, added_date->iso_year(), added_date->iso_month(), added_date->iso_day(), temporal_date_time->iso_hour(), temporal_date_time->iso_minute(), temporal_date_time->iso_second(), temporal_date_time->iso_millisecond(), temporal_date_time->iso_microsecond(), temporal_date_time->iso_nanosecond(), calendar));
// 10. Let intermediateInstant be ? BuiltinTimeZoneGetInstantFor(timeZone, intermediateDateTime, "compatible").
auto intermediate_instant = TRY(builtin_time_zone_get_instant_for(vm, time_zone, *intermediate_date_time, "compatible"sv));
// 11. Return ? AddInstant(intermediateInstant.[[Nanoseconds]], hours, minutes, seconds, milliseconds, microseconds, nanoseconds).
return add_instant(vm, intermediate_instant->nanoseconds(), hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
// 6.5.6 DifferenceZonedDateTime ( ns1, ns2, timeZone, calendar, largestUnit, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencezoneddatetime
ThrowCompletionOr<DurationRecord> difference_zoned_date_time(VM& vm, BigInt const& nanoseconds1, BigInt const& nanoseconds2, Object& time_zone, Object& calendar, StringView largest_unit, Object const& options)
{
// 1. If ns1 is ns2, then
if (nanoseconds1.big_integer() == nanoseconds2.big_integer()) {
// a. Return ! CreateDurationRecord(0, 0, 0, 0, 0, 0, 0, 0, 0, 0).
return create_duration_record(0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
// 2. Let startInstant be ! CreateTemporalInstant(ns1).
auto* start_instant = MUST(create_temporal_instant(vm, nanoseconds1));
// 3. Let startDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, startInstant, calendar).
auto* start_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &time_zone, *start_instant, calendar));
// 4. Let endInstant be ! CreateTemporalInstant(ns2).
auto* end_instant = MUST(create_temporal_instant(vm, nanoseconds2));
// 5. Let endDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(timeZone, endInstant, calendar).
auto* end_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &time_zone, *end_instant, calendar));
// 6. Let dateDifference be ? DifferenceISODateTime(startDateTime.[[ISOYear]], startDateTime.[[ISOMonth]], startDateTime.[[ISODay]], startDateTime.[[ISOHour]], startDateTime.[[ISOMinute]], startDateTime.[[ISOSecond]], startDateTime.[[ISOMillisecond]], startDateTime.[[ISOMicrosecond]], startDateTime.[[ISONanosecond]], endDateTime.[[ISOYear]], endDateTime.[[ISOMonth]], endDateTime.[[ISODay]], endDateTime.[[ISOHour]], endDateTime.[[ISOMinute]], endDateTime.[[ISOSecond]], endDateTime.[[ISOMillisecond]], endDateTime.[[ISOMicrosecond]], endDateTime.[[ISONanosecond]], calendar, largestUnit, options).
auto date_difference = TRY(difference_iso_date_time(vm, start_date_time->iso_year(), start_date_time->iso_month(), start_date_time->iso_day(), start_date_time->iso_hour(), start_date_time->iso_minute(), start_date_time->iso_second(), start_date_time->iso_millisecond(), start_date_time->iso_microsecond(), start_date_time->iso_nanosecond(), end_date_time->iso_year(), end_date_time->iso_month(), end_date_time->iso_day(), end_date_time->iso_hour(), end_date_time->iso_minute(), end_date_time->iso_second(), end_date_time->iso_millisecond(), end_date_time->iso_microsecond(), end_date_time->iso_nanosecond(), calendar, largest_unit, options));
// 7. Let intermediateNs be ? AddZonedDateTime(ns1, timeZone, calendar, dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], 0, 0, 0, 0, 0, 0, 0).
auto* intermediate_ns = TRY(add_zoned_date_time(vm, nanoseconds1, &time_zone, calendar, date_difference.years, date_difference.months, date_difference.weeks, 0, 0, 0, 0, 0, 0, 0));
// 8. Let timeRemainderNs be ns2 - intermediateNs.
auto time_remainder_ns = nanoseconds2.big_integer().minus(intermediate_ns->big_integer());
// 9. Let intermediate be ! CreateTemporalZonedDateTime(intermediateNs, timeZone, calendar).
auto* intermediate = MUST(create_temporal_zoned_date_time(vm, *intermediate_ns, time_zone, calendar));
// 10. Let result be ? NanosecondsToDays(timeRemainderNs, intermediate).
auto result = TRY(nanoseconds_to_days(vm, time_remainder_ns, intermediate));
// 11. Let timeDifference be ! BalanceDuration(0, 0, 0, 0, 0, 0, result.[[Nanoseconds]], "hour").
auto time_difference = MUST(balance_duration(vm, 0, 0, 0, 0, 0, 0, result.nanoseconds, "hour"sv));
// 12. Return ! CreateDurationRecord(dateDifference.[[Years]], dateDifference.[[Months]], dateDifference.[[Weeks]], result.[[Days]], timeDifference.[[Hours]], timeDifference.[[Minutes]], timeDifference.[[Seconds]], timeDifference.[[Milliseconds]], timeDifference.[[Microseconds]], timeDifference.[[Nanoseconds]]).
return create_duration_record(date_difference.years, date_difference.months, date_difference.weeks, result.days, time_difference.hours, time_difference.minutes, time_difference.seconds, time_difference.milliseconds, time_difference.microseconds, time_difference.nanoseconds);
}
// 6.5.7 NanosecondsToDays ( nanoseconds, relativeTo ), https://tc39.es/proposal-temporal/#sec-temporal-nanosecondstodays
ThrowCompletionOr<NanosecondsToDaysResult> nanoseconds_to_days(VM& vm, Crypto::SignedBigInteger nanoseconds, Value relative_to_value)
{
auto& realm = *vm.current_realm();
// 1. Let dayLengthNs be nsPerDay.
auto day_length_ns = ns_per_day_bigint;
// 2. If nanoseconds = 0, then
if (nanoseconds.is_zero()) {
// a. Return the Record { [[Days]]: 0, [[Nanoseconds]]: 0, [[DayLength]]: dayLengthNs }.
return NanosecondsToDaysResult { .days = 0, .nanoseconds = "0"_sbigint, .day_length = day_length_ns.to_double() };
}
// 3. If nanoseconds < 0, let sign be -1; else, let sign be 1.
auto sign = nanoseconds.is_negative() ? -1 : 1;
// 4. If Type(relativeTo) is not Object or relativeTo does not have an [[InitializedTemporalZonedDateTime]] internal slot, then
if (!relative_to_value.is_object() || !is<ZonedDateTime>(relative_to_value.as_object())) {
// a. Return the Record { [[Days]]: truncate(nanoseconds / dayLengthNs), [[Nanoseconds]]: (abs(nanoseconds) modulo dayLengthNs) × sign, [[DayLength]]: dayLengthNs }.
return NanosecondsToDaysResult {
.days = nanoseconds.divided_by(day_length_ns).quotient.to_double(),
.nanoseconds = Crypto::SignedBigInteger { nanoseconds.unsigned_value() }.divided_by(day_length_ns).remainder.multiplied_by(Crypto::SignedBigInteger { sign }),
.day_length = day_length_ns.to_double()
};
}
auto& relative_to = static_cast<ZonedDateTime&>(relative_to_value.as_object());
// 5. Let startNs be (relativeTo.[[Nanoseconds]]).
auto& start_ns = relative_to.nanoseconds().big_integer();
// 6. Let startInstant be ! CreateTemporalInstant((startNs)).
auto* start_instant = MUST(create_temporal_instant(vm, BigInt::create(vm, start_ns)));
// 7. Let startDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], startInstant, relativeTo.[[Calendar]]).
auto* start_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &relative_to.time_zone(), *start_instant, relative_to.calendar()));
// 8. Let endNs be startNs + nanoseconds.
auto end_ns = start_ns.plus(nanoseconds);
auto end_ns_bigint = BigInt::create(vm, end_ns);
// 9. If ! IsValidEpochNanoseconds((endNs)) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(end_ns_bigint))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 10. Let endInstant be ! CreateTemporalInstant((endNs)).
auto* end_instant = MUST(create_temporal_instant(vm, end_ns_bigint));
// 11. Let endDateTime be ? BuiltinTimeZoneGetPlainDateTimeFor(relativeTo.[[TimeZone]], endInstant, relativeTo.[[Calendar]]).
auto* end_date_time = TRY(builtin_time_zone_get_plain_date_time_for(vm, &relative_to.time_zone(), *end_instant, relative_to.calendar()));
// 12. Let dateDifference be ? DifferenceISODateTime(startDateTime.[[ISOYear]], startDateTime.[[ISOMonth]], startDateTime.[[ISODay]], startDateTime.[[ISOHour]], startDateTime.[[ISOMinute]], startDateTime.[[ISOSecond]], startDateTime.[[ISOMillisecond]], startDateTime.[[ISOMicrosecond]], startDateTime.[[ISONanosecond]], endDateTime.[[ISOYear]], endDateTime.[[ISOMonth]], endDateTime.[[ISODay]], endDateTime.[[ISOHour]], endDateTime.[[ISOMinute]], endDateTime.[[ISOSecond]], endDateTime.[[ISOMillisecond]], endDateTime.[[ISOMicrosecond]], endDateTime.[[ISONanosecond]], relativeTo.[[Calendar]], "day", OrdinaryObjectCreate(null)).
auto date_difference = TRY(difference_iso_date_time(vm, start_date_time->iso_year(), start_date_time->iso_month(), start_date_time->iso_day(), start_date_time->iso_hour(), start_date_time->iso_minute(), start_date_time->iso_second(), start_date_time->iso_millisecond(), start_date_time->iso_microsecond(), start_date_time->iso_nanosecond(), end_date_time->iso_year(), end_date_time->iso_month(), end_date_time->iso_day(), end_date_time->iso_hour(), end_date_time->iso_minute(), end_date_time->iso_second(), end_date_time->iso_millisecond(), end_date_time->iso_microsecond(), end_date_time->iso_nanosecond(), relative_to.calendar(), "day"sv, *Object::create(realm, nullptr)));
// 13. Let days be dateDifference.[[Days]].
auto days = date_difference.days;
// 14. Let intermediateNs be (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
auto intermediate_ns = TRY(add_zoned_date_time(vm, BigInt::create(vm, start_ns), &relative_to.time_zone(), relative_to.calendar(), 0, 0, 0, days, 0, 0, 0, 0, 0, 0))->big_integer();
// 15. If sign is 1, then
if (sign == 1) {
// a. Repeat, while days > 0 and intermediateNs > endNs,
while (days > 0 && intermediate_ns > end_ns) {
// i. Set days to days - 1.
days--;
// ii. Set intermediateNs to (? AddZonedDateTime((startNs), relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)).
intermediate_ns = TRY(add_zoned_date_time(vm, BigInt::create(vm, start_ns), &relative_to.time_zone(), relative_to.calendar(), 0, 0, 0, days, 0, 0, 0, 0, 0, 0))->big_integer();
}
}
// 16. Set nanoseconds to endNs - intermediateNs.
nanoseconds = end_ns.minus(intermediate_ns);
// 17. Let done be false.
// 18. Repeat, while done is false,
while (true) {
// a. Let oneDayFartherNs be (? AddZonedDateTime((intermediateNs), relativeTo.[[TimeZone]], relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)).
auto one_day_farther_ns = TRY(add_zoned_date_time(vm, BigInt::create(vm, intermediate_ns), &relative_to.time_zone(), relative_to.calendar(), 0, 0, 0, sign, 0, 0, 0, 0, 0, 0))->big_integer();
// b. Set dayLengthNs to oneDayFartherNs - intermediateNs.
day_length_ns = one_day_farther_ns.minus(intermediate_ns);
// c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
if (nanoseconds.minus(day_length_ns).multiplied_by(Crypto::SignedBigInteger { sign }) >= "0"_sbigint) {
// i. Set nanoseconds to nanoseconds - dayLengthNs.
nanoseconds = nanoseconds.minus(day_length_ns);
// ii. Set intermediateNs to oneDayFartherNs.
intermediate_ns = move(one_day_farther_ns);
// iii. Set days to days + sign.
days += sign;
}
// d. Else,
else {
// i. Set done to true.
break;
}
}
// 19. If days < 0 and sign = 1, throw a RangeError exception.
if (days < 0 && sign == 1)
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToDaysWithOppositeSign);
// 20. If days > 0 and sign = -1, throw a RangeError exception.
if (days > 0 && sign == -1)
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToDaysWithOppositeSign);
// 21. If nanoseconds < 0 and sign = 1, throw a RangeError exception.
if (nanoseconds.is_negative() && sign == 1)
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToRemainderOfNanosecondsWithOppositeSign);
// 22. If nanoseconds > 0 and sign = -1, throw a RangeError exception.
if (nanoseconds.is_positive() && sign == -1)
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToRemainderOfNanosecondsWithOppositeSign);
// 23. If abs(nanoseconds) ≥ abs(dayLengthNs), throw a RangeError exception.
auto compare_result = nanoseconds.unsigned_value().compare_to_double(fabs(day_length_ns.to_double()));
if (compare_result == Crypto::UnsignedBigInteger::CompareResult::DoubleLessThanBigInt || compare_result == Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt)
return vm.throw_completion<RangeError>(ErrorType::TemporalNanosecondsConvertedToRemainderOfNanosecondsLongerThanDayLength);
// 24. Return the Record { [[Days]]: days, [[Nanoseconds]]: nanoseconds, [[DayLength]]: abs(dayLengthNs) }.
return NanosecondsToDaysResult { .days = days, .nanoseconds = move(nanoseconds), .day_length = fabs(day_length_ns.to_double()) };
}
// 6.5.8 DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalzoneddatetime
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_zoned_date_time(VM& vm, DifferenceOperation operation, ZonedDateTime& zoned_date_time, Value other_value, Value options_value)
{
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalZonedDateTime(other).
auto* other = TRY(to_temporal_zoned_date_time(vm, other_value));
// 3. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then
if (!TRY(calendar_equals(vm, zoned_date_time.calendar(), other->calendar()))) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalDifferentCalendars);
}
// 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « », "nanosecond", "hour").
auto settings = TRY(get_difference_settings(vm, operation, options_value, UnitGroup::DateTime, {}, { "nanosecond"sv }, "hour"sv));
// 5. If settings.[[LargestUnit]] is not one of "year", "month", "week", or "day", then
if (!settings.largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
// a. Let result be DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[LargestUnit]], settings.[[RoundingMode]]).
auto result = difference_instant(vm, zoned_date_time.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode);
// b. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds);
}
// 6. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
if (!TRY(time_zone_equals(vm, zoned_date_time.time_zone(), other->time_zone()))) {
// a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(ErrorType::TemporalDifferentTimeZones);
}
// 7. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], settings.[[LargestUnit]]).
auto* until_options = TRY(merge_largest_unit_option(vm, settings.options, settings.largest_unit));
// 8. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], settings.[[LargestUnit]], untilOptions).
auto difference = TRY(difference_zoned_date_time(vm, zoned_date_time.nanoseconds(), other->nanoseconds(), zoned_date_time.time_zone(), zoned_date_time.calendar(), settings.largest_unit, *until_options));
auto calendar_record = TRY(create_calendar_methods_record(vm, GC::Ref<Object> { zoned_date_time.calendar() }, { { CalendarMethod::DateAdd, CalendarMethod::DateFromFields, CalendarMethod::DateUntil, CalendarMethod::Fields } }));
// 9. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime)).[[DurationRecord]].
auto round_result = TRY(round_duration(vm, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &zoned_date_time, calendar_record)).duration_record;
// 10. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime).
auto result = TRY(adjust_rounded_duration_days(vm, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &zoned_date_time));
// 11. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(vm, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
}
// 6.5.9 AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime
ThrowCompletionOr<ZonedDateTime*> add_duration_to_or_subtract_duration_from_zoned_date_time(VM& vm, ArithmeticOperation operation, ZonedDateTime& zoned_date_time, Value temporal_duration_like, Value options_value)
{
// 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == ArithmeticOperation::Subtract ? -1 : 1;
// 2. Let duration be ? ToTemporalDurationRecord(temporalDurationLike).
auto duration = TRY(to_temporal_duration_record(vm, temporal_duration_like));
// 3. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(vm, options_value));
// 4. Let timeZone be zonedDateTime.[[TimeZone]].
auto& time_zone = zoned_date_time.time_zone();
// 5. Let calendar be zonedDateTime.[[Calendar]].
auto& calendar = zoned_date_time.calendar();
// 6. Let epochNanoseconds be ? AddZonedDateTime(zonedDateTime.[[Nanoseconds]], timeZone, calendar, sign × duration.[[Years]], sign × duration.[[Months]], sign × duration.[[Weeks]], sign × duration.[[Days]], sign × duration.[[Hours]], sign × duration.[[Minutes]], sign × duration.[[Seconds]], sign × duration.[[Milliseconds]], sign × duration.[[Microseconds]], sign × duration.[[Nanoseconds]], options).
auto* epoch_nanoseconds = TRY(add_zoned_date_time(vm, zoned_date_time.nanoseconds(), &time_zone, calendar, sign * duration.years, sign * duration.months, sign * duration.weeks, sign * duration.days, sign * duration.hours, sign * duration.minutes, sign * duration.seconds, sign * duration.milliseconds, sign * duration.microseconds, sign * duration.nanoseconds, options));
// 7. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
return MUST(create_temporal_zoned_date_time(vm, *epoch_nanoseconds, time_zone, calendar));
}
}

View file

@ -1,66 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
namespace JS::Temporal {
class ZonedDateTime final : public Object {
JS_OBJECT(ZonedDateTime, Object);
GC_DECLARE_ALLOCATOR(ZonedDateTime);
public:
virtual ~ZonedDateTime() override = default;
[[nodiscard]] BigInt const& nanoseconds() const { return m_nanoseconds; }
[[nodiscard]] Object const& time_zone() const { return m_time_zone; }
[[nodiscard]] Object& time_zone() { return m_time_zone; }
[[nodiscard]] Object const& calendar() const { return m_calendar; }
[[nodiscard]] Object& calendar() { return m_calendar; }
private:
ZonedDateTime(BigInt const& nanoseconds, Object& time_zone, Object& calendar, Object& prototype);
virtual void visit_edges(Visitor&) override;
// 6.4 Properties of Temporal.ZonedDateTime Instances, https://tc39.es/proposal-temporal/#sec-properties-of-temporal-zoneddatetime-instances
GC::Ref<BigInt const> m_nanoseconds; // [[Nanoseconds]]
GC::Ref<Object> m_time_zone; // [[TimeZone]]
GC::Ref<Object> m_calendar; // [[Calendar]]
};
struct NanosecondsToDaysResult {
double days;
Crypto::SignedBigInteger nanoseconds;
double day_length;
};
enum class OffsetBehavior {
Option,
Exact,
Wall,
};
enum class MatchBehavior {
MatchExactly,
MatchMinutes,
};
ThrowCompletionOr<BigInt const*> interpret_iso_date_time_offset(VM&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, OffsetBehavior offset_behavior, double offset_nanoseconds, Value time_zone, StringView disambiguation, StringView offset_option, MatchBehavior match_behavior);
ThrowCompletionOr<ZonedDateTime*> to_temporal_zoned_date_time(VM&, Value item, Object const* options = nullptr);
ThrowCompletionOr<ZonedDateTime*> create_temporal_zoned_date_time(VM&, BigInt const& epoch_nanoseconds, Object& time_zone, Object& calendar, FunctionObject const* new_target = nullptr);
ThrowCompletionOr<String> temporal_zoned_date_time_to_string(VM&, ZonedDateTime& zoned_date_time, Variant<StringView, u8> const& precision, StringView show_calendar, StringView show_time_zone, StringView show_offset, Optional<u64> increment = {}, Optional<StringView> unit = {}, Optional<StringView> rounding_mode = {});
ThrowCompletionOr<BigInt*> add_zoned_date_time(VM&, BigInt const& epoch_nanoseconds, Value time_zone, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options = nullptr);
ThrowCompletionOr<DurationRecord> difference_zoned_date_time(VM&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, Object& time_zone, Object& calendar, StringView largest_unit, Object const& options);
ThrowCompletionOr<NanosecondsToDaysResult> nanoseconds_to_days(VM&, Crypto::SignedBigInteger nanoseconds, Value relative_to);
ThrowCompletionOr<GC::Ref<Duration>> difference_temporal_zoned_date_time(VM&, DifferenceOperation, ZonedDateTime&, Value other, Value options);
ThrowCompletionOr<ZonedDateTime*> add_duration_to_or_subtract_duration_from_zoned_date_time(VM&, ArithmeticOperation, ZonedDateTime&, Value temporal_duration_like, Value options_value);
}

View file

@ -1,116 +0,0 @@
/*
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TypeCasts.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/Calendar.h>
#include <LibJS/Runtime/Temporal/Instant.h>
#include <LibJS/Runtime/Temporal/TimeZone.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
namespace JS::Temporal {
GC_DEFINE_ALLOCATOR(ZonedDateTimeConstructor);
// 6.1 The Temporal.ZonedDateTime Constructor, https://tc39.es/proposal-temporal/#sec-temporal-zoneddatetime-constructor
ZonedDateTimeConstructor::ZonedDateTimeConstructor(Realm& realm)
: NativeFunction(realm.vm().names.ZonedDateTime.as_string(), realm.intrinsics().function_prototype())
{
}
void ZonedDateTimeConstructor::initialize(Realm& realm)
{
Base::initialize(realm);
auto& vm = this->vm();
// 6.2.1 Temporal.ZonedDateTime.prototype, https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype
define_direct_property(vm.names.prototype, realm.intrinsics().temporal_zoned_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(2), Attribute::Configurable);
}
// 6.1.1 Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime
ThrowCompletionOr<Value> ZonedDateTimeConstructor::call()
{
auto& vm = this->vm();
// 1. If NewTarget is undefined, then
// a. Throw a TypeError exception.
return vm.throw_completion<TypeError>(ErrorType::ConstructorWithoutNew, "Temporal.ZonedDateTime");
}
// 6.1.1 Temporal.ZonedDateTime ( epochNanoseconds, timeZoneLike [ , calendarLike ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime
ThrowCompletionOr<GC::Ref<Object>> ZonedDateTimeConstructor::construct(FunctionObject& new_target)
{
auto& vm = this->vm();
// 2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).
auto epoch_nanoseconds = TRY(vm.argument(0).to_bigint(vm));
// 3. If ! IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
// 4. Let timeZone be ? ToTemporalTimeZone(timeZoneLike).
auto* time_zone = TRY(to_temporal_time_zone(vm, vm.argument(1)));
// 5. Let calendar be ? ToTemporalCalendarWithISODefault(calendarLike).
auto* calendar = TRY(to_temporal_calendar_with_iso_default(vm, vm.argument(2)));
// 6. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget).
return *TRY(create_temporal_zoned_date_time(vm, epoch_nanoseconds, *time_zone, *calendar, &new_target));
}
// 6.2.2 Temporal.ZonedDateTime.from ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.from
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimeConstructor::from)
{
// 1. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, vm.argument(1)));
auto item = vm.argument(0);
// 2. If Type(item) is Object and item has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (item.is_object() && is<ZonedDateTime>(item.as_object())) {
auto& item_object = static_cast<ZonedDateTime&>(item.as_object());
// a. Perform ? ToTemporalOverflow(options).
(void)TRY(to_temporal_overflow(vm, options));
// b. Perform ? ToTemporalDisambiguation(options).
(void)TRY(to_temporal_disambiguation(vm, options));
// c. Perform ? ToTemporalOffset(options, "reject").
(void)TRY(to_temporal_offset(vm, options, "reject"sv));
// d. Return ! CreateTemporalZonedDateTime(item.[[Nanoseconds]], item.[[TimeZone]], item.[[Calendar]]).
return MUST(create_temporal_zoned_date_time(vm, item_object.nanoseconds(), item_object.time_zone(), item_object.calendar()));
}
// 3. Return ? ToTemporalZonedDateTime(item, options).
return TRY(to_temporal_zoned_date_time(vm, item, options));
}
// 6.2.3 Temporal.ZonedDateTime.compare ( one, two ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.compare
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimeConstructor::compare)
{
// 1. Set one to ? ToTemporalZonedDateTime(one).
auto* one = TRY(to_temporal_zoned_date_time(vm, vm.argument(0)));
// 2. Set two to ? ToTemporalZonedDateTime(two).
auto* two = TRY(to_temporal_zoned_date_time(vm, vm.argument(1)));
// 3. Return 𝔽(! CompareEpochNanoseconds(one.[[Nanoseconds]], two.[[Nanoseconds]])).
return Value(compare_epoch_nanoseconds(one->nanoseconds(), two->nanoseconds()));
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS::Temporal {
class ZonedDateTimeConstructor final : public NativeFunction {
JS_OBJECT(ZonedDateTimeConstructor, NativeFunction);
GC_DECLARE_ALLOCATOR(ZonedDateTimeConstructor);
public:
virtual void initialize(Realm&) override;
virtual ~ZonedDateTimeConstructor() override = default;
virtual ThrowCompletionOr<Value> call() override;
virtual ThrowCompletionOr<GC::Ref<Object>> construct(FunctionObject& new_target) override;
private:
explicit ZonedDateTimeConstructor(Realm&);
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_FUNCTION(from);
JS_DECLARE_NATIVE_FUNCTION(compare);
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,81 +0,0 @@
/*
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/PrototypeObject.h>
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
namespace JS::Temporal {
class ZonedDateTimePrototype final : public PrototypeObject<ZonedDateTimePrototype, ZonedDateTime> {
JS_PROTOTYPE_OBJECT(ZonedDateTimePrototype, ZonedDateTime, Temporal.ZonedDateTime);
GC_DECLARE_ALLOCATOR(ZonedDateTimePrototype);
public:
virtual void initialize(Realm&) override;
virtual ~ZonedDateTimePrototype() override = default;
private:
explicit ZonedDateTimePrototype(Realm&);
JS_DECLARE_NATIVE_FUNCTION(calendar_getter);
JS_DECLARE_NATIVE_FUNCTION(calendar_id_getter);
JS_DECLARE_NATIVE_FUNCTION(time_zone_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(epoch_seconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_milliseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_microseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(epoch_nanoseconds_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(hours_in_day_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);
JS_DECLARE_NATIVE_FUNCTION(offset_nanoseconds_getter);
JS_DECLARE_NATIVE_FUNCTION(offset_getter);
JS_DECLARE_NATIVE_FUNCTION(era_getter);
JS_DECLARE_NATIVE_FUNCTION(era_year_getter);
JS_DECLARE_NATIVE_FUNCTION(with);
JS_DECLARE_NATIVE_FUNCTION(with_plain_time);
JS_DECLARE_NATIVE_FUNCTION(with_plain_date);
JS_DECLARE_NATIVE_FUNCTION(with_time_zone);
JS_DECLARE_NATIVE_FUNCTION(with_calendar);
JS_DECLARE_NATIVE_FUNCTION(add);
JS_DECLARE_NATIVE_FUNCTION(subtract);
JS_DECLARE_NATIVE_FUNCTION(until);
JS_DECLARE_NATIVE_FUNCTION(since);
JS_DECLARE_NATIVE_FUNCTION(round);
JS_DECLARE_NATIVE_FUNCTION(equals);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
JS_DECLARE_NATIVE_FUNCTION(to_json);
JS_DECLARE_NATIVE_FUNCTION(value_of);
JS_DECLARE_NATIVE_FUNCTION(start_of_day);
JS_DECLARE_NATIVE_FUNCTION(to_instant);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date);
JS_DECLARE_NATIVE_FUNCTION(to_plain_time);
JS_DECLARE_NATIVE_FUNCTION(to_plain_date_time);
JS_DECLARE_NATIVE_FUNCTION(to_plain_year_month);
JS_DECLARE_NATIVE_FUNCTION(to_plain_month_day);
JS_DECLARE_NATIVE_FUNCTION(get_iso_fields);
};
}

View file

@ -87,7 +87,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::to_base64)
auto typed_array = TRY(validate_uint8_array(vm)); auto typed_array = TRY(validate_uint8_array(vm));
// 3. Let opts be ? GetOptionsObject(options). // 3. Let opts be ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 4. Let alphabet be ? Get(opts, "alphabet"). // 4. Let alphabet be ? Get(opts, "alphabet").
// 5. If alphabet is undefined, set alphabet to "base64". // 5. If alphabet is undefined, set alphabet to "base64".
@ -159,7 +159,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayConstructorHelpers::from_base64)
return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value); return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value);
// 2. Let opts be ? GetOptionsObject(options). // 2. Let opts be ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 3. Let alphabet be ? Get(opts, "alphabet"). // 3. Let alphabet be ? Get(opts, "alphabet").
// 4. If alphabet is undefined, set alphabet to "base64". // 4. If alphabet is undefined, set alphabet to "base64".
@ -214,7 +214,7 @@ JS_DEFINE_NATIVE_FUNCTION(Uint8ArrayPrototypeHelpers::set_from_base64)
return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value); return vm.throw_completion<TypeError>(ErrorType::NotAString, string_value);
// 4. Let opts be ? GetOptionsObject(options). // 4. Let opts be ? GetOptionsObject(options).
auto* options = TRY(Temporal::get_options_object(vm, options_value)); auto options = TRY(Temporal::get_options_object(vm, options_value));
// 5. Let alphabet be ? Get(opts, "alphabet"). // 5. Let alphabet be ? Get(opts, "alphabet").
// 6. If alphabet is undefined, set alphabet to "base64". // 6. If alphabet is undefined, set alphabet to "base64".

View file

@ -1,15 +0,0 @@
describe("correct behavior", () => {
test("basic functionality", () => {
const date = new Date("2021-07-09T01:36:00Z");
const instant = date.toTemporalInstant();
expect(instant.epochSeconds).toBe(1625794560);
});
});
describe("errors", () => {
test("this value must be a Date object", () => {
expect(() => {
Date.prototype.toTemporalInstant.call(123);
}).toThrowWithMessage(TypeError, "Not an object of type Date");
});
});

View file

@ -1,73 +0,0 @@
describe("normal behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.from).toHaveLength(1);
});
test("basic functionality", () => {
const timeZone = new Temporal.TimeZone("UTC");
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const plainTime = new Temporal.PlainTime();
const plainDateTime = new Temporal.PlainDateTime(1970, 1, 1);
const plainMonthDay = new Temporal.PlainMonthDay(1, 1);
const plainYearMonth = new Temporal.PlainYearMonth(1970, 1);
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
const calendarLike = {};
const withCalendarLike = { calendar: {} };
expect(Temporal.Calendar.from(plainDate)).toBe(plainDate.calendar);
expect(Temporal.Calendar.from(plainTime)).toBe(plainTime.calendar);
expect(Temporal.Calendar.from(plainDateTime)).toBe(plainDateTime.calendar);
expect(Temporal.Calendar.from(plainMonthDay)).toBe(plainMonthDay.calendar);
expect(Temporal.Calendar.from(plainYearMonth)).toBe(plainYearMonth.calendar);
expect(Temporal.Calendar.from(zonedDateTime)).toBe(zonedDateTime.calendar);
expect(Temporal.Calendar.from(calendarLike)).toBe(calendarLike);
expect(Temporal.Calendar.from(withCalendarLike)).toBe(withCalendarLike.calendar);
expect(Temporal.Calendar.from("iso8601").id).toBe("iso8601");
expect(Temporal.Calendar.from("2021-07-06[u-ca=iso8601]").id).toBe("iso8601");
});
test("ToTemporalCalendar fast path returns if it is passed a Temporal.Calendar instance", () => {
// This is obseravble via there being no property lookups (avoiding a "calendar" property lookup in this case)
let madeObservableHasPropertyLookup = false;
class Calendar extends Temporal.Calendar {
constructor() {
super("iso8601");
}
get calendar() {
madeObservableHasPropertyLookup = true;
return this;
}
}
const calendar = new Calendar();
Temporal.Calendar.from(calendar);
expect(madeObservableHasPropertyLookup).toBeFalse();
});
});
describe("errors", () => {
test("Calendar from TimeZone", () => {
const timeZone = new Temporal.TimeZone("UTC");
expect(() => {
Temporal.Calendar.from(timeZone);
}).toThrowWithMessage(
RangeError,
"Got unexpected TimeZone object in conversion to Calendar"
);
});
test("yyyy-mm and mm-dd strings can only use the iso8601 calendar", () => {
// FIXME: The error message doesn't really indicate this is the case.
const values = [
"02-10[u-ca=iso8602]",
"02-10[u-ca=SerenityOS]",
"2023-02[u-ca=iso8602]",
"2023-02[u-ca=SerenityOS]",
];
for (const value of values) {
expect(() => {
Temporal.Calendar.from(value);
}).toThrowWithMessage(RangeError, `Invalid calendar string '${value}'`);
}
});
});

View file

@ -1,37 +0,0 @@
describe("errors", () => {
test("called without new", () => {
expect(() => {
Temporal.Calendar();
}).toThrowWithMessage(TypeError, "Temporal.Calendar constructor must be called with 'new'");
});
test("argument must be coercible to string", () => {
expect(() => {
new Temporal.Calendar({
toString() {
throw new Error();
},
});
}).toThrow(Error);
});
test("invalid calendar identifier", () => {
expect(() => {
new Temporal.Calendar("foo");
}).toThrowWithMessage(RangeError, "Invalid calendar identifier 'foo'");
});
});
describe("normal behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
expect(calendar.id).toBe("iso8601");
expect(typeof calendar).toBe("object");
expect(calendar).toBeInstanceOf(Temporal.Calendar);
expect(Object.getPrototypeOf(calendar)).toBe(Temporal.Calendar.prototype);
});
});

View file

@ -1,3 +0,0 @@
test("basic functionality", () => {
expect(Temporal.Calendar.prototype[Symbol.toStringTag]).toBe("Temporal.Calendar");
});

View file

@ -1,15 +0,0 @@
describe("correct behavior", () => {
test("length is 2", () => {
expect(Temporal.Calendar.prototype.dateAdd).toHaveLength(2);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const plainDate = new Temporal.PlainDate(1970, 1, 1);
const duration = new Temporal.Duration(1, 2, 3, 4);
const newPlainDate = calendar.dateAdd(plainDate, duration);
expect(newPlainDate.year).toBe(1971);
expect(newPlainDate.month).toBe(3);
expect(newPlainDate.day).toBe(26);
});
});

View file

@ -1,54 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.dateFromFields).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = calendar.dateFromFields({ year: 2000, month: 5, day: 2 });
expect(date.calendar).toBe(calendar);
});
test("gets overflow after temporal fields", () => {
const operations = [];
const calendar = new Temporal.Calendar("iso8601");
const fields = {
get day() {
operations.push("get day");
return 3;
},
get month() {
operations.push("get month");
return 10;
},
get monthCode() {
operations.push("get monthCode");
return "M10";
},
get year() {
operations.push("get year");
return 2022;
},
};
const options = {
get overflow() {
operations.push("get overflow");
return "constrain";
},
};
expect(operations).toHaveLength(0);
calendar.dateFromFields(fields, options);
expect(operations).toHaveLength(5);
expect(operations[0]).toBe("get day");
expect(operations[1]).toBe("get month");
expect(operations[2]).toBe("get monthCode");
expect(operations[3]).toBe("get year");
expect(operations[4]).toBe("get overflow");
});
});

View file

@ -1,79 +0,0 @@
describe("correct behavior", () => {
test("length is 2", () => {
expect(Temporal.Calendar.prototype.dateUntil).toHaveLength(2);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const one = new Temporal.PlainDate(2021, 7, 6);
const two = new Temporal.PlainDate(2021, 10, 10);
const oneToTwo = calendar.dateUntil(one, two);
expect(oneToTwo.years).toBe(0);
expect(oneToTwo.months).toBe(0);
expect(oneToTwo.weeks).toBe(0);
expect(oneToTwo.days).toBe(96);
expect(oneToTwo.hours).toBe(0);
expect(oneToTwo.minutes).toBe(0);
expect(oneToTwo.seconds).toBe(0);
expect(oneToTwo.milliseconds).toBe(0);
expect(oneToTwo.microseconds).toBe(0);
expect(oneToTwo.nanoseconds).toBe(0);
const twoToOne = calendar.dateUntil(two, one);
expect(twoToOne.years).toBe(0);
expect(twoToOne.months).toBe(0);
expect(twoToOne.weeks).toBe(0);
expect(twoToOne.days).toBe(-96);
expect(twoToOne.hours).toBe(0);
expect(twoToOne.minutes).toBe(0);
expect(twoToOne.seconds).toBe(0);
expect(twoToOne.milliseconds).toBe(0);
expect(twoToOne.microseconds).toBe(0);
expect(twoToOne.nanoseconds).toBe(0);
});
test("largestUnit option", () => {
const calendar = new Temporal.Calendar("iso8601");
const one = new Temporal.PlainDate(1970, 1, 1);
const two = new Temporal.PlainDate(2021, 7, 6);
const values = [
["years", 51, 6, 0, 5],
["months", 0, 618, 0, 5],
["weeks", 0, 0, 2687, 5],
["days", 0, 0, 0, 18814],
];
for (const [largestUnit, years, months, weeks, days] of values) {
const duration = calendar.dateUntil(one, two, { largestUnit });
expect(duration.years).toBe(years);
expect(duration.months).toBe(months);
expect(duration.weeks).toBe(weeks);
expect(duration.days).toBe(days);
expect(duration.hours).toBe(0);
expect(duration.minutes).toBe(0);
expect(duration.seconds).toBe(0);
expect(duration.milliseconds).toBe(0);
expect(duration.microseconds).toBe(0);
expect(duration.nanoseconds).toBe(0);
}
});
});
describe("errors", () => {
test("forbidden largestUnit option values", () => {
const calendar = new Temporal.Calendar("iso8601");
const one = new Temporal.PlainDate(1970, 1, 1);
const two = new Temporal.PlainDate(2021, 7, 6);
const values = ["hour", "minute", "second", "millisecond", "microsecond", "nanosecond"];
for (const largestUnit of values) {
expect(() => {
calendar.dateUntil(one, two, { largestUnit });
}).toThrowWithMessage(
RangeError,
`${largestUnit} is not a valid value for option largestUnit`
);
}
});
});

View file

@ -1,11 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.day).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = new Temporal.PlainDate(2021, 7, 23);
expect(calendar.day(date)).toBe(23);
});
});

View file

@ -1,11 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.dayOfWeek).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = new Temporal.PlainDate(2021, 7, 23);
expect(calendar.dayOfWeek(date)).toBe(5);
});
});

View file

@ -1,11 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.dayOfYear).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = new Temporal.PlainDate(2021, 7, 23);
expect(calendar.dayOfYear(date)).toBe(204);
});
});

View file

@ -1,11 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.daysInMonth).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = new Temporal.PlainDate(2021, 7, 23);
expect(calendar.daysInMonth(date)).toBe(31);
});
});

View file

@ -1,11 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.daysInWeek).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = new Temporal.PlainDate(2021, 7, 23);
expect(calendar.daysInWeek(date)).toBe(7);
});
});

View file

@ -1,11 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.daysInYear).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const date = new Temporal.PlainDate(2021, 7, 23);
expect(calendar.daysInYear(date)).toBe(365);
});
});

View file

@ -1,26 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.era).toHaveLength(1);
});
test("basic functionality", () => {
const plainDate = new Temporal.PlainDate(2021, 7, 6);
const calendar = new Temporal.Calendar("iso8601");
expect(calendar.era(plainDate)).toBeUndefined();
});
});
describe("errors", () => {
test("this value must be a Temporal.Calendar object", () => {
expect(() => {
Temporal.Calendar.prototype.era.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Calendar");
});
test("argument must be date-like", () => {
const calendar = new Temporal.Calendar("iso8601");
expect(() => {
calendar.era({});
}).toThrowWithMessage(TypeError, "Required property day is missing or undefined");
});
});

View file

@ -1,26 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.eraYear).toHaveLength(1);
});
test("basic functionality", () => {
const plainDate = new Temporal.PlainDate(2021, 7, 6);
const calendar = new Temporal.Calendar("iso8601");
expect(calendar.eraYear(plainDate)).toBeUndefined();
});
});
describe("errors", () => {
test("this value must be a Temporal.Calendar object", () => {
expect(() => {
Temporal.Calendar.prototype.eraYear.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Calendar");
});
test("argument must be date-like", () => {
const calendar = new Temporal.Calendar("iso8601");
expect(() => {
calendar.eraYear({});
}).toThrowWithMessage(TypeError, "Required property day is missing or undefined");
});
});

View file

@ -1,55 +0,0 @@
describe("correct behavior", () => {
test("length is 1", () => {
expect(Temporal.Calendar.prototype.fields).toHaveLength(1);
});
test("basic functionality", () => {
const calendar = new Temporal.Calendar("iso8601");
const array = [
"year",
"month",
"monthCode",
"day",
"hour",
"minute",
"second",
"millisecond",
"microsecond",
"nanosecond",
];
const fields = calendar.fields(array);
expect(fields).toEqual(array);
expect(fields).not.toBe(array);
});
});
describe("errors", () => {
test("this value must be a Temporal.Calendar object", () => {
expect(() => {
Temporal.Calendar.prototype.fields.call("foo");
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.Calendar");
});
test("iterator values must be strings", () => {
const calendar = new Temporal.Calendar("iso8601");
for (const value of [123, null, undefined, true, {}]) {
expect(() => {
calendar.fields([value]);
}).toThrowWithMessage(TypeError, `Invalid calendar field ${value}, expected a string`);
}
});
test("iterator values must be valid field names", () => {
const calendar = new Temporal.Calendar("iso8601");
expect(() => {
calendar.fields(["foo"]);
}).toThrowWithMessage(RangeError, "Invalid calendar field 'foo'");
});
test("iterator values must not contain duplicates", () => {
const calendar = new Temporal.Calendar("iso8601");
expect(() => {
calendar.fields(["year", "month", "year", "month"]);
}).toThrowWithMessage(RangeError, "Duplicate calendar field 'year'");
});
});

Some files were not shown because too many files have changed in this diff Show more