1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:17:44 +00:00

LibJS: Refactor common option reading into the GetDifferenceSettings AO

This is an editorial change in the Temporal spec.

See: 78abbb8
This commit is contained in:
Linus Groh 2022-06-24 00:27:29 +01:00
parent 05c5263ecc
commit ee0d5d6649
8 changed files with 141 additions and 264 deletions

View file

@ -457,7 +457,7 @@ static Vector<TemporalUnit> temporal_units = {
}; };
// 13.15 GetTemporalUnit ( normalizedOptions, key, unitGroup, default [ , extraValues ] ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalunit // 13.15 GetTemporalUnit ( normalizedOptions, key, unitGroup, default [ , extraValues ] ), https://tc39.es/proposal-temporal/#sec-temporal-gettemporalunit
ThrowCompletionOr<Optional<String>> get_temporal_unit(GlobalObject& global_object, Object const& normalized_options, PropertyKey const& key, UnitGroup unit_group, Variant<TemporalUnitRequired, Optional<StringView>> const& default_, Vector<StringView> const& extra_values) ThrowCompletionOr<Optional<String>> get_temporal_unit(GlobalObject& global_object, Object const& normalized_options, PropertyKey const& key, UnitGroup unit_group, TemporalUnitDefault const& default_, Vector<StringView> const& extra_values)
{ {
auto& vm = global_object.vm(); auto& vm = global_object.vm();
@ -1838,4 +1838,62 @@ ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject& global_object,
return result; return result;
} }
// 13.44 GetDifferenceSettings ( operation, options, unitGroup, disallowedUnits, fallbackSmallestUnit, smallestLargestDefaultUnit ), https://tc39.es/proposal-temporal/#sec-temporal-getdifferencesettings
ThrowCompletionOr<DifferenceSettings> get_difference_settings(GlobalObject& global_object, DifferenceOperation operation, Value options_value, UnitGroup unit_group, Vector<StringView> const& disallowed_units, TemporalUnitDefault const& fallback_smallest_unit, StringView smallest_largest_default_unit)
{
auto& vm = global_object.vm();
// 1. Set options to ? GetOptionsObject(options).
auto* options = TRY(get_options_object(global_object, options_value));
// 2. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", unitGroup, fallbackSmallestUnit).
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, unit_group, fallback_smallest_unit));
// 3. If disallowedUnits contains smallestUnit, throw a RangeError exception.
if (disallowed_units.contains_slow(*smallest_unit))
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *smallest_unit, "smallestUnit"sv);
// 4. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits(smallestLargestDefaultUnit, smallestUnit).
auto default_largest_unit = larger_of_two_temporal_units(smallest_largest_default_unit, *smallest_unit);
// 5. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", unitGroup, "auto").
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, unit_group, { "auto"sv }));
// 6. If disallowedUnits contains largestUnit, throw a RangeError exception.
if (disallowed_units.contains_slow(*largest_unit))
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *largest_unit, "largestUnit"sv);
// 7. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
if (largest_unit == "auto"sv)
largest_unit = default_largest_unit;
// 8. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 10. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double> { maximum }, false));
// 13. Return the Record { [[SmallestUnit]]: smallestUnit, [[LargestUnit]]: largestUnit, [[RoundingMode]]: roundingMode, [[RoundingIncrement]]: roundingIncrement, [[Options]]: options }.
return DifferenceSettings {
.smallest_unit = smallest_unit.release_value(),
.largest_unit = largest_unit.release_value(),
.rounding_mode = move(rounding_mode),
.rounding_increment = rounding_increment,
.options = *options,
};
}
} }

View file

