1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 19:47:44 +00:00

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.
This commit is contained in:
Shannon Booth 2024-02-18 17:53:37 +13:00 committed by Tim Flynn
parent fa692ae3f6
commit c063bf39a9
6 changed files with 101 additions and 70 deletions

View file

@ -545,8 +545,21 @@ ThrowCompletionOr<Optional<String>> get_temporal_unit(VM& vm, Object const& norm
return value; 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 // 13.16 ToRelativeTemporalObject ( options ), https://tc39.es/proposal-temporal/#sec-temporal-torelativetemporalobject
ThrowCompletionOr<Value> to_relative_temporal_object(VM& vm, Object const& options) ThrowCompletionOr<RelativeTo> to_relative_temporal_object(VM& vm, Object const& options)
{ {
auto& realm = *vm.current_realm(); auto& realm = *vm.current_realm();
@ -558,7 +571,7 @@ ThrowCompletionOr<Value> to_relative_temporal_object(VM& vm, Object const& optio
// 3. If value is undefined, then // 3. If value is undefined, then
if (value.is_undefined()) { if (value.is_undefined()) {
// a. Return value. // a. Return value.
return value; return RelativeTo {};
} }
// 4. Let offsetBehaviour be option. // 4. Let offsetBehaviour be option.
@ -575,52 +588,77 @@ ThrowCompletionOr<Value> to_relative_temporal_object(VM& vm, Object const& optio
// 6. If Type(value) is Object, then // 6. If Type(value) is Object, then
if (value.is_object()) { if (value.is_object()) {
auto& value_object = value.as_object(); auto& value_object = value.as_object();
// a. If value has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (is<ZonedDateTime>(value_object)) {
auto& zoned_relative_to = static_cast<ZonedDateTime&>(value_object);
// a. If value has either an [[InitializedTemporalDate]] or [[InitializedTemporalZonedDateTime]] internal slot, then // i. Let timeZoneRec be ? CreateTimeZoneMethodsRecord(value.[[TimeZone]], « GET-OFFSET-NANOSECONDS-FOR, GET-POSSIBLE-INSTANTS-FOR »).
if (is<PlainDate>(value_object) || is<ZonedDateTime>(value_object)) { auto time_zone_record = TRY(create_time_zone_methods_record(vm, NonnullGCPtr<Object> { zoned_relative_to.time_zone() }, { { TimeZoneMethod::GetOffsetNanosecondsFor, TimeZoneMethod::GetPossibleInstantsFor } }));
// i. Return value.
return value; // 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<PlainDate>(value_object)) {
// i. Return the Record { [[PlainRelativeTo]]: value, [[ZonedRelativeTo]]: undefined, [[TimeZoneRec]]: undefined }.
return RelativeTo {
.plain_relative_to = static_cast<PlainDate&>(value_object),
.zoned_relative_to = {},
.time_zone_record = {},
};
}
// c. If value has an [[InitializedTemporalDateTime]] internal slot, then
if (is<PlainDateTime>(value_object)) { if (is<PlainDateTime>(value_object)) {
auto& plain_date_time = static_cast<PlainDateTime&>(value_object); auto& plain_date_time = static_cast<PlainDateTime&>(value_object);
// i. Return ? CreateTemporalDate(value.[[ISOYear]], value.[[ISOMonth]], value.[[ISODay]], 0, 0, 0, 0, 0, 0, value.[[Calendar]]). // i. Let plainDate be ! CreateTemporalDate(value.[[ISOYear]], value.[[ISOMonth]], value.[[ISODay]], 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())); 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)); 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 })); 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<StringView> {})); auto* fields = TRY(prepare_temporal_fields(vm, value_object, field_names, Vector<StringView> {}));
// f. Let dateOptions be OrdinaryObjectCreate(null). // g. Let dateOptions be OrdinaryObjectCreate(null).
auto date_options = Object::create(realm, nullptr); 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))); 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)); 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)); 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)); 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()) { if (!time_zone.is_undefined()) {
// i. Set timeZone to ? ToTemporalTimeZone(timeZone). // i. Set timeZone to ? ToTemporalTimeZone(timeZone).
time_zone = TRY(to_temporal_time_zone(vm, time_zone)); 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()) { if (offset_string.is_undefined()) {
// i. Set offsetBehaviour to wall. // i. Set offsetBehaviour to wall.
offset_behavior = OffsetBehavior::Wall; offset_behavior = OffsetBehavior::Wall;
@ -682,7 +720,12 @@ ThrowCompletionOr<Value> to_relative_temporal_object(VM& vm, Object const& optio
// 8. If timeZone is undefined, then // 8. If timeZone is undefined, then
if (time_zone.is_undefined()) { if (time_zone.is_undefined()) {
// a. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). // 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; double offset_ns;
@ -710,7 +753,13 @@ ThrowCompletionOr<Value> 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)); 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). // 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<Object> { 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 // 13.17 LargerOfTwoTemporalUnits ( u1, u2 ), https://tc39.es/proposal-temporal/#sec-temporal-largeroftwotemporalunits

View file

@ -14,6 +14,7 @@
#include <LibJS/Runtime/Completion.h> #include <LibJS/Runtime/Completion.h>
#include <LibJS/Runtime/GlobalObject.h> #include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Temporal/ISO8601.h> #include <LibJS/Runtime/Temporal/ISO8601.h>
#include <LibJS/Runtime/Temporal/TimeZoneMethods.h>
#include <LibJS/Runtime/ValueInlines.h> #include <LibJS/Runtime/ValueInlines.h>
namespace JS::Temporal { namespace JS::Temporal {
@ -148,7 +149,16 @@ ThrowCompletionOr<u64> to_temporal_rounding_increment(VM&, Object const& normali
ThrowCompletionOr<u64> to_temporal_date_time_rounding_increment(VM&, Object const& normalized_options, StringView smallest_unit); ThrowCompletionOr<u64> to_temporal_date_time_rounding_increment(VM&, Object const& normalized_options, StringView smallest_unit);
ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(VM&, Object const& normalized_options); ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(VM&, Object const& normalized_options);
ThrowCompletionOr<Optional<String>> get_temporal_unit(VM&, Object const& normalized_options, PropertyKey const&, UnitGroup, TemporalUnitDefault const& default_, Vector<StringView> const& extra_values = {}); ThrowCompletionOr<Optional<String>> get_temporal_unit(VM&, Object const& normalized_options, PropertyKey const&, UnitGroup, TemporalUnitDefault const& default_, Vector<StringView> const& extra_values = {});
ThrowCompletionOr<Value> to_relative_temporal_object(VM&, Object const& options);
struct RelativeTo {
GCPtr<PlainDate> plain_relative_to; // [[PlainRelativeTo]]
GCPtr<ZonedDateTime> zoned_relative_to; // [[ZonedRelativeTo]]
Optional<TimeZoneMethods> time_zone_record; // [[TimeZoneRec]]
};
ThrowCompletionOr<RelativeTo> to_relative_temporal_object(VM&, Object const& options);
Value relative_to_converted_to_value(RelativeTo const&);
StringView larger_of_two_temporal_units(StringView, StringView); StringView larger_of_two_temporal_units(StringView, StringView);
ThrowCompletionOr<Object*> merge_largest_unit_option(VM&, Object const& options, String largest_unit); ThrowCompletionOr<Object*> merge_largest_unit_option(VM&, Object const& options, String largest_unit);
Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit); Optional<u16> maximum_temporal_duration_rounding_increment(StringView unit);

View file

@ -2039,11 +2039,11 @@ ThrowCompletionOr<NonnullGCPtr<Duration>> add_duration_to_or_subtract_duration_f
// 3. Set options to ? GetOptionsObject(options). // 3. Set options to ? GetOptionsObject(options).
auto const* options = TRY(get_options_object(vm, options_value)); auto const* options = TRY(get_options_object(vm, options_value));
// 4. Let relativeTo be ? ToRelativeTemporalObject(options). // 4. Let relativeToRecord be ? ToRelativeTemporalObject(options).
auto relative_to = TRY(to_relative_temporal_object(vm, *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). // 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]]). // 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)); 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));

View file

@ -117,7 +117,7 @@ JS_DEFINE_NATIVE_FUNCTION(DurationConstructor::compare)
auto const* options = TRY(get_options_object(vm, vm.argument(2))); auto const* options = TRY(get_options_object(vm, vm.argument(2)));
// 4. Let relativeTo be ? ToRelativeTemporalObject(options). // 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]]). // 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())); auto shift1 = TRY(calculate_offset_shift(vm, relative_to, one->years(), one->months(), one->weeks(), one->days()));

View file

@ -418,43 +418,29 @@ JS_DEFINE_NATIVE_FUNCTION(DurationPrototype::round)
// 20. Let relativeTo be ? ToRelativeTemporalObject(roundTo). // 20. Let relativeTo be ? ToRelativeTemporalObject(roundTo).
auto relative_to = TRY(to_relative_temporal_object(vm, *round_to)); 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). // 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. 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 } }));
PlainDate* plain_relative_to_ptr = nullptr;
ZonedDateTime* zoned_relative_to_ptr = nullptr;
if (relative_to.is_object()) {
if (is<PlainDate>(relative_to.as_object()))
plain_relative_to_ptr = &static_cast<PlainDate&>(relative_to.as_object());
else if (is<ZonedDateTime>(relative_to.as_object()))
zoned_relative_to_ptr = &static_cast<ZonedDateTime&>(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 } }));
// 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]]. // 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). // 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). // 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 // 25. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (relative_to.is_object() && is<ZonedDateTime>(relative_to.as_object())) { if (relative_to.zoned_relative_to) {
auto& relative_to_zoned_date_time = static_cast<ZonedDateTime&>(relative_to.as_object());
// a. Set relativeTo to ? MoveRelativeZonedDateTime(relativeTo, balanceResult.[[Years]], balanceResult.[[Months]], balanceResult.[[Weeks]], 0). // 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). // 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]]). // 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)); 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). // 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf).
auto relative_to = TRY(to_relative_temporal_object(vm, *total_of)); 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). // 7. Let unit be ? GetTemporalUnit(totalOf, "unit", datetime, required).
auto unit = TRY(get_temporal_unit(vm, *total_of, vm.names.unit, UnitGroup::DateTime, TemporalUnitRequired {})); 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). // 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. // 9. Let intermediate be undefined.
ZonedDateTime* intermediate = nullptr; ZonedDateTime* intermediate = nullptr;
// 10. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then // 10. If Type(relativeTo) is Object and relativeTo has an [[InitializedTemporalZonedDateTime]] internal slot, then
if (relative_to.is_object() && is<ZonedDateTime>(relative_to.as_object())) { if (relative_to.zoned_relative_to) {
auto& relative_to_zoned_date_time = static_cast<ZonedDateTime&>(relative_to.as_object());
// a. Set intermediate to ? MoveRelativeZonedDateTime(relativeTo, unbalanceResult.[[Years]], unbalanceResult.[[Months]], unbalanceResult.[[Weeks]], 0). // 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). // 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)); 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). // 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. 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 } }));
PlainDate* plain_relative_to_ptr = nullptr;
ZonedDateTime* zoned_relative_to_ptr = nullptr;
if (relative_to.is_object()) { 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));
if (is<PlainDate>(relative_to.as_object()))
plain_relative_to_ptr = &static_cast<PlainDate&>(relative_to.as_object());
else if (is<ZonedDateTime>(relative_to.as_object()))
zoned_relative_to_ptr = &static_cast<ZonedDateTime&>(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));
// 13. Return 𝔽(roundRecord.[[Total]]). // 13. Return 𝔽(roundRecord.[[Total]]).
return Value(round_record.total); return Value(round_record.total);

View file

@ -98,7 +98,7 @@ describe("errors", () => {
const duration = new Temporal.Duration(); const duration = new Temporal.Duration();
expect(() => { expect(() => {
Temporal.Duration.compare(duration, duration, { relativeTo: zonedDateTime }); 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", () => { test("UTC designator only allowed with bracketed time zone", () => {