mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 18:50:50 +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/PlainYearMonthPrototype.cpp
|
||||||
Runtime/Temporal/Temporal.cpp
|
Runtime/Temporal/Temporal.cpp
|
||||||
Runtime/Temporal/TimeZone.cpp
|
Runtime/Temporal/TimeZone.cpp
|
||||||
|
Runtime/Temporal/ZonedDateTime.cpp
|
||||||
|
Runtime/Temporal/ZonedDateTimeConstructor.cpp
|
||||||
|
Runtime/Temporal/ZonedDateTimePrototype.cpp
|
||||||
Runtime/TypedArray.cpp
|
Runtime/TypedArray.cpp
|
||||||
Runtime/TypedArrayConstructor.cpp
|
Runtime/TypedArrayConstructor.cpp
|
||||||
Runtime/TypedArrayPrototype.cpp
|
Runtime/TypedArrayPrototype.cpp
|
||||||
|
|
|
@ -87,14 +87,15 @@
|
||||||
__JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \
|
__JS_ENUMERATE(RelativeTimeFormat, relative_time_format, RelativeTimeFormatPrototype, RelativeTimeFormatConstructor) \
|
||||||
__JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor)
|
__JS_ENUMERATE(Segmenter, segmenter, SegmenterPrototype, SegmenterConstructor)
|
||||||
|
|
||||||
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
#define JS_ENUMERATE_TEMPORAL_OBJECTS \
|
||||||
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
__JS_ENUMERATE(Duration, duration, DurationPrototype, DurationConstructor) \
|
||||||
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
|
__JS_ENUMERATE(Instant, instant, InstantPrototype, InstantConstructor) \
|
||||||
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
|
__JS_ENUMERATE(PlainDate, plain_date, PlainDatePrototype, PlainDateConstructor) \
|
||||||
__JS_ENUMERATE(PlainDateTime, plain_date_time, PlainDateTimePrototype, PlainDateTimeConstructor) \
|
__JS_ENUMERATE(PlainDateTime, plain_date_time, PlainDateTimePrototype, PlainDateTimeConstructor) \
|
||||||
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \
|
__JS_ENUMERATE(PlainMonthDay, plain_month_day, PlainMonthDayPrototype, PlainMonthDayConstructor) \
|
||||||
__JS_ENUMERATE(PlainTime, plain_time, PlainTimePrototype, PlainTimeConstructor) \
|
__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 \
|
#define JS_ENUMERATE_BUILTIN_NAMESPACE_OBJECTS \
|
||||||
__JS_ENUMERATE(AtomicsObject, atomics) \
|
__JS_ENUMERATE(AtomicsObject, atomics) \
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
#include <LibJS/Runtime/Temporal/PlainMonthDay.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
#include <LibJS/Runtime/TypedArray.h>
|
#include <LibJS/Runtime/TypedArray.h>
|
||||||
#include <LibJS/Runtime/Value.h>
|
#include <LibJS/Runtime/Value.h>
|
||||||
#include <LibJS/Runtime/WeakMap.h>
|
#include <LibJS/Runtime/WeakMap.h>
|
||||||
|
@ -893,6 +894,18 @@ ErrorOr<void> print_temporal_plain_year_month(JS::PrintContext& print_context, J
|
||||||
return {};
|
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)
|
ErrorOr<void> print_boolean_object(JS::PrintContext& print_context, JS::BooleanObject const& boolean_object, HashTable<JS::Object*>& seen_objects)
|
||||||
{
|
{
|
||||||
TRY(print_type(print_context, "Boolean"sv));
|
TRY(print_type(print_context, "Boolean"sv));
|
||||||
|
@ -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);
|
return print_temporal_plain_time(print_context, static_cast<JS::Temporal::PlainTime&>(object), seen_objects);
|
||||||
if (is<JS::Temporal::PlainYearMonth>(object))
|
if (is<JS::Temporal::PlainYearMonth>(object))
|
||||||
return print_temporal_plain_year_month(print_context, static_cast<JS::Temporal::PlainYearMonth&>(object), seen_objects);
|
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);
|
return print_object(print_context, object, seen_objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,8 @@
|
||||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
|
#include <LibJS/Runtime/Temporal/PlainYearMonthPrototype.h>
|
||||||
#include <LibJS/Runtime/Temporal/Temporal.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/TypedArray.h>
|
||||||
#include <LibJS/Runtime/TypedArrayConstructor.h>
|
#include <LibJS/Runtime/TypedArrayConstructor.h>
|
||||||
#include <LibJS/Runtime/TypedArrayPrototype.h>
|
#include <LibJS/Runtime/TypedArrayPrototype.h>
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
|
|
||||||
namespace JS::Temporal {
|
namespace JS::Temporal {
|
||||||
|
|
||||||
|
@ -100,6 +101,29 @@ ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM& vm, Object const& o
|
||||||
return Overflow::Reject;
|
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
|
// 13.8 NegateRoundingMode ( roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-negateroundingmode
|
||||||
RoundingMode negate_rounding_mode(RoundingMode rounding_mode)
|
RoundingMode negate_rounding_mode(RoundingMode rounding_mode)
|
||||||
{
|
{
|
||||||
|
@ -123,22 +147,64 @@ RoundingMode negate_rounding_mode(RoundingMode rounding_mode)
|
||||||
return 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
|
// 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)
|
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").
|
// 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_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.
|
// 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;
|
return ShowCalendar::Always;
|
||||||
|
|
||||||
// 3. If stringValue is "never", return NEVER.
|
// 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;
|
return ShowCalendar::Never;
|
||||||
|
|
||||||
// 4. If stringValue is "critical", return CRITICAL.
|
// 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;
|
return ShowCalendar::Critical;
|
||||||
|
|
||||||
// 5. Return AUTO.
|
// 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]],
|
// 2. If value has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]],
|
||||||
// [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal
|
// [[InitializedTemporalTime]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal
|
||||||
// slot, return false.
|
// slot, return false.
|
||||||
// FIXME: Add the other types as we define them.
|
|
||||||
if (is<PlainDate>(object))
|
if (is<PlainDate>(object))
|
||||||
return false;
|
return false;
|
||||||
if (is<PlainDateTime>(object))
|
if (is<PlainDateTime>(object))
|
||||||
|
@ -641,6 +706,8 @@ ThrowCompletionOr<bool> is_partial_temporal_object(VM& vm, Value value)
|
||||||
return false;
|
return false;
|
||||||
if (is<PlainYearMonth>(object))
|
if (is<PlainYearMonth>(object))
|
||||||
return false;
|
return false;
|
||||||
|
if (is<ZonedDateTime>(object))
|
||||||
|
return false;
|
||||||
|
|
||||||
// 3. Let calendarProperty be ? Get(value, "calendar").
|
// 3. Let calendarProperty be ? Get(value, "calendar").
|
||||||
auto calendar_property = TRY(object.get(vm.names.calendar));
|
auto calendar_property = TRY(object.get(vm.names.calendar));
|
||||||
|
|
|
@ -33,11 +33,25 @@ enum class DateType {
|
||||||
YearMonth,
|
YearMonth,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class Disambiguation {
|
||||||
|
Compatible,
|
||||||
|
Earlier,
|
||||||
|
Later,
|
||||||
|
Reject,
|
||||||
|
};
|
||||||
|
|
||||||
enum class DurationOperation {
|
enum class DurationOperation {
|
||||||
Since,
|
Since,
|
||||||
Until,
|
Until,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OffsetOption {
|
||||||
|
Prefer,
|
||||||
|
Use,
|
||||||
|
Ignore,
|
||||||
|
Reject,
|
||||||
|
};
|
||||||
|
|
||||||
enum class Overflow {
|
enum class Overflow {
|
||||||
Constrain,
|
Constrain,
|
||||||
Reject,
|
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);
|
double epoch_days_to_epoch_ms(double day, double time);
|
||||||
ThrowCompletionOr<void> check_iso_days_range(VM&, ISODate const&);
|
ThrowCompletionOr<void> check_iso_days_range(VM&, ISODate const&);
|
||||||
ThrowCompletionOr<Overflow> get_temporal_overflow_option(VM&, Object const& options);
|
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);
|
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<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<void> validate_temporal_rounding_increment(VM&, u64 increment, u64 dividend, bool inclusive);
|
||||||
ThrowCompletionOr<Precision> get_temporal_fractional_second_digits_option(VM&, Object const& options);
|
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/PlainMonthDay.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
#include <LibJS/Runtime/Temporal/PlainYearMonth.h>
|
||||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
#include <LibJS/Runtime/VM.h>
|
#include <LibJS/Runtime/VM.h>
|
||||||
#include <LibUnicode/Locale.h>
|
#include <LibUnicode/Locale.h>
|
||||||
#include <LibUnicode/UnicodeKeywords.h>
|
#include <LibUnicode/UnicodeKeywords.h>
|
||||||
|
@ -452,7 +453,6 @@ ThrowCompletionOr<String> to_temporal_calendar_identifier(VM& vm, Value temporal
|
||||||
// [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]]
|
// [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]]
|
||||||
// internal slot, then
|
// internal slot, then
|
||||||
// i. Return temporalCalendarLike.[[Calendar]].
|
// i. Return temporalCalendarLike.[[Calendar]].
|
||||||
// FIXME: Add the other calendar-holding types as we define them.
|
|
||||||
if (is<PlainDate>(temporal_calendar_object))
|
if (is<PlainDate>(temporal_calendar_object))
|
||||||
return static_cast<PlainDate const&>(temporal_calendar_object).calendar();
|
return static_cast<PlainDate const&>(temporal_calendar_object).calendar();
|
||||||
if (is<PlainDateTime>(temporal_calendar_object))
|
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();
|
return static_cast<PlainMonthDay const&>(temporal_calendar_object).calendar();
|
||||||
if (is<PlainYearMonth>(temporal_calendar_object))
|
if (is<PlainYearMonth>(temporal_calendar_object))
|
||||||
return static_cast<PlainYearMonth const&>(temporal_calendar_object).calendar();
|
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.
|
// 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]],
|
// 1. If item has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]],
|
||||||
// [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
|
// [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then
|
||||||
// a. Return item.[[Calendar]].
|
// a. Return item.[[Calendar]].
|
||||||
// FIXME: Add the other calendar-holding types as we define them.
|
|
||||||
if (is<PlainDate>(item))
|
if (is<PlainDate>(item))
|
||||||
return static_cast<PlainDate const&>(item).calendar();
|
return static_cast<PlainDate const&>(item).calendar();
|
||||||
if (is<PlainDateTime>(item))
|
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();
|
return static_cast<PlainMonthDay const&>(item).calendar();
|
||||||
if (is<PlainYearMonth>(item))
|
if (is<PlainYearMonth>(item))
|
||||||
return static_cast<PlainYearMonth const&>(item).calendar();
|
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").
|
// 2. Let calendarLike be ? Get(item, "calendar").
|
||||||
auto calendar_like = TRY(item.get(vm.names.calendar));
|
auto calendar_like = TRY(item.get(vm.names.calendar));
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
|
#include <LibJS/Runtime/Temporal/PlainTimeConstructor.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
#include <LibJS/Runtime/Temporal/PlainYearMonthConstructor.h>
|
||||||
#include <LibJS/Runtime/Temporal/Temporal.h>
|
#include <LibJS/Runtime/Temporal/Temporal.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTimeConstructor.h>
|
||||||
|
|
||||||
namespace JS::Temporal {
|
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.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.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.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/PlainDateTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
#include <LibJS/Runtime/Temporal/PlainTime.h>
|
||||||
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
#include <LibJS/Runtime/Temporal/TimeZone.h>
|
||||||
|
#include <LibJS/Runtime/Temporal/ZonedDateTime.h>
|
||||||
#include <LibJS/Runtime/VM.h>
|
#include <LibJS/Runtime/VM.h>
|
||||||
|
|
||||||
namespace JS::Temporal {
|
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));
|
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
|
// 11.1.7 FormatDateTimeUTCOffsetRounded ( offsetNanoseconds ), https://tc39.es/proposal-temporal/#sec-temporal-formatdatetimeutcoffsetrounded
|
||||||
String format_date_time_utc_offset_rounded(i64 offset_nanoseconds)
|
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
|
// 1. If temporalTimeZoneLike is an Object, then
|
||||||
if (temporal_time_zone_like.is_object()) {
|
if (temporal_time_zone_like.is_object()) {
|
||||||
// FIXME: a. If temporalTimeZoneLike has an [[InitializedTemporalZonedDateTime]] internal slot, then
|
auto const& object = temporal_time_zone_like.as_object();
|
||||||
// FIXME: i. Return temporalTimeZoneLike.[[TimeZone]].
|
|
||||||
|
// 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.
|
// 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;
|
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
|
// 11.1.16 ParseTimeZoneIdentifier ( identifier ), https://tc39.es/proposal-temporal/#sec-parsetimezoneidentifier
|
||||||
ThrowCompletionOr<TimeZone> parse_time_zone_identifier(VM& vm, StringView identifier)
|
ThrowCompletionOr<TimeZone> parse_time_zone_identifier(VM& vm, StringView identifier)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,15 +21,9 @@ struct TimeZone {
|
||||||
Optional<i64> offset_minutes;
|
Optional<i64> offset_minutes;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Disambiguation {
|
|
||||||
Compatible,
|
|
||||||
Earlier,
|
|
||||||
Later,
|
|
||||||
Reject,
|
|
||||||
};
|
|
||||||
|
|
||||||
ISODateTime get_iso_parts_from_epoch(Crypto::SignedBigInteger const& epoch_nanoseconds);
|
ISODateTime get_iso_parts_from_epoch(Crypto::SignedBigInteger const& epoch_nanoseconds);
|
||||||
String format_offset_time_zone_identifier(i64 offset_minutes, Optional<TimeStyle> = {});
|
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);
|
String format_date_time_utc_offset_rounded(i64 offset_nanoseconds);
|
||||||
ThrowCompletionOr<String> to_temporal_time_zone_identifier(VM&, Value temporal_time_zone_like);
|
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);
|
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> 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<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<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);
|
ThrowCompletionOr<TimeZone> parse_time_zone_identifier(VM&, StringView identifier);
|
||||||
TimeZone parse_time_zone_identifier(StringView identifier);
|
TimeZone parse_time_zone_identifier(StringView identifier);
|
||||||
TimeZone parse_time_zone_identifier(ParseResult const&);
|
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