@ -121,11 +121,20 @@ struct SecondsStringPrecision {
u32 increment; u32 increment;
}; };
struct DifferenceSettings {
String smallest_unit;
String largest_unit;
String rounding_mode;
u64 rounding_increment;
Object& options;
};
struct TemporalUnitRequired { }; struct TemporalUnitRequired { };
struct PrepareTemporalFieldsPartial { }; struct PrepareTemporalFieldsPartial { };
struct GetOptionRequired { }; struct GetOptionRequired { };
using OptionDefault = Variant<GetOptionRequired, Empty, bool, StringView, double>; using OptionDefault = Variant<GetOptionRequired, Empty, bool, StringView, double>;
using TemporalUnitDefault = Variant<TemporalUnitRequired, Optional<StringView>>;
ThrowCompletionOr<MarkedVector<Value>> iterable_to_list_of_type(GlobalObject&, Value items, Vector<OptionType> const& element_types); ThrowCompletionOr<MarkedVector<Value>> iterable_to_list_of_type(GlobalObject&, Value items, Vector<OptionType> const& element_types);
ThrowCompletionOr<Object*> get_options_object(GlobalObject&, Value options); ThrowCompletionOr<Object*> get_options_object(GlobalObject&, Value options);
@ -141,7 +150,7 @@ ThrowCompletionOr<String> to_show_offset_option(GlobalObject&, Object const& nor
ThrowCompletionOr<u64> to_temporal_rounding_increment(GlobalObject&, Object const& normalized_options, Optional<double> dividend, bool inclusive); ThrowCompletionOr<u64> to_temporal_rounding_increment(GlobalObject&, Object const& normalized_options, Optional<double> dividend, bool inclusive);
ThrowCompletionOr<u64> to_temporal_date_time_rounding_increment(GlobalObject&, Object const& normalized_options, StringView smallest_unit); ThrowCompletionOr<u64> to_temporal_date_time_rounding_increment(GlobalObject&, Object const& normalized_options, StringView smallest_unit);
ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(GlobalObject&, Object const& normalized_options); ThrowCompletionOr<SecondsStringPrecision> to_seconds_string_precision(GlobalObject&, Object const& normalized_options);
ThrowCompletionOr<Optional<String>> get_temporal_unit(GlobalObject&, Object const& normalized_options, PropertyKey const&, UnitGroup, Variant<TemporalUnitRequired, Optional<StringView>> const& default_, Vector<StringView> const& extra_values = {}); ThrowCompletionOr<Optional<String>> get_temporal_unit(GlobalObject&, Object const& normalized_options, PropertyKey const&, UnitGroup, TemporalUnitDefault const& default_, Vector<StringView> const& extra_values = {});
ThrowCompletionOr<Value> to_relative_temporal_object(GlobalObject&, Object const& options); ThrowCompletionOr<Value> to_relative_temporal_object(GlobalObject&, Object const& options);
StringView larger_of_two_temporal_units(StringView, StringView); StringView larger_of_two_temporal_units(StringView, StringView);
ThrowCompletionOr<Object*> merge_largest_unit_option(GlobalObject&, Object const& options, String largest_unit); ThrowCompletionOr<Object*> merge_largest_unit_option(GlobalObject&, Object const& options, String largest_unit);
@ -170,6 +179,7 @@ ThrowCompletionOr<TemporalTimeZone> parse_temporal_time_zone_string(GlobalObject
ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(GlobalObject&, String const& iso_string); ThrowCompletionOr<TemporalYearMonth> parse_temporal_year_month_string(GlobalObject&, String const& iso_string);
ThrowCompletionOr<double> to_positive_integer(GlobalObject&, Value argument); ThrowCompletionOr<double> to_positive_integer(GlobalObject&, Value argument);
ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names, Variant<PrepareTemporalFieldsPartial, Vector<StringView>> const& required_fields); ThrowCompletionOr<Object*> prepare_temporal_fields(GlobalObject&, Object const& fields, Vector<String> const& field_names, Variant<PrepareTemporalFieldsPartial, Vector<StringView>> const& required_fields);
ThrowCompletionOr<DifferenceSettings> get_difference_settings(GlobalObject&, DifferenceOperation, Value options_value, UnitGroup unit_group, Vector<StringView> const& disallowed_units, TemporalUnitDefault const& fallback_smallest_unit, StringView smallest_largest_default_unit);
template<size_t Size> template<size_t Size>
ThrowCompletionOr<Value> get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_) ThrowCompletionOr<Value> get_option(GlobalObject& global_object, Object const& options, PropertyKey const& property, OptionType type, StringView const (&values)[Size], OptionDefault const& default_)

View file

