From c063bf39a920b6d912159ba1371edcd4e353f5d3 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 18 Feb 2024 17:53:37 +1300 Subject: [PATCH] LibJS: Make ToRelativeTemporalObject return a RelativeTo struct This follows a change in the spec which refactored this function and its callers to make use of a record instead of stuffing all of the possible return values into a single Value. As always in temporal land, this AO remains out of date, as well of all its callers. Update all of these callers to the new API where possible, and use an ad-hoc function to convert this struct back to a JS::Value for APIs that have not been updated yet. --- .../Runtime/Temporal/AbstractOperations.cpp | 91 ++++++++++++++----- .../Runtime/Temporal/AbstractOperations.h | 12 ++- .../LibJS/Runtime/Temporal/Duration.cpp | 6 +- .../Runtime/Temporal/DurationConstructor.cpp | 2 +- .../Runtime/Temporal/DurationPrototype.cpp | 58 +++--------- .../Temporal/Duration/Duration.compare.js | 2 +- 6 files changed, 101 insertions(+), 70 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index 42c053ac78..56ecb58455 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -545,8 +545,21 @@ ThrowCompletionOr> get_temporal_unit(VM& vm, Object const& norm return value; } +// FIXME: This function is a hack to undo a RelativeTo back to the old API, which was just a Value. +// We should get rid of this once all callers have been converted to the new API. +Value relative_to_converted_to_value(RelativeTo const& relative_to) +{ + if (relative_to.plain_relative_to) + return relative_to.plain_relative_to; + + if (relative_to.zoned_relative_to) + return relative_to.zoned_relative_to; + + return js_undefined(); +} + // 13.16 ToRelativeTemporalObject ( options ), https://tc39.es/proposal-temporal/#sec-temporal-torelativetemporalobject -ThrowCompletionOr to_relative_temporal_object(VM& vm, Object const& options) +ThrowCompletionOr to_relative_temporal_object(VM& vm, Object const& options) { auto& realm = *vm.current_realm(); @@ -558,7 +571,7 @@ ThrowCompletionOr to_relative_temporal_object(VM& vm, Object const& optio // 3. If value is undefined, then if (value.is_undefined()) { // a. Return value. - return value; + return RelativeTo {}; } // 4. Let offsetBehaviour be option. @@ -575,52 +588,77 @@ ThrowCompletionOr to_relative_temporal_object(VM& vm, Object const& optio // 6. If Type(value) is Object, then if (value.is_object()) { auto& value_object = value.as_object(); + // a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then + if (is(value_object)) { + auto& zoned_relative_to = static_cast(value_object); - // a. If value has either an [[InitializedTemporalDate]] or [[InitializedTemporalZonedDateTime]] internal slot, then - if (is(value_object) || is(value_object)) { - // i. Return value. - return value; + // i. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(value.[[TimeZone]], « GET-OFFSET-NANOSECONDS-FOR, GET-POSSIBLE-INSTANTS-FOR »). + auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr { zoned_relative_to.time_zone() }, { { TimeZoneMethod::GetOffsetNanosecondsFor, TimeZoneMethod::GetPossibleInstantsFor } })); + + // ii. Return the Record { [[PlainRelativeTo]]: undefined, [[ZonedRelativeTo]]: value, [[TimeZoneRec]]: timeZoneRec }. + return RelativeTo { + .plain_relative_to = {}, + .zoned_relative_to = zoned_relative_to, + .time_zone_record = time_zone_record, + }; } - // b. If value has an [[InitializedTemporalDateTime]] internal slot, then + // b. If value has an [[InitializedTemporalDate]] internal slot, then + if (is(value_object)) { + // i. Return the Record { [[PlainRelativeTo]]: value, [[ZonedRelativeTo]]: undefined, [[TimeZoneRec]]: undefined }. + return RelativeTo { + .plain_relative_to = static_cast(value_object), + .zoned_relative_to = {}, + .time_zone_record = {}, + }; + } + + // c. If value has an [[InitializedTemporalDateTime]] internal slot, then if (is(value_object)) { auto& plain_date_time = static_cast(value_object); - // i. Return ? CreateTemporalDate(value.[[ISOYear]], value.[[ISOMonth]], value.[[ISODay]], 0, 0, 0, 0, 0, 0, value.[[Calendar]]). - return TRY(create_temporal_date(vm, plain_date_time.iso_year(), plain_date_time.iso_month(), plain_date_time.iso_day(), plain_date_time.calendar())); + // i. Let plainDate be ! CreateTemporalDate(value.[[ISOYear]], value.[[ISOMonth]], value.[[ISODay]], value.[[Calendar]]). + auto* plain_date = TRY(create_temporal_date(vm, plain_date_time.iso_year(), plain_date_time.iso_month(), plain_date_time.iso_day(), plain_date_time.calendar())); + + // ii. Return the Record { [[PlainRelativeTo]]: plainDate, [[ZonedRelativeTo]]: undefined, [[TimeZoneRec]]: undefined }. + return RelativeTo { + .plain_relative_to = plain_date, + .zoned_relative_to = {}, + .time_zone_record = {}, + }; } - // c. Let calendar be ? GetTemporalCalendarWithISODefault(value). + // d. Let calendar be ? GetTemporalCalendarWithISODefault(value). calendar = TRY(get_temporal_calendar_with_iso_default(vm, value_object)); - // d. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »). + // e. Let fieldNames be ? CalendarFields(calendar, « "day", "hour", "microsecond", "millisecond", "minute", "month", "monthCode", "nanosecond", "second", "year" »). auto field_names = TRY(calendar_fields(vm, *calendar, { "day"sv, "hour"sv, "microsecond"sv, "millisecond"sv, "minute"sv, "month"sv, "monthCode"sv, "nanosecond"sv, "second"sv, "year"sv })); - // e. Let fields be ? PrepareTemporalFields(value, fieldNames, «»). + // f. Let fields be ? PrepareTemporalFields(value, fieldNames, «»). auto* fields = TRY(prepare_temporal_fields(vm, value_object, field_names, Vector {})); - // f. Let dateOptions be OrdinaryObjectCreate(null). + // g. Let dateOptions be OrdinaryObjectCreate(null). auto date_options = Object::create(realm, nullptr); - // g. Perform ! CreateDataPropertyOrThrow(dateOptions, "overflow", "constrain"). + // h. Perform ! CreateDataPropertyOrThrow(dateOptions, "overflow", "constrain"). MUST(date_options->create_data_property_or_throw(vm.names.overflow, PrimitiveString::create(vm, "constrain"_string))); - // h. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, dateOptions). + // i. Let result be ? InterpretTemporalDateTimeFields(calendar, fields, dateOptions). result = TRY(interpret_temporal_date_time_fields(vm, *calendar, *fields, date_options)); - // i. Let offsetString be ? Get(value, "offset"). + // j. Let offsetString be ? Get(value, "offset"). offset_string = TRY(value_object.get(vm.names.offset)); - // j. Let timeZone be ? Get(value, "timeZone"). + // k. Let timeZone be ? Get(value, "timeZone"). time_zone = TRY(value_object.get(vm.names.timeZone)); - // k. If timeZone is not undefined, then + // l. If timeZone is not undefined, then if (!time_zone.is_undefined()) { // i. Set timeZone to ? ToTemporalTimeZone(timeZone). time_zone = TRY(to_temporal_time_zone(vm, time_zone)); } - // l. If offsetString is undefined, then + // m. If offsetString is undefined, then if (offset_string.is_undefined()) { // i. Set offsetBehaviour to wall. offset_behavior = OffsetBehavior::Wall; @@ -682,7 +720,12 @@ ThrowCompletionOr to_relative_temporal_object(VM& vm, Object const& optio // 8. If timeZone is undefined, then if (time_zone.is_undefined()) { // a. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). - return TRY(create_temporal_date(vm, result.year, result.month, result.day, *calendar)); + auto* plain_date = TRY(create_temporal_date(vm, result.year, result.month, result.day, *calendar)); + return RelativeTo { + .plain_relative_to = plain_date, + .zoned_relative_to = {}, + .time_zone_record = {}, + }; } double offset_ns; @@ -710,7 +753,13 @@ ThrowCompletionOr to_relative_temporal_object(VM& vm, Object const& optio auto const* epoch_nanoseconds = TRY(interpret_iso_date_time_offset(vm, result.year, result.month, result.day, result.hour, result.minute, result.second, result.millisecond, result.microsecond, result.nanosecond, offset_behavior, offset_ns, time_zone, "compatible"sv, "reject"sv, match_behavior)); // 12. Return ! CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar). - return MUST(create_temporal_zoned_date_time(vm, *epoch_nanoseconds, time_zone.as_object(), *calendar)); + auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr { time_zone.as_object() }, { { TimeZoneMethod::GetOffsetNanosecondsFor, TimeZoneMethod::GetPossibleInstantsFor } })); + auto* zoned_relative_to = MUST(create_temporal_zoned_date_time(vm, *epoch_nanoseconds, time_zone.as_object(), *calendar)); + return RelativeTo { + .plain_relative_to = {}, + .zoned_relative_to = zoned_relative_to, + .time_zone_record = time_zone_record, + }; } // 13.17 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index b0d200f620..e99a448059 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -14,6 +14,7 @@ #include #include #include +#include #include namespace JS::Temporal { @@ -148,7 +149,16 @@ ThrowCompletionOr to_temporal_rounding_increment(VM&, Object const& normali ThrowCompletionOr to_temporal_date_time_rounding_increment(VM&, Object const& normalized_options, StringView smallest_unit); ThrowCompletionOr to_seconds_string_precision(VM&, Object const& normalized_options); ThrowCompletionOr> get_temporal_unit(VM&, Object const& normalized_options, PropertyKey const&, UnitGroup, TemporalUnitDefault const& default_, Vector const& extra_values = {}); -ThrowCompletionOr to_relative_temporal_object(VM&, Object const& options); + +struct RelativeTo { + GCPtr plain_relative_to; // [[PlainRelativeTo]] + GCPtr zoned_relative_to; // [[ZonedRelativeTo]] + Optional time_zone_record; // [[TimeZoneRec]] +}; +ThrowCompletionOr to_relative_temporal_object(VM&, Object const& options); + +Value relative_to_converted_to_value(RelativeTo const&); + StringView larger_of_two_temporal_units(StringView, StringView); ThrowCompletionOr merge_largest_unit_option(VM&, Object const& options, String largest_unit); Optional maximum_temporal_duration_rounding_increment(StringView unit); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp index 3c0e663483..41d4ab9883 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Duration.cpp @@ -2039,11 +2039,11 @@ ThrowCompletionOr> add_duration_to_or_subtract_duration_f // 3. Set options to ? GetOptionsObject(options). auto const* options = TRY(get_options_object(vm, options_value)); - // 4. Let relativeTo be ? ToRelativeTemporalObject(options). - auto relative_to = TRY(to_relative_temporal_object(vm, *options)); + // 4. Let relativeToRecord be ? ToRelativeTemporalObject(options). + auto relative_to_record = TRY(to_relative_temporal_object(vm, *options)); // 5. Let result be ? AddDuration(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], sign × other.[[Years]], sign × other.[[Months]], sign × other.[[Weeks]], sign × other.[[Days]], sign × other.[[Hours]], sign × other.[[Minutes]], sign × other.[[Seconds]], sign × other.[[Milliseconds]], sign × other.[[Microseconds]], sign × other.[[Nanoseconds]], relativeTo). - auto result = TRY(add_duration(vm, duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds(), sign * other.years, sign * other.months, sign * other.weeks, sign * other.days, sign * other.hours, sign * other.minutes, sign * other.seconds, sign * other.milliseconds, sign * other.microseconds, sign * other.nanoseconds, relative_to)); + auto result = TRY(add_duration(vm, duration.years(), duration.months(), duration.weeks(), duration.days(), duration.hours(), duration.minutes(), duration.seconds(), duration.milliseconds(), duration.microseconds(), duration.nanoseconds(), sign * other.years, sign * other.months, sign * other.weeks, sign * other.days, sign * other.hours, sign * other.minutes, sign * other.seconds, sign * other.milliseconds, sign * other.microseconds, sign * other.nanoseconds, relative_to_converted_to_value(relative_to_record))); // 6. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). return MUST(create_temporal_duration(vm, result.years, result.months, result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp index 65b5823a7e..f5d3570411 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/DurationConstructor.cpp @@ -117,7 +117,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare) auto const* options = TRY(get_options_object(vm, vm.argument(2))); // 4. Let relativeTo be ? ToRelativeTemporalObject(options). - auto relative_to = TRY(to_relative_temporal_object(vm, *options)); + auto relative_to = relative_to_converted_to_value(TRY(to_relative_temporal_object(vm, *options))); // 5. Let shift1 be ? CalculateOffsetShift(relativeTo, one.[[Years]], one.[[Months]], one.[[Weeks]], one.[[Days]]). auto shift1 = TRY(calculate_offset_shift(vm, relative_to, one->years(), one->months(), one->weeks(), one->days())); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp index 28f89ea882..969e8c7fff 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/DurationPrototype.cpp @@ -418,43 +418,29 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round) // 20. Let relativeTo be ? ToRelativeTemporalObject(roundTo). auto relative_to = TRY(to_relative_temporal_object(vm, *round_to)); + auto relative_to_value = relative_to_converted_to_value(relative_to); // 21. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], largestUnit, relativeTo). - auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *largest_unit, relative_to)); + auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *largest_unit, relative_to_value)); - // FIXME: AD-HOC - this function is not up to date with latest spec. - PlainDate* plain_relative_to_ptr = nullptr; - ZonedDateTime* zoned_relative_to_ptr = nullptr; - - if (relative_to.is_object()) { - if (is(relative_to.as_object())) - plain_relative_to_ptr = &static_cast(relative_to.as_object()); - else if (is(relative_to.as_object())) - zoned_relative_to_ptr = &static_cast(relative_to.as_object()); - else - VERIFY_NOT_REACHED(); - } - - auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, plain_relative_to_ptr, zoned_relative_to_ptr, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } })); + auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, relative_to.plain_relative_to, relative_to.zoned_relative_to, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } })); // 22. Let roundResult be (? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]]. - auto round_result = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode, relative_to.is_object() ? &relative_to.as_object() : nullptr, calendar_record)).duration_record; + auto round_result = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), duration->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr, calendar_record)).duration_record; // 23. Let adjustResult be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo). - auto adjust_result = TRY(adjust_rounded_duration_days(vm, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to.is_object() ? &relative_to.as_object() : nullptr)); + auto adjust_result = TRY(adjust_rounded_duration_days(vm, round_result.years, round_result.months, round_result.weeks, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, round_result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr)); // 24. Let balanceResult be ? BalanceDurationRelative(adjustResult.[[Years]], adjustResult.[[Months]], adjustResult.[[Weeks]], adjustResult.[[Days]], largestUnit, relativeTo). - auto balance_result = TRY(balance_duration_relative(vm, adjust_result.years, adjust_result.months, adjust_result.weeks, adjust_result.days, *largest_unit, relative_to)); + auto balance_result = TRY(balance_duration_relative(vm, adjust_result.years, adjust_result.months, adjust_result.weeks, adjust_result.days, *largest_unit, relative_to_value)); // 25. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then - if (relative_to.is_object() && is(relative_to.as_object())) { - auto& relative_to_zoned_date_time = static_cast(relative_to.as_object()); - + if (relative_to.zoned_relative_to) { // a. Set relativeTo to ? MoveRelativeZonedDateTime(relativeTo, balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], 0). - relative_to = TRY(move_relative_zoned_date_time(vm, relative_to_zoned_date_time, balance_result.years, balance_result.months, balance_result.weeks, 0)); + relative_to_value = TRY(move_relative_zoned_date_time(vm, *relative_to.zoned_relative_to, balance_result.years, balance_result.months, balance_result.weeks, 0)); } // 26. Let result be ? BalanceDuration(balanceResult.[[Days]], adjustResult.[[Hours]], adjustResult.[[Minutes]], adjustResult.[[Seconds]], adjustResult.[[Milliseconds]], adjustResult.[[Microseconds]], adjustResult.[[Nanoseconds]], largestUnit, relativeTo). - auto result = TRY(balance_duration(vm, balance_result.days, adjust_result.hours, adjust_result.minutes, adjust_result.seconds, adjust_result.milliseconds, adjust_result.microseconds, Crypto::SignedBigInteger { adjust_result.nanoseconds }, *largest_unit, relative_to.is_object() ? &relative_to.as_object() : nullptr)); + auto result = TRY(balance_duration(vm, balance_result.days, adjust_result.hours, adjust_result.minutes, adjust_result.seconds, adjust_result.milliseconds, adjust_result.microseconds, Crypto::SignedBigInteger { adjust_result.nanoseconds }, *largest_unit, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr)); // 27. Return ! CreateTemporalDuration(balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). return MUST(create_temporal_duration(vm, balance_result.years, balance_result.months, balance_result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); @@ -494,44 +480,30 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::total) // 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf). auto relative_to = TRY(to_relative_temporal_object(vm, *total_of)); + auto relative_to_value = relative_to_converted_to_value(relative_to); // 7. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required). auto unit = TRY(get_temporal_unit(vm, *total_of, vm.names.unit, UnitGroup::DateTime, TemporalUnitRequired {})); // 8. Let unbalanceResult be ? UnbalanceDurationRelative(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], unit, relativeTo). - auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *unit, relative_to)); + auto unbalance_result = TRY(unbalance_duration_relative(vm, duration->years(), duration->months(), duration->weeks(), duration->days(), *unit, relative_to_value)); // 9. Let intermediate be undefined. ZonedDateTime* intermediate = nullptr; // 10. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then - if (relative_to.is_object() && is(relative_to.as_object())) { - auto& relative_to_zoned_date_time = static_cast(relative_to.as_object()); - + if (relative_to.zoned_relative_to) { // a. Set intermediate to ? MoveRelativeZonedDateTime(relativeTo, unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], 0). - intermediate = TRY(move_relative_zoned_date_time(vm, relative_to_zoned_date_time, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, 0)); + intermediate = TRY(move_relative_zoned_date_time(vm, *relative_to.zoned_relative_to, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, 0)); } // 11. Let balanceResult be ? BalanceDuration(unbalanceResult.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], unit, intermediate). auto balance_result = TRY(balance_duration(vm, unbalance_result.days, duration->hours(), duration->minutes(), duration->seconds(), duration->milliseconds(), duration->microseconds(), Crypto::SignedBigInteger { duration->nanoseconds() }, *unit, intermediate)); // 12. Let roundRecord be ? RoundDuration(unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], balanceResult.[[Days]], balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]], 1, unit, "trunc", relativeTo). - // FIXME: AD-HOC - this function is not up to date with latest spec. - PlainDate* plain_relative_to_ptr = nullptr; - ZonedDateTime* zoned_relative_to_ptr = nullptr; + auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, relative_to.plain_relative_to, relative_to.zoned_relative_to, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } })); - if (relative_to.is_object()) { - if (is(relative_to.as_object())) - plain_relative_to_ptr = &static_cast(relative_to.as_object()); - else if (is(relative_to.as_object())) - zoned_relative_to_ptr = &static_cast(relative_to.as_object()); - else - VERIFY_NOT_REACHED(); - } - - auto calendar_record = TRY(create_calendar_methods_record_from_relative_to(vm, plain_relative_to_ptr, zoned_relative_to_ptr, { { CalendarMethod::DateAdd, CalendarMethod::DateUntil } })); - - auto round_record = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds, 1, *unit, "trunc"sv, relative_to.is_object() ? &relative_to.as_object() : nullptr, calendar_record)); + auto round_record = TRY(round_duration(vm, unbalance_result.years, unbalance_result.months, unbalance_result.weeks, balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds, 1, *unit, "trunc"sv, relative_to_value.is_object() ? &relative_to_value.as_object() : nullptr, calendar_record)); // 13. Return 𝔽(roundRecord.[[Total]]). return Value(round_record.total); diff --git a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js index 084e343c60..d533512434 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js +++ b/Userland/Libraries/LibJS/Tests/builtins/Temporal/Duration/Duration.compare.js @@ -98,7 +98,7 @@ describe("errors", () => { const duration = new Temporal.Duration(); expect(() => { Temporal.Duration.compare(duration, duration, { relativeTo: zonedDateTime }); - }).toThrowWithMessage(TypeError, "null is not a function"); + }).toThrowWithMessage(TypeError, "getOffsetNanosecondsFor is undefined"); }); test("UTC designator only allowed with bracketed time zone", () => {