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", () => {