@ -280,58 +280,24 @@ ThrowCompletionOr<String> temporal_instant_to_string(GlobalObject& global_object
// 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant // 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
ThrowCompletionOr<Duration*> difference_temporal_instant(GlobalObject& global_object, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value) ThrowCompletionOr<Duration*> difference_temporal_instant(GlobalObject& global_object, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value)
{ {
auto& vm = global_object.vm();
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1. // 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1; i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalInstant(other). // 2. Set other to ? ToTemporalInstant(other).
auto* other = TRY(to_temporal_instant(global_object, other_value)); auto* other = TRY(to_temporal_instant(global_object, other_value));
// 3. Set options to ? GetOptionsObject(options). // 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », "nanosecond", "second").
auto const* options = TRY(get_options_object(global_object, options_value)); auto settings = TRY(get_difference_settings(global_object, operation, options_value, UnitGroup::Time, {}, { "nanosecond"sv }, "second"sv));
// 4. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", time, "nanosecond"). // 4. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Time, { "nanosecond"sv })); auto* rounded_ns = difference_instant(global_object, instant.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.rounding_mode);
// 5. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit). // 5. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 2 × nsMaxInstant.
auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit);
// 6. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", time, "auto"). // 6. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, settings.[[LargestUnit]]).
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Time, { "auto"sv })); auto result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), settings.largest_unit));
// 7. If largestUnit is "auto", set largestUnit to defaultLargestUnit. // 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
if (largest_unit == "auto"sv)
largest_unit = default_largest_unit;
// 8. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 10. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 11. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, *maximum, false));
// 13. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
auto* rounded_ns = difference_instant(global_object, instant.nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
// 14. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 2 × nsMaxInstant.
// 15. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, largestUnit).
auto result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), *largest_unit));
// 16. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds)); return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
} }

View file

@ -507,50 +507,21 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_date(GlobalObject& global
if (!TRY(calendar_equals(global_object, temporal_date.calendar(), other->calendar()))) if (!TRY(calendar_equals(global_object, temporal_date.calendar(), other->calendar())))
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars); return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
// 4. Set options to ? GetOptionsObject(options). // 4. Let settings be ? GetDifferenceSettings(operation, options, date, « », "day", "day").
auto const* options = TRY(get_options_object(global_object, options_value)); auto settings = TRY(get_difference_settings(global_object, operation, options_value, UnitGroup::Date, {}, { "day"sv }, "day"sv));
// 5. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", date, "day"). // 5. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], settings.[[LargestUnit]]).
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Date, { "day"sv })); auto* until_options = TRY(merge_largest_unit_option(global_object, settings.options, settings.largest_unit));
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). // 6. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], temporalDate, other, untilOptions).
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
// 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
// 8. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
if (largest_unit == "auto"sv)
largest_unit = default_largest_unit;
// 9. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 11. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
// 13. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit).
auto* until_options = TRY(merge_largest_unit_option(global_object, *options, largest_unit.release_value()));
// 14. Let result be ? CalendarDateUntil(temporalDate.[[Calendar]], temporalDate, other, untilOptions).
auto* duration = TRY(calendar_date_until(global_object, temporal_date.calendar(), &temporal_date, other, *until_options)); auto* duration = TRY(calendar_date_until(global_object, temporal_date.calendar(), &temporal_date, other, *until_options));
auto result = DurationRecord { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 }; auto result = DurationRecord { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 };
// 15. If smallestUnit is not "day" or roundingIncrement ≠ 1, then // 7. If settings.[[SmallestUnit]] is not "day" or settings.[[RoundingIncrement]] ≠ 1, then
if (*smallest_unit != "day"sv || rounding_increment != 1) { if (settings.smallest_unit != "day"sv || settings.rounding_increment != 1) {
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, temporalDate)).[[DurationRecord]]. // a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], temporalDate)).[[DurationRecord]].
result = TRY(round_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, &temporal_date)).duration_record; result = TRY(round_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &temporal_date)).duration_record;
} }
// 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0). // 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0).

View file

