mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-12 02:59:45 +00:00
LibJS: Implement the Temporal.ZonedDateTime constructor
And the simple Temporal.ZonedDateTime.prototype getters, so that the constructed Temporal.ZonedDateTime may actually be validated.
This commit is contained in:
parent
8ab765a3eb
commit
8c73cae2b8
Notes:
github-actions[bot]
2024-11-26 10:04:22 +00:00
Author: https://github.com/trflynn89
Commit: 8c73cae2b8
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2577
Reviewed-by: https://github.com/shannonbooth ✅
48 changed files with 1757 additions and 23 deletions
|
@ -232,6 +232,9 @@ set(SOURCES
|
|||
Runtime/Temporal/PlainYearMonthPrototype.cpp
|
||||
Runtime/Temporal/Temporal.cpp
|
||||
Runtime/Temporal/TimeZone.cpp
|
||||
Runtime/Temporal/ZonedDateTime.cpp
|
||||
Runtime/Temporal/ZonedDateTimeConstructor.cpp
|
||||
Runtime/Temporal/ZonedDateTimePrototype.cpp
|
||||
Runtime/TypedArray.cpp
|
||||
Runtime/TypedArrayConstructor.cpp
|
||||
Runtime/TypedArrayPrototype.cpp
|
||||
|
|
|
@ -94,7 +94,8 @@
|
|||
__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(PlainYearMonth, plain_year_month, PlainYearMonthPrototype, PlainYearMonthConstructor) \
|
||||
__JS_ENUMERATE(ZonedDateTime, zoned_date_time, ZonedDateTimePrototype, ZonedDateTimeConstructor)
|
||||
|
||||
#define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \
|
||||
__JS_ENUMERATE(AtomicsObject, atomics) \
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibJS/Runtime/WeakMap.h>
|
||||
|
@ -893,6 +894,18 @@ ErrorOr<void> print_temporal_plain_year_month(JS::PrintContext& print_context, J
|
|||
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.epoch_nanoseconds(), seen_objects));
|
||||
TRY(js_out(print_context, "\n timeZone: "));
|
||||
TRY(print_value(print_context, JS::PrimitiveString::create(zoned_date_time.vm(), zoned_date_time.time_zone()), seen_objects));
|
||||
TRY(js_out(print_context, "\n calendar: "));
|
||||
TRY(print_value(print_context, JS::PrimitiveString::create(zoned_date_time.vm(), zoned_date_time.calendar()), seen_objects));
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<void> print_boolean_object(JS::PrintContext& print_context, JS::BooleanObject const& boolean_object, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
TRY(print_type(print_context, "Boolean"sv));
|
||||
|
@ -1022,6 +1035,8 @@ ErrorOr<void> print_value(JS::PrintContext& print_context, JS::Value value, Hash
|
|||
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::ZonedDateTime>(object))
|
||||
return print_temporal_zoned_date_time(print_context, static_cast<JS::Temporal::ZonedDateTime&>(object), seen_objects);
|
||||
return print_object(print_context, object, seen_objects);
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,8 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibJS/Runtime/TypedArrayConstructor.h>
|
||||
#include <LibJS/Runtime/TypedArrayPrototype.h>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
|
@ -100,6 +101,29 @@ ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM& vm, Object const& o
|
|||
return Overflow::Reject;
|
||||
}
|
||||
|
||||
// 13.7 GetTemporalDisambiguationOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporaldisambiguationoption
|
||||
ThrowCompletionOr<Disambiguation> get_temporal_disambiguation_option(VM& vm, Object const& options)
|
||||
{
|
||||
// 1. Let stringValue be ? GetOption(options, "disambiguation", STRING, « "compatible", "earlier", "later", "reject" », "compatible").
|
||||
auto string_value = TRY(get_option(vm, options, vm.names.disambiguation, OptionType::String, { "compatible"sv, "earlier"sv, "later"sv, "reject"sv }, "compatible"sv));
|
||||
auto string_view = string_value.as_string().utf8_string_view();
|
||||
|
||||
// 2. If stringValue is "compatible", return COMPATIBLE.
|
||||
if (string_view == "compatible"sv)
|
||||
return Disambiguation::Compatible;
|
||||
|
||||
// 3. If stringValue is "earlier", return EARLIER.
|
||||
if (string_view == "earlier"sv)
|
||||
return Disambiguation::Earlier;
|
||||
|
||||
// 4. If stringValue is "later", return LATER.
|
||||
if (string_view == "later"sv)
|
||||
return Disambiguation::Later;
|
||||
|
||||
// 5. Return REJECT.
|
||||
return Disambiguation::Reject;
|
||||
}
|
||||
|
||||
// 13.8 NegateRoundingMode ( roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-negateroundingmode
|
||||
RoundingMode negate_rounding_mode(RoundingMode rounding_mode)
|
||||
{
|
||||
|
@ -123,22 +147,64 @@ RoundingMode negate_rounding_mode(RoundingMode rounding_mode)
|
|||
return rounding_mode;
|
||||
}
|
||||
|
||||
// 13.9 GetTemporalOffsetOption ( options, fallback ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporaloffsetoption
|
||||
ThrowCompletionOr<OffsetOption> get_temporal_offset_option(VM& vm, Object const& options, OffsetOption fallback)
|
||||
{
|
||||
auto string_fallback = [&]() {
|
||||
switch (fallback) {
|
||||
// 1. If fallback is PREFER, let stringFallback be "prefer".
|
||||
case OffsetOption::Prefer:
|
||||
return "prefer"sv;
|
||||
// 2. Else if fallback is USE, let stringFallback be "use".
|
||||
case OffsetOption::Use:
|
||||
return "use"sv;
|
||||
// 3. Else if fallback is IGNORE, let stringFallback be "ignore".
|
||||
case OffsetOption::Ignore:
|
||||
return "ignore"sv;
|
||||
// 4. Else, let stringFallback be "reject".
|
||||
case OffsetOption::Reject:
|
||||
return "reject"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}();
|
||||
|
||||
// 5. Let stringValue be ? GetOption(options, "offset", STRING, « "prefer", "use", "ignore", "reject" », stringFallback).
|
||||
auto string_value = TRY(get_option(vm, options, vm.names.offset, OptionType::String, { "prefer"sv, "use"sv, "ignore"sv, "reject"sv }, string_fallback));
|
||||
auto string_view = string_value.as_string().utf8_string_view();
|
||||
|
||||
// 6. If stringValue is "prefer", return PREFER.
|
||||
if (string_view == "prefer"sv)
|
||||
return OffsetOption::Prefer;
|
||||
|
||||
// 7. If stringValue is "use", return USE.
|
||||
if (string_view == "use"sv)
|
||||
return OffsetOption::Use;
|
||||
|
||||
// 8. If stringValue is "ignore", return IGNORE.
|
||||
if (string_view == "ignore"sv)
|
||||
return OffsetOption::Ignore;
|
||||
|
||||
// 9. Return REJECT.
|
||||
return OffsetOption::Reject;
|
||||
}
|
||||
|
||||
// 13.10 GetTemporalShowCalendarNameOption ( options ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalshowcalendarnameoption
|
||||
ThrowCompletionOr<ShowCalendar> get_temporal_show_calendar_name_option(VM& vm, Object const& options)
|
||||
{
|
||||
// 1. Let stringValue be ? GetOption(options, "calendarName", STRING, « "auto", "always", "never", "critical" », "auto").
|
||||
auto string_value = TRY(get_option(vm, options, vm.names.calendarName, OptionType::String, { "auto"sv, "always"sv, "never"sv, "critical"sv }, "auto"sv));
|
||||
auto string_view = string_value.as_string().utf8_string_view();
|
||||
|
||||
// 2. If stringValue is "always", return ALWAYS.
|
||||
if (string_value.as_string().utf8_string_view() == "always"sv)
|
||||
if (string_view == "always"sv)
|
||||
return ShowCalendar::Always;
|
||||
|
||||
// 3. If stringValue is "never", return NEVER.
|
||||
if (string_value.as_string().utf8_string_view() == "never"sv)
|
||||
if (string_view == "never"sv)
|
||||
return ShowCalendar::Never;
|
||||
|
||||
// 4. If stringValue is "critical", return CRITICAL.
|
||||
if (string_value.as_string().utf8_string_view() == "critical"sv)
|
||||
if (string_view == "critical"sv)
|
||||
return ShowCalendar::Critical;
|
||||
|
||||
// 5. Return AUTO.
|
||||
|
@ -630,7 +696,6 @@ ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
|
|||
// 2. If value has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]],
|
||||
// [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal
|
||||
// slot, return false.
|
||||
// FIXME: Add the other types as we define them.
|
||||
if (is<PlainDate>(object))
|
||||
return false;
|
||||
if (is<PlainDateTime>(object))
|
||||
|
@ -641,6 +706,8 @@ ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
|
|||
return false;
|
||||
if (is<PlainYearMonth>(object))
|
||||
return false;
|
||||
if (is<ZonedDateTime>(object))
|
||||
return false;
|
||||
|
||||
// 3. Let calendarProperty be ? Get(value, "calendar").
|
||||
auto calendar_property = TRY(object.get(vm.names.calendar));
|
||||
|
|
|
@ -33,11 +33,25 @@ enum class DateType {
|
|||
YearMonth,
|
||||
};
|
||||
|
||||
enum class Disambiguation {
|
||||
Compatible,
|
||||
Earlier,
|
||||
Later,
|
||||
Reject,
|
||||
};
|
||||
|
||||
enum class DurationOperation {
|
||||
Since,
|
||||
Until,
|
||||
};
|
||||
|
||||
enum class OffsetOption {
|
||||
Prefer,
|
||||
Use,
|
||||
Ignore,
|
||||
Reject,
|
||||
};
|
||||
|
||||
enum class Overflow {
|
||||
Constrain,
|
||||
Reject,
|
||||
|
@ -144,7 +158,9 @@ double iso_date_to_epoch_days(double year, double month, double date);
|
|||
double epoch_days_to_epoch_ms(double day, double time);
|
||||
ThrowCompletionOr<void> check_iso_days_range(VM&, ISODate const&);
|
||||
ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM&, Object const& options);
|
||||
ThrowCompletionOr<Disambiguation> get_temporal_disambiguation_option(VM&, Object const& options);
|
||||
RoundingMode negate_rounding_mode(RoundingMode);
|
||||
ThrowCompletionOr<OffsetOption> get_temporal_offset_option(VM&, Object const& options, OffsetOption fallback);
|
||||
ThrowCompletionOr<ShowCalendar> get_temporal_show_calendar_name_option(VM&, Object const& options);
|
||||
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);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibUnicode/Locale.h>
|
||||
#include <LibUnicode/UnicodeKeywords.h>
|
||||
|
@ -452,7 +453,6 @@ ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal
|
|||
// [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]]
|
||||
// internal slot, then
|
||||
// i. Return temporalCalendarLike.[[Calendar]].
|
||||
// FIXME: Add the other calendar-holding types as we define them.
|
||||
if (is<PlainDate>(temporal_calendar_object))
|
||||
return static_cast<PlainDate const&>(temporal_calendar_object).calendar();
|
||||
if (is<PlainDateTime>(temporal_calendar_object))
|
||||
|
@ -461,6 +461,8 @@ ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal
|
|||
return static_cast<PlainMonthDay const&>(temporal_calendar_object).calendar();
|
||||
if (is<PlainYearMonth>(temporal_calendar_object))
|
||||
return static_cast<PlainYearMonth const&>(temporal_calendar_object).calendar();
|
||||
if (is<ZonedDateTime>(temporal_calendar_object))
|
||||
return static_cast<ZonedDateTime const&>(temporal_calendar_object).calendar();
|
||||
}
|
||||
|
||||
// 2. If temporalCalendarLike is not a String, throw a TypeError exception.
|
||||
|
@ -480,7 +482,6 @@ ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&
|
|||
// 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]],
|
||||
// [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
// a. Return item.[[Calendar]].
|
||||
// FIXME: Add the other calendar-holding types as we define them.
|
||||
if (is<PlainDate>(item))
|
||||
return static_cast<PlainDate const&>(item).calendar();
|
||||
if (is<PlainDateTime>(item))
|
||||
|
@ -489,6 +490,8 @@ ThrowCompletionOr<String> get_temporal_calendar_identifier_with_iso_default(VM&
|
|||
return static_cast<PlainMonthDay const&>(item).calendar();
|
||||
if (is<PlainYearMonth>(item))
|
||||
return static_cast<PlainYearMonth const&>(item).calendar();
|
||||
if (is<ZonedDateTime>(item))
|
||||
return static_cast<PlainYearMonth const&>(item).calendar();
|
||||
|
||||
// 2. Let calendarLike be ? Get(item, "calendar").
|
||||
auto calendar_like = TRY(item.get(vm.names.calendar));
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
|
@ -44,6 +45,7 @@ void Temporal::initialize(Realm& realm)
|
|||
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.ZonedDateTime, attr, [](auto& realm) -> Value { return realm.intrinsics().temporal_zoned_date_time_constructor(); });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <LibJS/Runtime/Temporal/PlainDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
@ -96,6 +97,39 @@ String format_offset_time_zone_identifier(i64 offset_minutes, Optional<TimeStyle
|
|||
return MUST(String::formatted("{}{}", sign, time_string));
|
||||
}
|
||||
|
||||
// 11.1.6 FormatUTCOffsetNanoseconds ( offsetNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-formatutcoffsetnanoseconds
|
||||
String format_utc_offset_nanoseconds(i64 offset_nanoseconds)
|
||||
{
|
||||
// 1. If offsetNanoseconds ≥ 0, let sign be the code unit 0x002B (PLUS SIGN); otherwise, let sign be the code unit 0x002D (HYPHEN-MINUS).
|
||||
auto sign = offset_nanoseconds >= 0 ? '+' : '-';
|
||||
|
||||
// 2. Let absoluteNanoseconds be abs(offsetNanoseconds).
|
||||
auto absolute_nanoseconds = static_cast<double>(abs(offset_nanoseconds));
|
||||
|
||||
// 3. Let hour be floor(absoluteNanoseconds / (3600 × 10**9)).
|
||||
auto hour = floor(absolute_nanoseconds / 3'600'000'000'000.0);
|
||||
|
||||
// 4. Let minute be floor(absoluteNanoseconds / (60 × 10**9)) modulo 60.
|
||||
auto minute = modulo(floor(absolute_nanoseconds / 60'000'000'000.0), 60.0);
|
||||
|
||||
// 5. Let second be floor(absoluteNanoseconds / 10**9) modulo 60.
|
||||
auto second = modulo(floor(absolute_nanoseconds / 1'000'000'000.0), 60.0);
|
||||
|
||||
// 6. Let subSecondNanoseconds be absoluteNanoseconds modulo 10**9.
|
||||
auto sub_second_nanoseconds = modulo(absolute_nanoseconds, 1'000'000'000.0);
|
||||
|
||||
// 7. If second = 0 and subSecondNanoseconds = 0, let precision be MINUTE; otherwise, let precision be AUTO.
|
||||
SecondsStringPrecision::Precision precision { Auto {} };
|
||||
if (second == 0 && sub_second_nanoseconds == 0)
|
||||
precision = SecondsStringPrecision::Minute {};
|
||||
|
||||
// 8. Let timeString be FormatTimeString(hour, minute, second, subSecondNanoseconds, precision).
|
||||
auto time_string = format_time_string(hour, minute, second, sub_second_nanoseconds, precision);
|
||||
|
||||
// 9. Return the string-concatenation of sign and timeString.
|
||||
return MUST(String::formatted("{}{}", sign, time_string));
|
||||
}
|
||||
|
||||
// 11.1.7 FormatDateTimeUTCOffsetRounded ( offsetNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-formatdatetimeutcoffsetrounded
|
||||
String format_date_time_utc_offset_rounded(i64 offset_nanoseconds)
|
||||
{
|
||||
|
@ -117,8 +151,13 @@ ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM& vm, Value tempora
|
|||
{
|
||||
// 1. If temporalTimeZoneLike is an Object, then
|
||||
if (temporal_time_zone_like.is_object()) {
|
||||
// FIXME: a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
// FIXME: i. Return temporalTimeZoneLike.[[TimeZone]].
|
||||
auto const& object = temporal_time_zone_like.as_object();
|
||||
|
||||
// a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (is<ZonedDateTime>(object)) {
|
||||
// i. Return temporalTimeZoneLike.[[TimeZone]].
|
||||
return static_cast<ZonedDateTime const&>(object).time_zone();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. If temporalTimeZoneLike is not a String, throw a TypeError exception.
|
||||
|
@ -297,6 +336,23 @@ ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanosecon
|
|||
return possible_epoch_nanoseconds;
|
||||
}
|
||||
|
||||
// 11.1.14 GetStartOfDay ( timeZone, isoDate ), https://tc39.es/proposal-temporal/#sec-temporal-getstartofday
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> get_start_of_day(VM& vm, StringView time_zone, ISODate iso_date)
|
||||
{
|
||||
// 1. Let isoDateTime be CombineISODateAndTimeRecord(isoDate, MidnightTimeRecord()).
|
||||
auto iso_date_time = combine_iso_date_and_time_record(iso_date, midnight_time_record());
|
||||
|
||||
// 2. Let possibleEpochNs be ? GetPossibleEpochNanoseconds(timeZone, isoDateTime).
|
||||
auto possible_epoch_nanoseconds = TRY(get_possible_epoch_nanoseconds(vm, time_zone, iso_date_time));
|
||||
|
||||
// 3. If possibleEpochNs is not empty, return possibleEpochNs[0].
|
||||
if (!possible_epoch_nanoseconds.is_empty())
|
||||
return move(possible_epoch_nanoseconds[0]);
|
||||
|
||||
// FIXME: GetNamedTimeZoneEpochNanoseconds currently does not produce zero instants.
|
||||
TODO();
|
||||
}
|
||||
|
||||
// 11.1.16 ParseTimeZoneIdentifier ( identifier ), https://tc39.es/proposal-temporal/#sec-parsetimezoneidentifier
|
||||
ThrowCompletionOr<TimeZone> parse_time_zone_identifier(VM& vm, StringView identifier)
|
||||
{
|
||||
|
|
|
@ -21,15 +21,9 @@ struct TimeZone {
|
|||
Optional<i64> offset_minutes;
|
||||
};
|
||||
|
||||
enum class Disambiguation {
|
||||
Compatible,
|
||||
Earlier,
|
||||
Later,
|
||||
Reject,
|
||||
};
|
||||
|
||||
ISODateTime get_iso_parts_from_epoch(Crypto::SignedBigInteger const& epoch_nanoseconds);
|
||||
String format_offset_time_zone_identifier(i64 offset_minutes, Optional<TimeStyle> = {});
|
||||
String format_utc_offset_nanoseconds(i64 offset_nanoseconds);
|
||||
String format_date_time_utc_offset_rounded(i64 offset_nanoseconds);
|
||||
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, Value temporal_time_zone_like);
|
||||
i64 get_offset_nanoseconds_for(StringView time_zone, Crypto::SignedBigInteger const& epoch_nanoseconds);
|
||||
|
@ -38,6 +32,7 @@ ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, StringView tempo
|
|||
ThrowCompletionOr<Crypto::SignedBigInteger> get_epoch_nanoseconds_for(VM&, StringView time_zone, ISODateTime const&, Disambiguation);
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> disambiguate_possible_epoch_nanoseconds(VM&, Vector<Crypto::SignedBigInteger> possible_epoch_ns, StringView time_zone, ISODateTime const&, Disambiguation);
|
||||
ThrowCompletionOr<Vector<Crypto::SignedBigInteger>> get_possible_epoch_nanoseconds(VM&, StringView time_zone, ISODateTime const&);
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> get_start_of_day(VM&, StringView time_zone, ISODate);
|
||||
ThrowCompletionOr<TimeZone> parse_time_zone_identifier(VM&, StringView identifier);
|
||||
TimeZone parse_time_zone_identifier(StringView identifier);
|
||||
TimeZone parse_time_zone_identifier(ParseResult const&);
|
||||
|
|
320
Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp
Normal file
320
Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp
Normal file
|
@ -0,0 +1,320 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021-2023, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Date.h>
|
||||
#include <LibJS/Runtime/Temporal/Calendar.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& epoch_nanoseconds, String time_zone, String calendar, Object& prototype)
|
||||
: Object(ConstructWithPrototypeTag::Tag, prototype)
|
||||
, m_epoch_nanoseconds(epoch_nanoseconds)
|
||||
, m_time_zone(move(time_zone))
|
||||
, m_calendar(move(calendar))
|
||||
{
|
||||
}
|
||||
|
||||
void ZonedDateTime::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_epoch_nanoseconds);
|
||||
}
|
||||
|
||||
// 6.5.1 InterpretISODateTimeOffset ( isoDate, time, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour ), https://tc39.es/proposal-temporal/#sec-temporal-interpretisodatetimeoffset
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> interpret_iso_date_time_offset(VM& vm, ISODate iso_date, Variant<ParsedISODateTime::StartOfDay, Time> const& time_or_start_of_day, OffsetBehavior offset_behavior, double offset_nanoseconds, StringView time_zone, Disambiguation disambiguation, OffsetOption offset_option, MatchBehavior match_behavior)
|
||||
{
|
||||
// 1. If time is START-OF-DAY, then
|
||||
if (time_or_start_of_day.has<ParsedISODateTime::StartOfDay>()) {
|
||||
// a. Assert: offsetBehaviour is WALL.
|
||||
VERIFY(offset_behavior == OffsetBehavior::Wall);
|
||||
|
||||
// b. Assert: offsetNanoseconds is 0.
|
||||
VERIFY(offset_nanoseconds == 0);
|
||||
|
||||
// c. Return ? GetStartOfDay(timeZone, isoDate).
|
||||
return TRY(get_start_of_day(vm, time_zone, iso_date));
|
||||
}
|
||||
|
||||
auto time = time_or_start_of_day.get<Time>();
|
||||
|
||||
// 2. Let isoDateTime be CombineISODateAndTimeRecord(isoDate, time).
|
||||
auto iso_date_time = combine_iso_date_and_time_record(iso_date, time);
|
||||
|
||||
// 3. If offsetBehaviour is WALL, or offsetBehaviour is OPTION and offsetOption is IGNORE, then
|
||||
if (offset_behavior == OffsetBehavior::Wall || (offset_behavior == OffsetBehavior::Option && offset_option == OffsetOption::Ignore)) {
|
||||
// a. Return ? GetEpochNanosecondsFor(timeZone, isoDateTime, disambiguation).
|
||||
return TRY(get_epoch_nanoseconds_for(vm, time_zone, iso_date_time, disambiguation));
|
||||
}
|
||||
|
||||
// 4. If offsetBehaviour is EXACT, or offsetBehaviour is OPTION and offsetOption is USE, then
|
||||
if (offset_behavior == OffsetBehavior::Exact || (offset_behavior == OffsetBehavior::Option && offset_option == OffsetOption::Use)) {
|
||||
// a. Let balanced be BalanceISODateTime(isoDate.[[Year]], isoDate.[[Month]], isoDate.[[Day]], time.[[Hour]], time.[[Minute]], time.[[Second]], time.[[Millisecond]], time.[[Microsecond]], time.[[Nanosecond]] - offsetNanoseconds).
|
||||
auto balanced = balance_iso_date_time(iso_date.year, iso_date.month, iso_date.day, time.hour, time.minute, time.second, time.millisecond, time.microsecond, static_cast<double>(time.nanosecond) - offset_nanoseconds);
|
||||
|
||||
// b. Perform ? CheckISODaysRange(balanced.[[ISODate]]).
|
||||
TRY(check_iso_days_range(vm, balanced.iso_date));
|
||||
|
||||
// c. Let epochNanoseconds be GetUTCEpochNanoseconds(balanced).
|
||||
auto epoch_nanoseconds = get_utc_epoch_nanoseconds(balanced);
|
||||
|
||||
// d. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// e. Return epochNanoseconds.
|
||||
return epoch_nanoseconds;
|
||||
}
|
||||
|
||||
// 5. Assert: offsetBehaviour is OPTION.
|
||||
VERIFY(offset_behavior == OffsetBehavior::Option);
|
||||
|
||||
// 6. Assert: offsetOption is PREFER or REJECT.
|
||||
VERIFY(offset_option == OffsetOption::Prefer || offset_option == OffsetOption::Reject);
|
||||
|
||||
// 7. Perform ? CheckISODaysRange(isoDate).
|
||||
TRY(check_iso_days_range(vm, iso_date));
|
||||
|
||||
// 8. Let utcEpochNanoseconds be GetUTCEpochNanoseconds(isoDateTime).
|
||||
auto utc_epoch_nanoseconds = get_utc_epoch_nanoseconds(iso_date_time);
|
||||
|
||||
// 9. Let possibleEpochNs be ? GetPossibleEpochNanoseconds(timeZone, isoDateTime).
|
||||
auto possible_epoch_nanoseconds = TRY(get_possible_epoch_nanoseconds(vm, time_zone, iso_date_time));
|
||||
|
||||
// 10. For each element candidate of possibleEpochNs, do
|
||||
for (auto& candidate : possible_epoch_nanoseconds) {
|
||||
// a. Let candidateOffset be utcEpochNanoseconds - candidate.
|
||||
auto candidate_offset = utc_epoch_nanoseconds.minus(candidate);
|
||||
|
||||
// b. If candidateOffset = offsetNanoseconds, then
|
||||
if (candidate_offset.compare_to_double(offset_nanoseconds) == Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt) {
|
||||
// i. Return candidate.
|
||||
return move(candidate);
|
||||
}
|
||||
|
||||
// c. If matchBehaviour is MATCH-MINUTES, then
|
||||
if (match_behavior == MatchBehavior::MatchMinutes) {
|
||||
// i. Let roundedCandidateNanoseconds be RoundNumberToIncrement(candidateOffset, 60 × 10**9, HALF-EXPAND).
|
||||
auto rounded_candidate_nanoseconds = round_number_to_increment(candidate_offset, NANOSECONDS_PER_MINUTE, RoundingMode::HalfExpand);
|
||||
|
||||
// ii. If roundedCandidateNanoseconds = offsetNanoseconds, then
|
||||
if (candidate_offset.compare_to_double(offset_nanoseconds) == Crypto::UnsignedBigInteger::CompareResult::DoubleEqualsBigInt) {
|
||||
// 1. Return candidate.
|
||||
return move(candidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 11. If offsetOption is reject, throw a RangeError exception.
|
||||
if (offset_option == OffsetOption::Reject)
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidZonedDateTimeOffset);
|
||||
|
||||
// 12. Return ? DisambiguatePossibleEpochNanoseconds(possibleEpochNs, timeZone, isoDateTime, disambiguation).
|
||||
return TRY(disambiguate_possible_epoch_nanoseconds(vm, move(possible_epoch_nanoseconds), time_zone, iso_date_time, disambiguation));
|
||||
}
|
||||
|
||||
// 6.5.2 ToTemporalZonedDateTime ( item [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal-totemporalzoneddatetime
|
||||
ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM& vm, Value item, Value options)
|
||||
{
|
||||
// 1. If options is not present, set options to undefined.
|
||||
|
||||
// 2. Let offsetBehaviour be OPTION.
|
||||
auto offset_behavior = OffsetBehavior::Option;
|
||||
|
||||
// 3. Let matchBehaviour be MATCH-EXACTLY.
|
||||
auto match_behavior = MatchBehavior::MatchExactly;
|
||||
|
||||
String calendar;
|
||||
String time_zone;
|
||||
Optional<String> offset_string;
|
||||
|
||||
Disambiguation disambiguation;
|
||||
OffsetOption offset_option;
|
||||
|
||||
ISODate iso_date;
|
||||
Variant<ParsedISODateTime::StartOfDay, Time> time { Time {} };
|
||||
|
||||
// 4. If item is an Object, then
|
||||
if (item.is_object()) {
|
||||
auto const& object = item.as_object();
|
||||
|
||||
// a. If item has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||
if (is<ZonedDateTime>(object)) {
|
||||
auto const& zoned_date_time = static_cast<ZonedDateTime const&>(object);
|
||||
|
||||
// i. NOTE: The following steps, and similar ones below, read options and perform independent validation in
|
||||
// alphabetical order (GetTemporalDisambiguationOption reads "disambiguation", GetTemporalOffsetOption
|
||||
// reads "offset", and GetTemporalOverflowOption reads "overflow").
|
||||
|
||||
// ii. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, options));
|
||||
|
||||
// iii. Perform ? GetTemporalDisambiguationOption(resolvedOptions).
|
||||
TRY(get_temporal_disambiguation_option(vm, resolved_options));
|
||||
|
||||
// iv. Perform ? GetTemporalOffsetOption(resolvedOptions, REJECT).
|
||||
TRY(get_temporal_offset_option(vm, resolved_options, OffsetOption::Reject));
|
||||
|
||||
// v. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||||
|
||||
// vi. Return ! CreateTemporalZonedDateTime(item.[[EpochNanoseconds]], item.[[TimeZone]], item.[[Calendar]]).
|
||||
return MUST(create_temporal_zoned_date_time(vm, zoned_date_time.epoch_nanoseconds(), zoned_date_time.time_zone(), zoned_date_time.calendar()));
|
||||
}
|
||||
|
||||
// b. Let calendar be ? GetTemporalCalendarIdentifierWithISODefault(item).
|
||||
calendar = TRY(get_temporal_calendar_identifier_with_iso_default(vm, object));
|
||||
|
||||
// c. Let fields be ? PrepareCalendarFields(calendar, item, « YEAR, MONTH, MONTH-CODE, DAY », « HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND, OFFSET, TIME-ZONE », « TIME-ZONE »).
|
||||
static constexpr auto calendar_field_names = to_array({ CalendarField::Year, CalendarField::Month, CalendarField::MonthCode, CalendarField::Day });
|
||||
static constexpr auto non_calendar_field_names = to_array({ CalendarField::Hour, CalendarField::Minute, CalendarField::Second, CalendarField::Millisecond, CalendarField::Microsecond, CalendarField::Nanosecond, CalendarField::Offset, CalendarField::TimeZone });
|
||||
static constexpr auto required_field_names = to_array({ CalendarField::TimeZone });
|
||||
auto fields = TRY(prepare_calendar_fields(vm, calendar, object, calendar_field_names, non_calendar_field_names, required_field_names.span()));
|
||||
|
||||
// d. Let timeZone be fields.[[TimeZone]].
|
||||
time_zone = fields.time_zone.release_value();
|
||||
|
||||
// e. Let offsetString be fields.[[OffsetString]].
|
||||
offset_string = move(fields.offset);
|
||||
|
||||
// f. If offsetString is UNSET, then
|
||||
if (!offset_string.has_value()) {
|
||||
// i. Set offsetBehaviour to WALL.
|
||||
offset_behavior = OffsetBehavior::Wall;
|
||||
}
|
||||
|
||||
// g. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, options));
|
||||
|
||||
// h. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
|
||||
disambiguation = TRY(get_temporal_disambiguation_option(vm, resolved_options));
|
||||
|
||||
// i. Let offsetOption be ? GetTemporalOffsetOption(resolvedOptions, REJECT).
|
||||
offset_option = TRY(get_temporal_offset_option(vm, resolved_options, OffsetOption::Reject));
|
||||
|
||||
// j. Let overflow be ? GetTemporalOverflowOption(resolvedOptions).
|
||||
auto overflow = TRY(get_temporal_overflow_option(vm, resolved_options));
|
||||
|
||||
// k. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, overflow).
|
||||
auto result = TRY(interpret_temporal_date_time_fields(vm, calendar, fields, overflow));
|
||||
|
||||
// l. Let isoDate be result.[[ISODate]].
|
||||
iso_date = result.iso_date;
|
||||
|
||||
// m. Let time be result.[[Time]].
|
||||
time = result.time;
|
||||
}
|
||||
// 5. Else,
|
||||
else {
|
||||
// a. If item is not a String, throw a TypeError exception.
|
||||
if (!item.is_string())
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidZonedDateTimeString, item);
|
||||
|
||||
// b. Let result be ? ParseISODateTime(item, « TemporalDateTimeString[+Zoned] »).
|
||||
auto result = TRY(parse_iso_date_time(vm, item.as_string().utf8_string_view(), { { Production::TemporalZonedDateTimeString } }));
|
||||
|
||||
// c. Let annotation be result.[[TimeZone]].[[TimeZoneAnnotation]].
|
||||
auto annotation = move(result.time_zone.time_zone_annotation);
|
||||
|
||||
// d. Assert: annotation is not empty.
|
||||
VERIFY(annotation.has_value());
|
||||
|
||||
// e. Let timeZone be ? ToTemporalTimeZoneIdentifier(annotation).
|
||||
time_zone = TRY(to_temporal_time_zone_identifier(vm, *annotation));
|
||||
|
||||
// f. Let offsetString be result.[[TimeZone]].[[OffsetString]].
|
||||
offset_string = move(result.time_zone.offset_string);
|
||||
|
||||
// g. If result.[[TimeZone]].[[Z]] is true, then
|
||||
if (result.time_zone.z_designator) {
|
||||
// i. Set offsetBehaviour to EXACT.
|
||||
offset_behavior = OffsetBehavior::Exact;
|
||||
}
|
||||
// h. Else if offsetString is EMPTY, then
|
||||
else if (!offset_string.has_value()) {
|
||||
// i. Set offsetBehaviour to WALL.
|
||||
offset_behavior = OffsetBehavior::Wall;
|
||||
}
|
||||
|
||||
// i. Let calendar be result.[[Calendar]].
|
||||
// j. If calendar is empty, set calendar to "iso8601".
|
||||
calendar = result.calendar.value_or("iso8601"_string);
|
||||
|
||||
// k. Set calendar to ? CanonicalizeCalendar(calendar).
|
||||
calendar = TRY(canonicalize_calendar(vm, calendar));
|
||||
|
||||
// l. Set matchBehaviour to MATCH-MINUTES.
|
||||
match_behavior = MatchBehavior::MatchMinutes;
|
||||
|
||||
// m. Let resolvedOptions be ? GetOptionsObject(options).
|
||||
auto resolved_options = TRY(get_options_object(vm, options));
|
||||
|
||||
// n. Let disambiguation be ? GetTemporalDisambiguationOption(resolvedOptions).
|
||||
disambiguation = TRY(get_temporal_disambiguation_option(vm, resolved_options));
|
||||
|
||||
// o. Let offsetOption be ? GetTemporalOffsetOption(resolvedOptions, REJECT).
|
||||
offset_option = TRY(get_temporal_offset_option(vm, resolved_options, OffsetOption::Reject));
|
||||
|
||||
// p. Perform ? GetTemporalOverflowOption(resolvedOptions).
|
||||
TRY(get_temporal_overflow_option(vm, resolved_options));
|
||||
|
||||
// q. Let isoDate be CreateISODateRecord(result.[[Year]], result.[[Month]], result.[[Day]]).
|
||||
iso_date = create_iso_date_record(*result.year, result.month, result.day);
|
||||
|
||||
// r. Let time be result.[[Time]].
|
||||
time = result.time;
|
||||
}
|
||||
|
||||
// 6. Let offsetNanoseconds be 0.
|
||||
double offset_nanoseconds = 0;
|
||||
|
||||
// 7. If offsetBehaviour is OPTION, then
|
||||
if (offset_behavior == OffsetBehavior::Option) {
|
||||
// a. Set offsetNanoseconds to ! ParseDateTimeUTCOffset(offsetString).
|
||||
offset_nanoseconds = parse_date_time_utc_offset(*offset_string);
|
||||
}
|
||||
|
||||
// 8. Let epochNanoseconds be ? InterpretISODateTimeOffset(isoDate, time, offsetBehaviour, offsetNanoseconds, timeZone, disambiguation, offsetOption, matchBehaviour).
|
||||
auto epoch_nanoseconds = TRY(interpret_iso_date_time_offset(vm, iso_date, time, offset_behavior, offset_nanoseconds, time_zone, disambiguation, offset_option, match_behavior));
|
||||
|
||||
// 9. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar).
|
||||
return MUST(create_temporal_zoned_date_time(vm, BigInt::create(vm, move(epoch_nanoseconds)), move(time_zone), move(calendar)));
|
||||
}
|
||||
|
||||
// 6.5.3 CreateTemporalZonedDateTime ( epochNanoseconds, timeZone, calendar [ , newTarget ] ), https://tc39.es/proposal-temporal/#sec-temporal-createtemporalzoneddatetime
|
||||
ThrowCompletionOr<GC::Ref<ZonedDateTime>> create_temporal_zoned_date_time(VM& vm, BigInt const& epoch_nanoseconds, String time_zone, String calendar, GC::Ptr<FunctionObject> new_target)
|
||||
{
|
||||
auto& realm = *vm.current_realm();
|
||||
|
||||
// 1. Assert: IsValidEpochNanoseconds(epochNanoseconds) is true.
|
||||
VERIFY(is_valid_epoch_nanoseconds(epoch_nanoseconds.big_integer()));
|
||||
|
||||
// 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]], [[EpochNanoseconds]], [[TimeZone]], [[Calendar]] »).
|
||||
// 4. Set object.[[EpochNanoseconds]] 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_zoned_date_time_prototype, epoch_nanoseconds, move(time_zone), move(calendar)));
|
||||
|
||||
// 7. Return object.
|
||||
return object;
|
||||
}
|
||||
|
||||
}
|
54
Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h
Normal file
54
Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/BigInt.h>
|
||||
#include <LibJS/Runtime/Completion.h>
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
#include <LibJS/Runtime/Temporal/AbstractOperations.h>
|
||||
#include <LibJS/Runtime/Temporal/ISORecords.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class ZonedDateTime final : public Object {
|
||||
JS_OBJECT(ZonedDateTime, Object);
|
||||
GC_DECLARE_ALLOCATOR(ZonedDateTime);
|
||||
|
||||
public:
|
||||
virtual ~ZonedDateTime() override = default;
|
||||
|
||||
[[nodiscard]] GC::Ref<BigInt const> epoch_nanoseconds() const { return m_epoch_nanoseconds; }
|
||||
[[nodiscard]] String const& time_zone() const { return m_time_zone; }
|
||||
[[nodiscard]] String const& calendar() const { return m_calendar; }
|
||||
|
||||
private:
|
||||
ZonedDateTime(BigInt const& nanoseconds, String time_zone, String calendar, Object& prototype);
|
||||
|
||||
virtual void visit_edges(Visitor&) override;
|
||||
|
||||
GC::Ref<BigInt const> m_epoch_nanoseconds; // [[EpochNanoseconds]]
|
||||
String m_time_zone; // [[TimeZone]]
|
||||
String m_calendar; // [[Calendar]]
|
||||
};
|
||||
|
||||
enum class OffsetBehavior {
|
||||
Option,
|
||||
Exact,
|
||||
Wall,
|
||||
};
|
||||
|
||||
enum class MatchBehavior {
|
||||
MatchExactly,
|
||||
MatchMinutes,
|
||||
};
|
||||
|
||||
ThrowCompletionOr<Crypto::SignedBigInteger> interpret_iso_date_time_offset(VM&, ISODate, Variant<ParsedISODateTime::StartOfDay, Time> const&, OffsetBehavior, double offset_nanoseconds, StringView time_zone, Disambiguation, OffsetOption, MatchBehavior);
|
||||
ThrowCompletionOr<GC::Ref<ZonedDateTime>> to_temporal_zoned_date_time(VM&, Value item, Value options = js_undefined());
|
||||
ThrowCompletionOr<GC::Ref<ZonedDateTime>> create_temporal_zoned_date_time(VM&, BigInt const& epoch_nanoseconds, String time_zone, String calendar, GC::Ptr<FunctionObject> new_target = {});
|
||||
|
||||
}
|
130
Libraries/LibJS/Runtime/Temporal/ZonedDateTimeConstructor.cpp
Normal file
130
Libraries/LibJS/Runtime/Temporal/ZonedDateTimeConstructor.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||
#include <Libraries/LibJS/Runtime/Intl/AbstractOperations.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, timeZone [ , calendar ] ), 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, timeZone [ , calendar ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime
|
||||
ThrowCompletionOr<GC::Ref<Object>> ZonedDateTimeConstructor::construct(FunctionObject& new_target)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
||||
auto epoch_nanoseconds_value = vm.argument(0);
|
||||
auto time_zone_value = vm.argument(1);
|
||||
auto calendar_value = vm.argument(2);
|
||||
|
||||
// 2. Set epochNanoseconds to ? ToBigInt(epochNanoseconds).
|
||||
auto epoch_nanoseconds = TRY(epoch_nanoseconds_value.to_bigint(vm));
|
||||
|
||||
// 3. If IsValidEpochNanoseconds(epochNanoseconds) is false, throw a RangeError exception.
|
||||
if (!is_valid_epoch_nanoseconds(epoch_nanoseconds->big_integer()))
|
||||
return vm.throw_completion<RangeError>(ErrorType::TemporalInvalidEpochNanoseconds);
|
||||
|
||||
// 4. If timeZone is not a String, throw a TypeError exception.
|
||||
if (!time_zone_value.is_string())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAString, time_zone_value);
|
||||
|
||||
// 5. Let timeZoneParse be ? ParseTimeZoneIdentifier(timeZone).
|
||||
auto time_zone_parse = TRY(parse_time_zone_identifier(vm, time_zone_value.as_string().utf8_string_view()));
|
||||
|
||||
String time_zone;
|
||||
|
||||
// 6. If timeZoneParse.[[OffsetMinutes]] is EMPTY, then
|
||||
if (!time_zone_parse.offset_minutes.has_value()) {
|
||||
// a. Let identifierRecord be GetAvailableNamedTimeZoneIdentifier(timeZoneParse.[[Name]]).
|
||||
auto identifier_record = Intl::get_available_named_time_zone_identifier(*time_zone_parse.name);
|
||||
|
||||
// b. If identifierRecord is EMPTY, throw a RangeError exception.
|
||||
if (!identifier_record.has_value())
|
||||
return vm.throw_completion<TypeError>(ErrorType::TemporalInvalidTimeZoneName, *time_zone_parse.name);
|
||||
|
||||
// c. Set timeZone to identifierRecord.[[Identifier]].
|
||||
time_zone = identifier_record->identifier;
|
||||
}
|
||||
// 7. Else,
|
||||
else {
|
||||
// a. Set timeZone to FormatOffsetTimeZoneIdentifier(timeZoneParse.[[OffsetMinutes]]).
|
||||
time_zone = format_offset_time_zone_identifier(*time_zone_parse.offset_minutes);
|
||||
}
|
||||
|
||||
// 8. If calendar is undefined, set calendar to "iso8601".
|
||||
if (calendar_value.is_undefined())
|
||||
calendar_value = PrimitiveString::create(vm, "iso8601"_string);
|
||||
|
||||
// 9. If calendar is not a String, throw a TypeError exception.
|
||||
if (!calendar_value.is_string())
|
||||
return vm.throw_completion<TypeError>(ErrorType::NotAString, calendar_value);
|
||||
|
||||
// 10. Set calendar to ? CanonicalizeCalendar(calendar).
|
||||
auto calendar = TRY(canonicalize_calendar(vm, calendar_value.as_string().utf8_string_view()));
|
||||
|
||||
// 11. Return ? CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar, NewTarget).
|
||||
return TRY(create_temporal_zoned_date_time(vm, epoch_nanoseconds, move(time_zone), move(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. Return ? ToTemporalZonedDateTime(item, options).
|
||||
return TRY(to_temporal_zoned_date_time(vm, vm.argument(0), vm.argument(1)));
|
||||
}
|
||||
|
||||
// 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.[[EpochNanoseconds]], two.[[EpochNanoseconds]])).
|
||||
return compare_epoch_nanoseconds(one->epoch_nanoseconds()->big_integer(), two->epoch_nanoseconds()->big_integer());
|
||||
}
|
||||
|
||||
}
|
34
Libraries/LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h
Normal file
34
Libraries/LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
class 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);
|
||||
};
|
||||
|
||||
}
|
349
Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp
Normal file
349
Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp
Normal file
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/Temporal/Calendar.h>
|
||||
#include <LibJS/Runtime/Temporal/Duration.h>
|
||||
#include <LibJS/Runtime/Temporal/Instant.h>
|
||||
#include <LibJS/Runtime/Temporal/PlainDate.h>
|
||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||
#include <LibJS/Runtime/Temporal/ZonedDateTimePrototype.h>
|
||||
|
||||
namespace JS::Temporal {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(ZonedDateTimePrototype);
|
||||
|
||||
// 6.3 Properties of the Temporal.ZonedDateTime Prototype Object, https://tc39.es/proposal-temporal/#sec-properties-of-the-temporal-zoneddatetime-prototype-object
|
||||
ZonedDateTimePrototype::ZonedDateTimePrototype(Realm& realm)
|
||||
: PrototypeObject(realm.intrinsics().object_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void ZonedDateTimePrototype::initialize(Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
|
||||
auto& vm = this->vm();
|
||||
|
||||
// 6.3.2 Temporal.ZonedDateTime.prototype[ %Symbol.toStringTag% ], https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype-%symbol.tostringtag%
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, "Temporal.ZonedDateTime"_string), Attribute::Configurable);
|
||||
|
||||
define_native_accessor(realm, vm.names.calendarId, calendar_id_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.timeZoneId, time_zone_id_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.era, era_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.eraYear, era_year_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.year, year_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.month, month_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.monthCode, month_code_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.day, day_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.hour, hour_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.minute, minute_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.second, second_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.millisecond, millisecond_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.microsecond, microsecond_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.nanosecond, nanosecond_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.epochMilliseconds, epoch_milliseconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.epochNanoseconds, epoch_nanoseconds_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.hoursInDay, hours_in_day_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.offsetNanoseconds, offset_nanoseconds_getter, {}, Attribute::Configurable);
|
||||
define_native_accessor(realm, vm.names.offset, offset_getter, {}, Attribute::Configurable);
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_function(realm, vm.names.valueOf, value_of, 0, attr);
|
||||
}
|
||||
|
||||
// 6.3.3 get Temporal.ZonedDateTime.prototype.calendarId, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.calendarid
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::calendar_id_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return zonedDateTime.[[Calendar]].
|
||||
return PrimitiveString::create(vm, zoned_date_time->calendar());
|
||||
}
|
||||
|
||||
// 6.3.4 get Temporal.ZonedDateTime.prototype.timeZoneId, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.timezoneid
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::time_zone_id_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return zonedDateTime.[[TimeZone]].
|
||||
return PrimitiveString::create(vm, zoned_date_time->time_zone());
|
||||
}
|
||||
|
||||
// 6.3.5 get Temporal.ZonedDateTime.prototype.era, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.era
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::era_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 4. Return CalendarISOToDate(zonedDateTime.[[Calendar]], isoDateTime.[[ISODate]]).[[Era]].
|
||||
auto result = calendar_iso_to_date(zoned_date_time->calendar(), iso_date_time.iso_date).era;
|
||||
|
||||
// 5. If result is undefined, return undefined.
|
||||
if (!result.has_value())
|
||||
return js_undefined();
|
||||
|
||||
// 6. Return 𝔽(result).
|
||||
return PrimitiveString::create(vm, result.release_value());
|
||||
}
|
||||
|
||||
// 6.3.6 get Temporal.ZonedDateTime.prototype.eraYear, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.erayear
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::era_year_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 4. Let result be CalendarISOToDate(zonedDateTime.[[Calendar]], isoDateTime.[[ISODate]]).[[EraYear]].
|
||||
auto result = calendar_iso_to_date(zoned_date_time->calendar(), iso_date_time.iso_date).era_year;
|
||||
|
||||
// 5. If result is undefined, return undefined.
|
||||
if (!result.has_value())
|
||||
return js_undefined();
|
||||
|
||||
// 6. Return 𝔽(result).
|
||||
return *result;
|
||||
}
|
||||
|
||||
// 6.3.7 get Temporal.ZonedDateTime.prototype.year, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.year
|
||||
// 6.3.8 get Temporal.ZonedDateTime.prototype.month, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.month
|
||||
// 6.3.10 get Temporal.ZonedDateTime.prototype.day, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthcode
|
||||
// 6.3.19 get Temporal.ZonedDateTime.prototype.dayOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.dayofweek
|
||||
// 6.3.20 get Temporal.ZonedDateTime.prototype.dayOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.dayofyear
|
||||
// 6.3.24 get Temporal.ZonedDateTime.prototype.daysInWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinweek
|
||||
// 6.3.25 get Temporal.ZonedDateTime.prototype.daysInMonth, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinmonth
|
||||
// 6.3.26 get Temporal.ZonedDateTime.prototype.daysInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.daysinyear
|
||||
// 6.3.27 get Temporal.ZonedDateTime.prototype.monthsInYear, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthsinyear
|
||||
// 6.3.28 get Temporal.ZonedDateTime.prototype.inLeapYear, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.inleapyear
|
||||
#define JS_ENUMERATE_ZONED_DATE_TIME_SIMPLE_DATE_FIELDS \
|
||||
__JS_ENUMERATE(year) \
|
||||
__JS_ENUMERATE(month) \
|
||||
__JS_ENUMERATE(day) \
|
||||
__JS_ENUMERATE(day_of_week) \
|
||||
__JS_ENUMERATE(day_of_year) \
|
||||
__JS_ENUMERATE(days_in_week) \
|
||||
__JS_ENUMERATE(days_in_month) \
|
||||
__JS_ENUMERATE(days_in_year) \
|
||||
__JS_ENUMERATE(months_in_year) \
|
||||
__JS_ENUMERATE(in_leap_year)
|
||||
|
||||
#define __JS_ENUMERATE(field) \
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::field##_getter) \
|
||||
{ \
|
||||
/* 1. Let zonedDateTime be the this value. */ \
|
||||
/* 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]). */ \
|
||||
auto zoned_date_time = TRY(typed_this_object(vm)); \
|
||||
\
|
||||
/* Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]). */ \
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer()); \
|
||||
\
|
||||
/* 3. Return 𝔽(CalendarISOToDate(zonedDateTime.[[Calendar]], isoDateTime.[[ISODate]]).[[<field>]]). */ \
|
||||
return calendar_iso_to_date(zoned_date_time->calendar(), iso_date_time.iso_date).field; \
|
||||
}
|
||||
JS_ENUMERATE_ZONED_DATE_TIME_SIMPLE_DATE_FIELDS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
// 6.3.9 get Temporal.ZonedDateTime.prototype.monthCode, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.monthcode
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::month_code_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 4. Return CalendarISOToDate(zonedDateTime.[[Calendar]], isoDateTime.[[ISODate]]).[[MonthCode]].
|
||||
auto month_code = calendar_iso_to_date(zoned_date_time->calendar(), iso_date_time.iso_date).month_code;
|
||||
return PrimitiveString::create(vm, move(month_code));
|
||||
}
|
||||
|
||||
// 6.3.11 get Temporal.ZonedDateTime.prototype.hour, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.hour
|
||||
// 6.3.12 get Temporal.ZonedDateTime.prototype.minute, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.minute
|
||||
// 6.3.13 get Temporal.ZonedDateTime.prototype.second, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.second
|
||||
// 6.3.14 get Temporal.ZonedDateTime.prototype.millisecond, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.millisecond
|
||||
// 6.3.15 get Temporal.ZonedDateTime.prototype.microsecond, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.microsecond
|
||||
// 6.3.16 get Temporal.ZonedDateTime.prototype.nanosecond, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.nanosecond
|
||||
#define JS_ENUMERATE_PLAIN_DATE_TIME_TIME_FIELDS \
|
||||
__JS_ENUMERATE(hour) \
|
||||
__JS_ENUMERATE(minute) \
|
||||
__JS_ENUMERATE(second) \
|
||||
__JS_ENUMERATE(millisecond) \
|
||||
__JS_ENUMERATE(microsecond) \
|
||||
__JS_ENUMERATE(nanosecond)
|
||||
|
||||
#define __JS_ENUMERATE(field) \
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::field##_getter) \
|
||||
{ \
|
||||
/* 1. Let zonedDateTime be the this value. */ \
|
||||
/* 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]). */ \
|
||||
auto zoned_date_time = TRY(typed_this_object(vm)); \
|
||||
\
|
||||
/* Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]). */ \
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer()); \
|
||||
\
|
||||
/* 3. Return 𝔽(isoDateTime.[[Time]].[[<field>]]). */ \
|
||||
return iso_date_time.time.field; \
|
||||
}
|
||||
JS_ENUMERATE_PLAIN_DATE_TIME_TIME_FIELDS
|
||||
#undef __JS_ENUMERATE
|
||||
|
||||
// 6.3.17 get Temporal.ZonedDateTime.prototype.epochMilliseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.epochmilliseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::epoch_milliseconds_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let ns be zonedDateTime.[[EpochNanoseconds]].
|
||||
auto const& nanoseconds = zoned_date_time->epoch_nanoseconds()->big_integer();
|
||||
|
||||
// 4. Let ms be floor(ℝ(ns) / 10**6).
|
||||
auto milliseconds = big_floor(nanoseconds, NANOSECONDS_PER_MILLISECOND);
|
||||
|
||||
// 5. Return 𝔽(ms).
|
||||
return milliseconds.to_double();
|
||||
}
|
||||
|
||||
// 6.3.18 get Temporal.ZonedDateTime.prototype.epochNanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.epochnanoseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::epoch_nanoseconds_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return zonedDateTime.[[EpochNanoseconds]].
|
||||
return zoned_date_time->epoch_nanoseconds();
|
||||
}
|
||||
|
||||
// 6.3.21 get Temporal.ZonedDateTime.prototype.weekOfYear, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.weekofyear
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::week_of_year_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 4. Let result be CalendarISOToDate(zonedDateTime.[[Calendar]], isoDateTime.[[ISODate]]).[[WeekOfYear]].[[Week]].
|
||||
auto result = calendar_iso_to_date(zoned_date_time->calendar(), iso_date_time.iso_date).week_of_year.week;
|
||||
|
||||
// 5. If result is undefined, return undefined.
|
||||
if (!result.has_value())
|
||||
return js_undefined();
|
||||
|
||||
// 6. Return 𝔽(result).
|
||||
return *result;
|
||||
}
|
||||
|
||||
// 6.3.22 get Temporal.ZonedDateTime.prototype.yearOfWeek, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.yearofweek
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::year_of_week_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let isoDateTime be GetISODateTimeFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto iso_date_time = get_iso_date_time_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 4. Let result be CalendarISOToDate(zonedDateTime.[[Calendar]], isoDateTime.[[ISODate]]).[[WeekOfYear]].[[Year]].
|
||||
auto result = calendar_iso_to_date(zoned_date_time->calendar(), iso_date_time.iso_date).week_of_year.year;
|
||||
|
||||
// 5. If result is undefined, return undefined.
|
||||
if (!result.has_value())
|
||||
return js_undefined();
|
||||
|
||||
// 6. Return 𝔽(result).
|
||||
return *result;
|
||||
}
|
||||
|
||||
// 6.3.23 get Temporal.ZonedDateTime.prototype.hoursInDay, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.hoursinday
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::hours_in_day_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let timeZone be zonedDateTime.[[TimeZone]].
|
||||
auto const& time_zone = zoned_date_time->time_zone();
|
||||
|
||||
// 4. Let isoDateTime be GetISODateTimeFor(timeZone, zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto iso_date_time = get_iso_date_time_for(time_zone, zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 5. Let today be isoDateTime.[[ISODate]].
|
||||
auto today = iso_date_time.iso_date;
|
||||
|
||||
// 6. Let tomorrow be BalanceISODate(today.[[Year]], today.[[Month]], today.[[Day]] + 1).
|
||||
auto tomorrow = balance_iso_date(today.year, today.month, today.day + 1);
|
||||
|
||||
// 7. Let todayNs be ? GetStartOfDay(timeZone, today).
|
||||
auto today_nanoseconds = TRY(get_start_of_day(vm, time_zone, today));
|
||||
|
||||
// 8. Let tomorrowNs be ? GetStartOfDay(timeZone, tomorrow).
|
||||
auto tomorrow_nanoseconds = TRY(get_start_of_day(vm, time_zone, tomorrow));
|
||||
|
||||
// 9. Let diff be TimeDurationFromEpochNanosecondsDifference(tomorrowNs, todayNs).
|
||||
auto diff = time_duration_from_epoch_nanoseconds_difference(tomorrow_nanoseconds, today_nanoseconds);
|
||||
|
||||
// 10. Return 𝔽(TotalTimeDuration(diff, HOUR)).
|
||||
return total_time_duration(diff, Unit::Hour).to_double();
|
||||
}
|
||||
|
||||
// 6.3.29 get Temporal.ZonedDateTime.prototype.offsetNanoseconds, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offsetnanoseconds
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::offset_nanoseconds_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Return 𝔽(GetOffsetNanosecondsFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]])).
|
||||
return static_cast<double>(get_offset_nanoseconds_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer()));
|
||||
}
|
||||
|
||||
// 6.3.30 get Temporal.ZonedDateTime.prototype.offset, https://tc39.es/proposal-temporal/#sec-get-temporal.zoneddatetime.prototype.offset
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::offset_getter)
|
||||
{
|
||||
// 1. Let zonedDateTime be the this value.
|
||||
// 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]).
|
||||
auto zoned_date_time = TRY(typed_this_object(vm));
|
||||
|
||||
// 3. Let offsetNanoseconds be GetOffsetNanosecondsFor(zonedDateTime.[[TimeZone]], zonedDateTime.[[EpochNanoseconds]]).
|
||||
auto offset_nanoseconds = get_offset_nanoseconds_for(zoned_date_time->time_zone(), zoned_date_time->epoch_nanoseconds()->big_integer());
|
||||
|
||||
// 4. Return FormatUTCOffsetNanoseconds(offsetNanoseconds).
|
||||
return PrimitiveString::create(vm, format_utc_offset_nanoseconds(offset_nanoseconds));
|
||||
}
|
||||
|
||||
// 6.3.44 Temporal.ZonedDateTime.prototype.valueOf ( ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.valueof
|
||||
JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::value_of)
|
||||
{
|
||||
// 1. Throw a TypeError exception.
|
||||
return vm.throw_completion<TypeError>(ErrorType::Convert, "Temporal.ZonedDateTime", "a primitive value");
|
||||
}
|
||||
|
||||
}
|
57
Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.h
Normal file
57
Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2024, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/PrototypeObject.h>
|
||||
#include <LibJS/Runtime/Temporal/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_id_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(time_zone_id_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(era_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(era_year_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(year_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(month_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(month_code_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(day_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(hour_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(minute_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(second_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(millisecond_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(microsecond_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(nanosecond_getter);
|
||||
JS_DECLARE_NATIVE_FUNCTION(epoch_milliseconds_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(value_of);
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 2", () => {
|
||||
expect(Temporal.ZonedDateTime.compare).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const zonedDateTimeOne = new Temporal.ZonedDateTime(1n, "UTC");
|
||||
const zonedDateTimeTwo = new Temporal.ZonedDateTime(2n, "UTC");
|
||||
|
||||
expect(Temporal.ZonedDateTime.compare(zonedDateTimeOne, zonedDateTimeOne)).toBe(0);
|
||||
expect(Temporal.ZonedDateTime.compare(zonedDateTimeTwo, zonedDateTimeTwo)).toBe(0);
|
||||
expect(Temporal.ZonedDateTime.compare(zonedDateTimeOne, zonedDateTimeTwo)).toBe(-1);
|
||||
expect(Temporal.ZonedDateTime.compare(zonedDateTimeTwo, zonedDateTimeOne)).toBe(1);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,132 @@
|
|||
describe("correct behavior", () => {
|
||||
test("length is 1", () => {
|
||||
expect(Temporal.ZonedDateTime.from).toHaveLength(1);
|
||||
});
|
||||
|
||||
test("ZonedDateTime instance argument", () => {
|
||||
const timeZone = "UTC";
|
||||
const calendar = "gregory";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1627318123456789000n, timeZone, calendar);
|
||||
const createdZoneDateTime = Temporal.ZonedDateTime.from(zonedDateTime);
|
||||
|
||||
expect(createdZoneDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
|
||||
expect(createdZoneDateTime).not.toBe(zonedDateTime);
|
||||
expect(createdZoneDateTime.timeZoneId).toBe(timeZone);
|
||||
expect(createdZoneDateTime.calendarId).toBe(calendar);
|
||||
expect(createdZoneDateTime.epochNanoseconds).toBe(1627318123456789000n);
|
||||
});
|
||||
|
||||
test("PlainDate instance argument", () => {
|
||||
const timeZone = "UTC";
|
||||
const calendar = "gregory";
|
||||
const plainDate = new Temporal.PlainDate(2021, 11, 7, calendar);
|
||||
plainDate.timeZone = timeZone;
|
||||
const createdZoneDateTime = Temporal.ZonedDateTime.from(plainDate);
|
||||
|
||||
expect(createdZoneDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
|
||||
expect(createdZoneDateTime.timeZoneId).toBe(timeZone);
|
||||
expect(createdZoneDateTime.calendarId).toBe(calendar);
|
||||
expect(createdZoneDateTime.year).toBe(2021);
|
||||
expect(createdZoneDateTime.month).toBe(11);
|
||||
expect(createdZoneDateTime.day).toBe(7);
|
||||
});
|
||||
|
||||
test("PlainDateTime instance argument", () => {
|
||||
const timeZone = "UTC";
|
||||
const calendar = "gregory";
|
||||
const plainDateTime = new Temporal.PlainDateTime(
|
||||
2021,
|
||||
11,
|
||||
7,
|
||||
0,
|
||||
20,
|
||||
5,
|
||||
100,
|
||||
200,
|
||||
300,
|
||||
calendar
|
||||
);
|
||||
plainDateTime.timeZone = timeZone;
|
||||
const createdZoneDateTime = Temporal.ZonedDateTime.from(plainDateTime);
|
||||
|
||||
expect(createdZoneDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
|
||||
expect(createdZoneDateTime.timeZoneId).toBe(timeZone);
|
||||
expect(createdZoneDateTime.calendarId).toBe(calendar);
|
||||
expect(createdZoneDateTime.year).toBe(2021);
|
||||
expect(createdZoneDateTime.month).toBe(11);
|
||||
expect(createdZoneDateTime.day).toBe(7);
|
||||
expect(createdZoneDateTime.hour).toBe(0);
|
||||
expect(createdZoneDateTime.minute).toBe(20);
|
||||
expect(createdZoneDateTime.second).toBe(5);
|
||||
expect(createdZoneDateTime.millisecond).toBe(100);
|
||||
expect(createdZoneDateTime.microsecond).toBe(200);
|
||||
expect(createdZoneDateTime.nanosecond).toBe(300);
|
||||
});
|
||||
|
||||
test("ZonedDateTime-like argument", () => {
|
||||
const timeZone = "UTC";
|
||||
const calendar = "gregory";
|
||||
const zdtLike = {
|
||||
timeZone,
|
||||
calendar,
|
||||
year: 2021,
|
||||
month: 11,
|
||||
day: 7,
|
||||
hour: 0,
|
||||
minute: 20,
|
||||
second: 5,
|
||||
millisecond: 100,
|
||||
microsecond: 200,
|
||||
nanosecond: 300,
|
||||
};
|
||||
const createdZoneDateTime = Temporal.ZonedDateTime.from(zdtLike);
|
||||
|
||||
expect(createdZoneDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
|
||||
expect(createdZoneDateTime.timeZoneId).toBe(timeZone);
|
||||
expect(createdZoneDateTime.calendarId).toBe(calendar);
|
||||
expect(createdZoneDateTime.year).toBe(2021);
|
||||
expect(createdZoneDateTime.month).toBe(11);
|
||||
expect(createdZoneDateTime.day).toBe(7);
|
||||
expect(createdZoneDateTime.hour).toBe(0);
|
||||
expect(createdZoneDateTime.minute).toBe(20);
|
||||
expect(createdZoneDateTime.second).toBe(5);
|
||||
expect(createdZoneDateTime.millisecond).toBe(100);
|
||||
expect(createdZoneDateTime.microsecond).toBe(200);
|
||||
expect(createdZoneDateTime.nanosecond).toBe(300);
|
||||
});
|
||||
|
||||
test("from string", () => {
|
||||
const zonedDateTime = Temporal.ZonedDateTime.from(
|
||||
"2021-11-07T00:20:05.100200300+00:00[UTC][u-ca=iso8601]"
|
||||
);
|
||||
|
||||
expect(zonedDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
|
||||
expect(zonedDateTime.timeZoneId).toBe("UTC");
|
||||
expect(zonedDateTime.calendarId).toBe("iso8601");
|
||||
expect(zonedDateTime.year).toBe(2021);
|
||||
expect(zonedDateTime.month).toBe(11);
|
||||
expect(zonedDateTime.day).toBe(7);
|
||||
expect(zonedDateTime.hour).toBe(0);
|
||||
expect(zonedDateTime.minute).toBe(20);
|
||||
expect(zonedDateTime.second).toBe(5);
|
||||
expect(zonedDateTime.millisecond).toBe(100);
|
||||
expect(zonedDateTime.microsecond).toBe(200);
|
||||
expect(zonedDateTime.nanosecond).toBe(300);
|
||||
expect(zonedDateTime.offset).toBe("+00:00");
|
||||
expect(zonedDateTime.offsetNanoseconds).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("requires timeZone property", () => {
|
||||
expect(() => {
|
||||
Temporal.ZonedDateTime.from({});
|
||||
}).toThrowWithMessage(TypeError, "Required property timeZone is missing or undefined");
|
||||
});
|
||||
|
||||
test("invalid zoned date time string", () => {
|
||||
expect(() => {
|
||||
Temporal.ZonedDateTime.from("foo");
|
||||
}).toThrowWithMessage(RangeError, "Invalid ISO date time");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,39 @@
|
|||
describe("errors", () => {
|
||||
test("called without new", () => {
|
||||
expect(() => {
|
||||
Temporal.ZonedDateTime();
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
"Temporal.ZonedDateTime constructor must be called with 'new'"
|
||||
);
|
||||
});
|
||||
|
||||
test("out-of-range epoch nanoseconds value", () => {
|
||||
expect(() => {
|
||||
new Temporal.ZonedDateTime(8_640_000_000_000_000_000_001n);
|
||||
}).toThrowWithMessage(
|
||||
RangeError,
|
||||
"Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17"
|
||||
);
|
||||
expect(() => {
|
||||
new Temporal.ZonedDateTime(-8_640_000_000_000_000_000_001n);
|
||||
}).toThrowWithMessage(
|
||||
RangeError,
|
||||
"Invalid epoch nanoseconds value, must be in range -86400 * 10^17 to 86400 * 10^17"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("length is 2", () => {
|
||||
expect(Temporal.ZonedDateTime).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
expect(typeof zonedDateTime).toBe("object");
|
||||
expect(zonedDateTime).toBeInstanceOf(Temporal.ZonedDateTime);
|
||||
expect(Object.getPrototypeOf(zonedDateTime)).toBe(Temporal.ZonedDateTime.prototype);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
describe("correct behavior", () => {
|
||||
test("calendarId basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const calendar = "gregory";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone, calendar);
|
||||
expect(zonedDateTime.calendarId).toBe("gregory");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "calendarId", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.day).toBe(6);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "day", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.dayOfWeek).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "dayOfWeek", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.dayOfYear).toBe(187);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "dayOfYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.daysInMonth).toBe(31);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "daysInMonth", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.daysInWeek).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "daysInWeek", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.daysInYear).toBe(365);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "daysInYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.epochMilliseconds).toBe(1625614921000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "epochMilliseconds", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.epochNanoseconds).toBe(1625614921000000000n);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "epochNanoseconds", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.era).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "era", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.eraYear).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "eraYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.hour).toBe(23);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "hour", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.hoursInDay).toBe(24);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "hoursInDay", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.inLeapYear).toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "inLeapYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(123000n, timeZone);
|
||||
expect(zonedDateTime.microsecond).toBe(123);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "microsecond", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(123000000n, timeZone);
|
||||
expect(zonedDateTime.millisecond).toBe(123);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "millisecond", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.minute).toBe(42);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "minute", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.month).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "month", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.monthCode).toBe("M07");
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "monthCode", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.monthsInYear).toBe(12);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "monthsInYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(123n, timeZone);
|
||||
expect(zonedDateTime.nanosecond).toBe(123);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "nanosecond", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
expect(zonedDateTime.offset).toBe("+00:00");
|
||||
});
|
||||
|
||||
test("custom offset", () => {
|
||||
const timeZone = "+01:30";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
expect(zonedDateTime.offset).toBe(timeZone);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "offset", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
expect(zonedDateTime.offsetNanoseconds).toBe(0);
|
||||
});
|
||||
|
||||
test("custom offset", () => {
|
||||
const timeZone = "+01:30";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
expect(zonedDateTime.offsetNanoseconds).toBe(5400000000000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "offsetNanoseconds", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.second).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "second", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
expect(zonedDateTime.timeZoneId).toBe(timeZone);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "timeZoneId", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
describe("errors", () => {
|
||||
test("throws TypeError", () => {
|
||||
const timeZone = "UTC";
|
||||
expect(() => {
|
||||
new Temporal.ZonedDateTime(0n, timeZone).valueOf();
|
||||
}).toThrowWithMessage(
|
||||
TypeError,
|
||||
"Cannot convert Temporal.ZonedDateTime to a primitive value"
|
||||
);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.weekOfYear).toBe(27);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "weekOfYear", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1625614921000000000n, timeZone);
|
||||
expect(zonedDateTime.year).toBe(2021);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "year", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
describe("correct behavior", () => {
|
||||
test("basic functionality", () => {
|
||||
const timeZone = "UTC";
|
||||
const zonedDateTime = new Temporal.ZonedDateTime(1672531200000000000n, timeZone);
|
||||
expect(zonedDateTime.yearOfWeek).toBe(2022);
|
||||
});
|
||||
});
|
||||
|
||||
describe("errors", () => {
|
||||
test("this value must be a Temporal.ZonedDateTime object", () => {
|
||||
expect(() => {
|
||||
Reflect.get(Temporal.ZonedDateTime.prototype, "yearOfWeek", "foo");
|
||||
}).toThrowWithMessage(TypeError, "Not an object of type Temporal.ZonedDateTime");
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue