mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-21 15:10:19 +00:00
Compare commits
24 commits
061d36d603
...
14df797360
Author | SHA1 | Date | |
---|---|---|---|
|
14df797360 | ||
|
d6bcd3fb0b | ||
|
d527c5df5d | ||
|
13f349aea2 | ||
|
84f673515b | ||
|
4e48298414 | ||
|
bf5cf720b5 | ||
|
488034477a | ||
|
f57ff63432 | ||
|
c715711f88 | ||
|
5689621c2b | ||
|
4742775262 | ||
|
a80523be18 | ||
|
55c81482b0 | ||
|
dfaa3bf649 | ||
|
5fe0d3352d | ||
|
eca378a7a3 | ||
|
e4e05837e1 | ||
|
c8d2404230 | ||
|
d368fcadac | ||
|
f7517c5b8d | ||
|
b94307583b | ||
|
e236f1d2ae | ||
|
e467b38d3d |
410 changed files with 2886 additions and 30932 deletions
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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") \
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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));
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
|
@ -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
|
@ -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
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
@ -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;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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(); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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
|
@ -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);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -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".
|
||||||
|
|
|
@ -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");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,3 +0,0 @@
|
||||||
test("basic functionality", () => {
|
|
||||||
expect(Temporal.Calendar.prototype[Symbol.toStringTag]).toBe("Temporal.Calendar");
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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");
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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
Loading…
Reference in a new issue