@ -409,54 +409,23 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_date_time(GlobalObject& g
if (!TRY(calendar_equals(global_object, date_time.calendar(), other->calendar()))) if (!TRY(calendar_equals(global_object, date_time.calendar(), other->calendar())))
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars); return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
// 4. Set options to ? GetOptionsObject(options). // 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « », "nanosecond", "day").
auto const* options = TRY(get_options_object(global_object, options_value)); auto settings = TRY(get_difference_settings(global_object, operation, options_value, UnitGroup::DateTime, {}, { "nanosecond"sv }, "day"sv));
// 5. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", datetime, "nanosecond"). // 5. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], settings.[[LargestUnit]], settings.[[Options]]).
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::DateTime, { "nanosecond"sv })); auto diff = TRY(difference_iso_date_time(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time.calendar(), settings.largest_unit, settings.options));
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). // 6. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit);
// 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", datetime, "auto").
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::DateTime, { "auto"sv }));
// 8. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
if (largest_unit == "auto"sv)
largest_unit = default_largest_unit;
// 9. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 11. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 12. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
// 14. Let diff be ? DifferenceISODateTime(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[ISOHour]], dateTime.[[ISOMinute]], dateTime.[[ISOSecond]], dateTime.[[ISOMillisecond]], dateTime.[[ISOMicrosecond]], dateTime.[[ISONanosecond]], other.[[ISOYear]], other.[[ISOMonth]], other.[[ISODay]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], dateTime.[[Calendar]], largestUnit, options).
auto diff = TRY(difference_iso_date_time(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.iso_hour(), date_time.iso_minute(), date_time.iso_second(), date_time.iso_millisecond(), date_time.iso_microsecond(), date_time.iso_nanosecond(), other->iso_year(), other->iso_month(), other->iso_day(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), date_time.calendar(), *largest_unit, *options));
// 15. Let relativeTo be ! CreateTemporalDate(dateTime.[[ISOYear]], dateTime.[[ISOMonth]], dateTime.[[ISODay]], dateTime.[[Calendar]]).
auto* relative_to = MUST(create_temporal_date(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.calendar())); auto* relative_to = MUST(create_temporal_date(global_object, date_time.iso_year(), date_time.iso_month(), date_time.iso_day(), date_time.calendar()));
// 16. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, relativeTo)).[[DurationRecord]]. // 7. Let roundResult be (? RoundDuration(diff.[[Years]], diff.[[Months]], diff.[[Weeks]], diff.[[Days]], diff.[[Hours]], diff.[[Minutes]], diff.[[Seconds]], diff.[[Milliseconds]], diff.[[Microseconds]], diff.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], relativeTo)).[[DurationRecord]].
auto round_result = TRY(round_duration(global_object, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, relative_to)).duration_record; auto round_result = TRY(round_duration(global_object, diff.years, diff.months, diff.weeks, diff.days, diff.hours, diff.minutes, diff.seconds, diff.milliseconds, diff.microseconds, diff.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, relative_to)).duration_record;
// 17. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit). // 8. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], settings.[[LargestUnit]]).
auto result = MUST(balance_duration(global_object, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger::create_from((i64)round_result.nanoseconds), *largest_unit)); // FIXME: Narrowing conversion from 'double' to 'i64'
auto result = MUST(balance_duration(global_object, round_result.days, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger::create_from((i64)round_result.nanoseconds), settings.largest_unit));
// 18. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]). // 9. Return ! CreateTemporalDuration(sign × roundResult.[[Years]], sign × roundResult.[[Months]], sign × roundResult.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, sign * round_result.years, sign * round_result.months, sign * round_result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds)); return MUST(create_temporal_duration(global_object, sign * round_result.years, sign * round_result.months, sign * round_result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
} }

View file

@ -579,56 +579,25 @@ DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 micro
// 4.5.13 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime // 4.5.13 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime
ThrowCompletionOr<Duration*> difference_temporal_plain_time(GlobalObject& global_object, DifferenceOperation operation, PlainTime const& temporal_time, Value other_value, Value options_value) ThrowCompletionOr<Duration*> difference_temporal_plain_time(GlobalObject& global_object, DifferenceOperation operation, PlainTime const& temporal_time, Value other_value, Value options_value)
{ {
auto& vm = global_object.vm();
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1. // 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
i8 sign = operation == DifferenceOperation::Since ? -1 : 1; i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
// 2. Set other to ? ToTemporalTime(other). // 2. Set other to ? ToTemporalTime(other).
auto* other = TRY(to_temporal_time(global_object, other_value)); auto* other = TRY(to_temporal_time(global_object, other_value));
// 3. Set options to ? GetOptionsObject(options). // 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », "nanosecond", "hour").
auto const* options = TRY(get_options_object(global_object, options_value)); auto settings = TRY(get_difference_settings(global_object, operation, options_value, UnitGroup::Time, {}, { "nanosecond"sv }, "hour"sv));
// 4. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", time, "nanosecond"). // 4. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Time, { "nanosecond"sv }));
// 5. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", time, "auto").
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Time, { "auto"sv }));
// 6. If largestUnit is "auto", set largestUnit to "hour".
if (largest_unit == "auto"sv)
largest_unit = "hour"sv;
// 7. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 9. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 10. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// 11. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
// 12. Let result be ! DifferenceTime(temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]], other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]]).
auto result = difference_time(temporal_time.iso_hour(), temporal_time.iso_minute(), temporal_time.iso_second(), temporal_time.iso_millisecond(), temporal_time.iso_microsecond(), temporal_time.iso_nanosecond(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond()); auto result = difference_time(temporal_time.iso_hour(), temporal_time.iso_minute(), temporal_time.iso_second(), temporal_time.iso_millisecond(), temporal_time.iso_microsecond(), temporal_time.iso_nanosecond(), other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond());
// 13. Set result to (! RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]]. // 5. Set result to (! RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]])).[[DurationRecord]].
auto rounded_result = MUST(round_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, rounding_increment, *smallest_unit, rounding_mode)).duration_record; auto rounded_result = MUST(round_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode)).duration_record;
// 14. Set result to ! BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], largestUnit). // 6. Set result to ! BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], settings.[[LargestUnit]]).
result = MUST(balance_duration(global_object, 0, rounded_result.hours, rounded_result.minutes, rounded_result.seconds, rounded_result.milliseconds, rounded_result.microseconds, Crypto::SignedBigInteger { (i32)rounded_result.nanoseconds }, *largest_unit)); result = MUST(balance_duration(global_object, 0, rounded_result.hours, rounded_result.minutes, rounded_result.seconds, rounded_result.milliseconds, rounded_result.microseconds, Crypto::SignedBigInteger { (i32)rounded_result.nanoseconds }, settings.largest_unit));
// 15. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]). // 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds)); return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
} }

View file

@ -264,79 +264,45 @@ ThrowCompletionOr<Duration*> difference_temporal_plain_year_month(GlobalObject&
if (!TRY(calendar_equals(global_object, calendar, other->calendar()))) if (!TRY(calendar_equals(global_object, calendar, other->calendar())))
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars); return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
// 5. Set options to ? GetOptionsObject(options). // 5. Let settings be ? GetDifferenceSettings(operation, options, date, « "week", "day" », "month", "year").
auto const* options = TRY(get_options_object(global_object, options_value)); auto settings = TRY(get_difference_settings(global_object, operation, options_value, UnitGroup::Date, { "week"sv, "day"sv }, { "month"sv }, "year"sv));
// 6. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", date, "month"). // 6. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::Date, { "month"sv }));
// 7. If smallestUnit is "week" or "day", throw a RangeError exception.
if (smallest_unit == "week"sv || smallest_unit == "day"sv)
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *smallest_unit, "smallestUnit"sv);
// 8. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", date, "auto").
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::Date, { "auto"sv }));
// 9. If largestUnit is "week" or "day", throw a RangeError exception.
if (largest_unit == "week"sv || largest_unit == "day"sv)
return vm.throw_completion<RangeError>(global_object, ErrorType::OptionIsNotValidValue, *largest_unit, "largestUnit"sv);
// 10. If largestUnit is "auto", set largestUnit to "year".
if (largest_unit == "auto"sv)
largest_unit = "year"sv;
// 11. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 12. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 13. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 14. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false));
// 15. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »).
auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv })); auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv }));
// 16. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»). // 7. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»).
auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, Vector<StringView> {})); auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, Vector<StringView> {}));
// 17. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽). // 8. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽).
MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1))); MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1)));
// 18. Let otherDate be ? CalendarDateFromFields(calendar, otherFields). // 9. Let otherDate be ? CalendarDateFromFields(calendar, otherFields).
auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields)); auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields));
// 19. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»). // 10. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»).
auto* this_fields = TRY(prepare_temporal_fields(global_object, year_month, field_names, Vector<StringView> {})); auto* this_fields = TRY(prepare_temporal_fields(global_object, year_month, field_names, Vector<StringView> {}));
// 20. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽). // 11. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽).
MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1))); MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1)));
// 21. Let thisDate be ? CalendarDateFromFields(calendar, thisFields). // 12. Let thisDate be ? CalendarDateFromFields(calendar, thisFields).
auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields)); auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields));
// 22. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). // 13. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], settings.[[LargestUnit]]).
auto* until_options = TRY(merge_largest_unit_option(global_object, *options, *largest_unit)); auto* until_options = TRY(merge_largest_unit_option(global_object, settings.options, settings.largest_unit));
// 23. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions). // 14. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions).
auto* duration = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options)); auto* duration = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options));
auto result = DurationRecord { duration->years(), duration->months(), 0, 0, 0, 0, 0, 0, 0, 0 }; auto result = DurationRecord { duration->years(), duration->months(), 0, 0, 0, 0, 0, 0, 0, 0 };
// 24. If smallestUnit is not "month" or roundingIncrement ≠ 1, then // 15. If settings.[[SmallestUnit]] is not "month" or settings.[[RoundingIncrement]] ≠ 1, then
if (smallest_unit != "month"sv || rounding_increment != 1) { if (settings.smallest_unit != "month"sv || settings.rounding_increment != 1) {
// a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]]. // a. Set result to (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], thisDate)).[[DurationRecord]].
result = TRY(round_duration(global_object, result.years, result.months, 0, 0, 0, 0, 0, 0, 0, 0, rounding_increment, *smallest_unit, rounding_mode, this_date)).duration_record; result = TRY(round_duration(global_object, result.years, result.months, 0, 0, 0, 0, 0, 0, 0, 0, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, this_date)).duration_record;
} }
// 25. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). // 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0).
return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, 0, 0, 0, 0, 0, 0, 0, 0)); return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, 0, 0, 0, 0, 0, 0, 0, 0));
} }

View file

@ -568,74 +568,42 @@ ThrowCompletionOr<Duration*> difference_temporal_zoned_date_time(GlobalObject& g
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars); return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentCalendars);
} }
// 4. Set options to ? GetOptionsObject(options). // 4. Let settings be ? GetDifferenceSettings(operation, options, datetime, « », "nanosecond", "hour").
auto const* options = TRY(get_options_object(global_object, options_value)); auto settings = TRY(get_difference_settings(global_object, operation, options_value, UnitGroup::DateTime, {}, { "nanosecond"sv }, "hour"sv));
// 5. Let smallestUnit be ? GetTemporalUnit(options, "smallestUnit", datetime, "nanosecond"). // 5. If settings.[[LargestUnit]] is not one of "year", "month", "week", or "day", then
auto smallest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.smallestUnit, UnitGroup::DateTime, { "nanosecond"sv })); if (!settings.largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
// 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit). auto* difference_ns = difference_instant(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.rounding_mode);
auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit);
// 7. Let largestUnit be ? GetTemporalUnit(options, "largestUnit", datetime, "auto").
auto largest_unit = TRY(get_temporal_unit(global_object, *options, vm.names.largestUnit, UnitGroup::DateTime, { "auto"sv }));
// 8. If largestUnit is "auto", set largestUnit to defaultLargestUnit.
if (largest_unit == "auto"sv)
largest_unit = default_largest_unit;
// 9. If LargerOfTwoTemporalUnits(largestUnit, smallestUnit) is not largestUnit, throw a RangeError exception.
if (larger_of_two_temporal_units(*largest_unit, *smallest_unit) != largest_unit)
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalInvalidUnitRange, *smallest_unit, *largest_unit);
// 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc").
auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv));
// 11. If operation is since, then
if (operation == DifferenceOperation::Since) {
// a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode).
rounding_mode = negate_temporal_rounding_mode(rounding_mode);
}
// 12. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit).
auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit);
// 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false).
auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional<double>(maximum), false));
// 14. If largestUnit is not one of "year", "month", "week", or "day", then
if (!largest_unit->is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode).
auto* difference_ns = difference_instant(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode);
// b. Assert: The following steps cannot fail due to overflow in the Number domain because abs(differenceNs) ≤ 2 × nsMaxInstant. // b. Assert: The following steps cannot fail due to overflow in the Number domain because abs(differenceNs) ≤ 2 × nsMaxInstant.
// c. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit). // c. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, settings.[[LargestUnit]]).
auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *largest_unit)); auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), settings.largest_unit));
// d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign × balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign × balanceResult.[[Microseconds]], sign × balanceResult.[[Nanoseconds]]). // d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign × balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign × balanceResult.[[Microseconds]], sign × balanceResult.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * balance_result.hours, sign * balance_result.minutes, sign * balance_result.seconds, sign * balance_result.milliseconds, sign * balance_result.microseconds, sign * balance_result.nanoseconds)); return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, sign * balance_result.hours, sign * balance_result.minutes, sign * balance_result.seconds, sign * balance_result.milliseconds, sign * balance_result.microseconds, sign * balance_result.nanoseconds));
} }
// 15. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then // 6. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
if (!TRY(time_zone_equals(global_object, zoned_date_time.time_zone(), other->time_zone()))) { if (!TRY(time_zone_equals(global_object, zoned_date_time.time_zone(), other->time_zone()))) {
// a. Throw a RangeError exception. // a. Throw a RangeError exception.
return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentTimeZones); return vm.throw_completion<RangeError>(global_object, ErrorType::TemporalDifferentTimeZones);
} }
// 16. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). // 7. Let untilOptions be ? MergeLargestUnitOption(settings.[[Options]], settings.[[LargestUnit]]).
auto* until_options = TRY(merge_largest_unit_option(global_object, *options, *largest_unit)); auto* until_options = TRY(merge_largest_unit_option(global_object, settings.options, settings.largest_unit));
// 17. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], largestUnit, untilOptions). // 8. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], settings.[[LargestUnit]], untilOptions).
auto difference = TRY(difference_zoned_date_time(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), zoned_date_time.time_zone(), zoned_date_time.calendar(), *largest_unit, *until_options)); auto difference = TRY(difference_zoned_date_time(global_object, zoned_date_time.nanoseconds(), other->nanoseconds(), zoned_date_time.time_zone(), zoned_date_time.calendar(), settings.largest_unit, *until_options));
// 18. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode, zonedDateTime)).[[DurationRecord]]. // 9. Let roundResult be (? RoundDuration(difference.[[Years]], difference.[[Months]], difference.[[Weeks]], difference.[[Days]], difference.[[Hours]], difference.[[Minutes]], difference.[[Seconds]], difference.[[Milliseconds]], difference.[[Microseconds]], difference.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime)).[[DurationRecord]].
auto round_result = TRY(round_duration(global_object, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, rounding_increment, *smallest_unit, rounding_mode, &zoned_date_time)).duration_record; auto round_result = TRY(round_duration(global_object, difference.years, difference.months, difference.weeks, difference.days, difference.hours, difference.minutes, difference.seconds, difference.milliseconds, difference.microseconds, difference.nanoseconds, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &zoned_date_time)).duration_record;
// 19. Let result 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, zonedDateTime). // 10. Let result be ? AdjustRoundedDurationDays(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]], zonedDateTime).
auto result = TRY(adjust_rounded_duration_days(global_object, 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, &zoned_date_time)); auto result = TRY(adjust_rounded_duration_days(global_object, 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, settings.rounding_increment, settings.smallest_unit, settings.rounding_mode, &zoned_date_time));
// 20. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]). // 11. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds)); return MUST(create_temporal_duration(global_object, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
} }