diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index fd4880dea0..2e173b25f9 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -22,6 +22,11 @@ enum class ArithmeticOperation { Subtract, }; +enum class DifferenceOperation { + Since, + Until, +}; + enum class OptionType { Boolean, String, diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp index b169472a73..7eab596f06 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.cpp @@ -281,7 +281,69 @@ ThrowCompletionOr temporal_instant_to_string(GlobalObject& global_object return String::formatted("{}{}", date_time_string, time_zone_string); } -// 8.5.10 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant +// 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant +ThrowCompletionOr difference_temporal_instant(GlobalObject& global_object, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value) +{ + // 1. Set other to ? ToTemporalInstant(other). + auto* other = TRY(to_temporal_instant(global_object, other_value)); + + Instant const* first; + Instant const* second; + + // 2. If operation is until, then + if (operation == DifferenceOperation::Until) { + // a. Let first be instant. + first = &instant; + + // b. Let second be other. + second = other; + } + // 3. Else, + else { + // a. Let first be other. + first = other; + + // b. Let second be instant. + second = &instant; + } + + // 4. Set options to ? GetOptionsObject(options). + auto const* options = TRY(get_options_object(global_object, options_value)); + + // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond"). + auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv)); + + // 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit). + auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit); + + // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit). + auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, default_largest_unit)); + + // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). + TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); + + // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). + auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); + + // 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, *maximum, false)); + + // 12. Let roundedNs be ! DifferenceInstant(first.[[Nanoseconds]], second.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode). + auto* rounded_ns = difference_instant(global_object, first->nanoseconds(), second->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode); + + // 13. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 1.728 × 10^22. + + // 14. 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)); + + // 15. Return ! CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). + return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); +} + +// 8.5.11 AddDurationToOrSubtractDurationFromInstant ( operation, instant, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfrominstant ThrowCompletionOr add_duration_to_or_subtract_duration_from_instant(GlobalObject& global_object, ArithmeticOperation operation, Instant const& instant, Value temporal_duration_like) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h index 861bf55cd0..7a3800bc51 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/Instant.h @@ -49,6 +49,7 @@ ThrowCompletionOr add_instant(GlobalObject&, BigInt const& epoch_nanose BigInt* difference_instant(GlobalObject&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode); BigInt* round_temporal_instant(GlobalObject&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode); ThrowCompletionOr temporal_instant_to_string(GlobalObject&, Instant&, Value time_zone, Variant const& precision); +ThrowCompletionOr difference_temporal_instant(GlobalObject&, DifferenceOperation, Instant const&, Value other, Value options); ThrowCompletionOr add_duration_to_or_subtract_duration_from_instant(GlobalObject&, ArithmeticOperation, Instant const&, Value temporal_duration_like); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp index 85ccaaeadb..54c0ee6ba6 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/InstantPrototype.cpp @@ -147,89 +147,29 @@ JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::subtract) // 8.3.9 Temporal.Instant.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.until JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::until) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). auto* instant = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalInstant(other). - auto* other = TRY(to_temporal_instant(global_object, vm.argument(0))); - - // 4. Set options to ? GetOptionsObject(options). - auto const* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv)); - - // 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit); - - // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, default_largest_unit)); - - // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 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, *maximum, false)); - - // 12. 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); - - // 13. 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)); - - // 14. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); + // 3. Return ? DifferenceTemporalInstant(until, instant, other, options). + return TRY(difference_temporal_instant(global_object, DifferenceOperation::Until, *instant, other, options)); } // 8.3.10 Temporal.Instant.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.since JS_DEFINE_NATIVE_FUNCTION(InstantPrototype::since) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let instant be the this value. // 2. Perform ? RequireInternalSlot(instant, [[InitializedTemporalInstant]]). auto* instant = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalInstant(other). - auto* other = TRY(to_temporal_instant(global_object, vm.argument(0))); - - // 4. Set options to ? GetOptionsObject(options). - auto const* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv)); - - // 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("second", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("second"sv, *smallest_unit); - - // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, move(default_largest_unit))); - - // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 9. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 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, *maximum, false)); - - // 12. Let roundedNs be ! DifferenceInstant(other.[[Nanoseconds]], instant.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode). - auto* rounded_ns = difference_instant(global_object, other->nanoseconds(), instant->nanoseconds(), rounding_increment, *smallest_unit, rounding_mode); - - // 13. 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)); - - // 14. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); + // 3. Return ? DifferenceTemporalInstant(since, instant, other, options). + return TRY(difference_temporal_instant(global_object, DifferenceOperation::Since, *instant, other, options)); } // 8.3.11 Temporal.Instant.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.instant.prototype.round diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp index 9c09a99099..2f2ccbb55d 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.cpp @@ -486,4 +486,67 @@ i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2 return 0; } +// 3.5.11 DifferenceTemporalPlainDate ( operation, temporalDate, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindate +ThrowCompletionOr difference_temporal_plain_date(GlobalObject& global_object, DifferenceOperation operation, PlainDate& temporal_date, 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. + i8 sign = operation == DifferenceOperation::Since ? -1 : 1; + + // 2. Set other to ? ToTemporalDate(other). + auto* other = TRY(to_temporal_date(global_object, other_value)); + + // 3. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception. + if (!TRY(calendar_equals(global_object, temporal_date.calendar(), other->calendar()))) + return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); + + // 4. Set options to ? GetOptionsObject(options). + auto const* options = TRY(get_options_object(global_object, options_value)); + + // 5. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ». + auto disallowed_units = Vector { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }; + + // 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day"). + auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv)); + + // 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). + auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit); + + // 8. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit). + auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit)); + + // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). + TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_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 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 + if (*smallest_unit != "day"sv || 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]]. + 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; + } + + // 16. Return ! CreateTemporalDuration(sign × result.[[Years]], sign × result.[[Months]], sign × result.[[Weeks]], sign × result.[[Days]], 0, 0, 0, 0, 0, 0). + return TRY(create_temporal_duration(global_object, sign * result.years, sign * result.months, sign * result.weeks, sign * result.days, 0, 0, 0, 0, 0, 0)); +} + } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h index 383bca9a83..12c6e2c7ea 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDate.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace JS::Temporal { @@ -52,5 +53,6 @@ String pad_iso_year(i32 y); ThrowCompletionOr temporal_date_to_string(GlobalObject&, PlainDate&, StringView show_calendar); ThrowCompletionOr add_iso_date(GlobalObject&, i32 year, u8 month, u8 day, double years, double months, double weeks, double days, StringView overflow); i8 compare_iso_date(i32 year1, u8 month1, u8 day1, i32 year2, u8 month2, u8 day2); +ThrowCompletionOr difference_temporal_plain_date(GlobalObject&, DifferenceOperation, PlainDate&, Value other, Value options); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp index fa8fff4997..a70bd999c2 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDatePrototype.cpp @@ -441,116 +441,29 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::with_calendar) // 3.3.23 Temporal.PlainDate.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.until JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::until) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). auto* temporal_date = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalDate(other). - auto* other = TRY(to_temporal_date(global_object, vm.argument(0))); - - // 4. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception. - if (!TRY(calendar_equals(global_object, temporal_date->calendar(), other->calendar()))) - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - - // 5. Set options to ? GetOptionsObject(options). - auto const* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 6. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ». - Vector disallowed_units { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }; - - // 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv)); - - // 8. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit); - - // 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit)); - - // 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 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, move(*largest_unit))); - - // 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)); - - DurationRecord result { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 }; - - // 15. If smallestUnit is not "day" or roundingIncrement ≠ 1, then - if (*smallest_unit != "day"sv || 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]]. - 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; - } - - // 16. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], result.[[Weeks]], result.[[Days]], 0, 0, 0, 0, 0, 0). - return MUST(create_temporal_duration(global_object, result.years, result.months, result.weeks, result.days, 0, 0, 0, 0, 0, 0)); + // 3. Return ? DifferenceTemporalPlainDate(until, temporalDate, other, options). + return TRY(difference_temporal_plain_date(global_object, DifferenceOperation::Until, *temporal_date, other, options)); } // 3.3.24 Temporal.PlainDate.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.since JS_DEFINE_NATIVE_FUNCTION(PlainDatePrototype::since) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let temporalDate be the this value. // 2. Perform ? RequireInternalSlot(temporalDate, [[InitializedTemporalDate]]). auto* temporal_date = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalDate(other). - auto* other = TRY(to_temporal_date(global_object, vm.argument(0))); - - // 4. If ? CalendarEquals(temporalDate.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception. - if (!TRY(calendar_equals(global_object, temporal_date->calendar(), other->calendar()))) - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - - // 5. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 6. Let disallowedUnits be « "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ». - Vector disallowed_units { "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }; - - // 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "day"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "day"sv)); - - // 8. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit); - - // 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, default_largest_unit)); - - // 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 12. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode). - rounding_mode = negate_temporal_rounding_mode(rounding_mode); - - // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false). - auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false)); - - // 14. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). - auto* until_options = TRY(merge_largest_unit_option(global_object, options, move(*largest_unit))); - - // 15. 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)); - - DurationRecord result { duration->years(), duration->months(), duration->weeks(), duration->days(), 0, 0, 0, 0, 0, 0 }; - - // 16. If smallestUnit is not "day" or roundingIncrement ≠ 1, then - if (*smallest_unit != "day"sv || 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]]. - 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; - } - - // 17. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], -result.[[Weeks]], -result.[[Days]], 0, 0, 0, 0, 0, 0). - return TRY(create_temporal_duration(global_object, -result.years, -result.months, -result.weeks, -result.days, 0, 0, 0, 0, 0, 0)); + // 3. Return ? DifferenceTemporalPlainDate(since, temporalDate, other, options). + return TRY(difference_temporal_plain_date(global_object, DifferenceOperation::Since, *temporal_date, other, options)); } // 3.3.25 Temporal.PlainDate.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plaindate.prototype.equals diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp index e5199d29ca..6bd22b1bbe 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.cpp @@ -391,7 +391,68 @@ ThrowCompletionOr difference_iso_date_time(GlobalObject& global_ return create_duration_record(date_difference->years(), date_difference->months(), date_difference->weeks(), balance_result.days, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds); } -// 5.5.12 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaindatetime +// 5.5.12 DifferenceTemporalPlainDateTime ( operation, dateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaindatetime +ThrowCompletionOr difference_temporal_plain_date_time(GlobalObject& global_object, DifferenceOperation operation, PlainDateTime& date_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. + i8 sign = operation == DifferenceOperation::Since ? -1 : 1; + + // 2. Set other to ? ToTemporalDateTime(other). + auto* other = TRY(to_temporal_date_time(global_object, other_value)); + + // 3. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception. + if (!TRY(calendar_equals(global_object, date_time.calendar(), other->calendar()))) + return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); + + // 4. Set options to ? GetOptionsObject(options). + auto const* options = TRY(get_options_object(global_object, options_value)); + + // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond"). + auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv)); + + // 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). + auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit); + + // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit). + auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit)); + + // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). + TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_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(maximum), false)); + + // 13. 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)); + + // 14. 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())); + + // 15. 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]]. + 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; + + // 16. Let result be ? BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], 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)); + + // 17. 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)); +} + +// 5.5.13 AddDurationToOrSubtractDurationFromPlainDateTime ( operation, dateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaindatetime ThrowCompletionOr add_duration_to_or_subtract_duration_from_plain_date_time(GlobalObject& global_object, ArithmeticOperation operation, PlainDateTime& date_time, Value temporal_duration_like, Value options_value) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h index dd5d4d2f4e..e0362ca902 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTime.h @@ -74,6 +74,7 @@ i8 compare_iso_date_time(i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 ThrowCompletionOr add_date_time(GlobalObject&, i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options); ISODateTime round_iso_date_time(i32 year, u8 month, u8 day, u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional day_length = {}); ThrowCompletionOr difference_iso_date_time(GlobalObject&, i32 year1, u8 month1, u8 day1, u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, i32 year2, u8 month2, u8 day2, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2, Object& calendar, StringView largest_unit, Object const* options = nullptr); +ThrowCompletionOr difference_temporal_plain_date_time(GlobalObject&, DifferenceOperation, PlainDateTime&, Value other, Value options); ThrowCompletionOr add_duration_to_or_subtract_duration_from_plain_date_time(GlobalObject&, ArithmeticOperation, PlainDateTime&, Value temporal_duration_like, Value options_value); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp index e6901bc5f7..d864b2b6e3 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainDateTimePrototype.cpp @@ -489,112 +489,29 @@ JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::subtract) // 5.3.28 Temporal.PlainDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::until) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). auto* date_time = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalDateTime(other). - auto* other = TRY(to_temporal_date_time(global_object, vm.argument(0))); - - // 4. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception. - if (!TRY(calendar_equals(global_object, date_time->calendar(), other->calendar()))) - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - - // 5. Set options to ? GetOptionsObject(options). - auto const* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv)); - - // 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit); - - // 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit)); - - // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 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(maximum), false)); - - // 13. 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)); - - // 14. 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())); - - // 15. 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]]. - 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; - - // 16. Let result be ! BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], 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)); - - // 17. Return ? CreateTemporalDuration(roundResult.[[Years]], roundResult.[[Months]], roundResult.[[Weeks]], result.[[Days]], result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, round_result.years, round_result.months, round_result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); + // 3. Return ? DifferenceTemporalPlainDateTime(until, dateTime, other, options). + return TRY(difference_temporal_plain_date_time(global_object, DifferenceOperation::Until, *date_time, other, options)); } // 5.3.29 Temporal.PlainDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.since JS_DEFINE_NATIVE_FUNCTION(PlainDateTimePrototype::since) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let dateTime be the this value. // 2. Perform ? RequireInternalSlot(dateTime, [[InitializedTemporalDateTime]]). auto* date_time = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalDateTime(other). - auto* other = TRY(to_temporal_date_time(global_object, vm.argument(0))); - - // 4. If ? CalendarEquals(dateTime.[[Calendar]], other.[[Calendar]]) is false, throw a RangeError exception. - if (!TRY(calendar_equals(global_object, date_time->calendar(), other->calendar()))) - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - - // 5. Set options to ? GetOptionsObject(options). - auto const* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv)); - - // 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("day", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("day"sv, *smallest_unit); - - // 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit)); - - // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 11. 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(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())); - - // 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]]. - 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; - - // 17. Let result be ! BalanceDuration(roundResult.[[Days]], roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], 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)); - - // 18. Return ? CreateTemporalDuration(-roundResult.[[Years]], -roundResult.[[Months]], -roundResult.[[Weeks]], -result.[[Days]], -result.[[Hours]], -result.[[Minutes]], -result.[[Seconds]], -result.[[Milliseconds]], -result.[[Microseconds]], -result.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, -round_result.years, -round_result.months, -round_result.weeks, -result.days, -result.hours, -result.minutes, -result.seconds, -result.milliseconds, -result.microseconds, -result.nanoseconds)); + // 3. Return ? DifferenceTemporalPlainDateTime(since, dateTime, other, options). + return TRY(difference_temporal_plain_date_time(global_object, DifferenceOperation::Since, *date_time, other, options)); } // 5.3.30 Temporal.PlainDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaindatetime.prototype.round diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp index c25299c4c4..9d21fac1b2 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.cpp @@ -619,7 +619,68 @@ DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 micro return balance_time(hour, minute, second, millisecond, microsecond, result); } -// 4.5.14 AddDurationToOrSubtractDurationFromPlainTime ( operation, temporalTime, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaintime +// 4.5.14 DifferenceTemporalPlainTime ( operation, temporalTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplaintime +ThrowCompletionOr difference_temporal_plain_time(GlobalObject& global_object, DifferenceOperation operation, PlainTime const& temporal_time, Value other_value, Value options_value) +{ + // 1. If operation is since, let sign be -1. Otherwise, let sign be 1. + i8 sign = operation == DifferenceOperation::Since ? -1 : 1; + + PlainTime const* first; + PlainTime const* second; + + // 2. Set other to ? ToTemporalTime(other). + auto* other = TRY(to_temporal_time(global_object, other_value)); + + // FIXME: Copied from DifferenceTemporalInstant, also needed here (spec issue, see https://github.com/tc39/proposal-temporal/pull/2184) + if (operation == DifferenceOperation::Until) { + first = &temporal_time; + second = other; + } else { + first = other; + second = &temporal_time; + } + + // 3. Set options to ? GetOptionsObject(options). + auto const* options = TRY(get_options_object(global_object, options_value)); + + // 4. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond"). + auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv)); + + // 5. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour"). + auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv)); + + // 6. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). + TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); + + // 7. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). + auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); + + // 8. If operation is since, then + if (operation == DifferenceOperation::Since) { + // a. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode). + rounding_mode = negate_temporal_rounding_mode(rounding_mode); + } + + // 9. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit). + auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit); + + // 10. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false). + auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional(maximum), false)); + + // 11. Let result be ! DifferenceTime(other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]]). + auto result = difference_time(first->iso_hour(), first->iso_minute(), first->iso_second(), first->iso_millisecond(), first->iso_microsecond(), first->iso_nanosecond(), second->iso_hour(), second->iso_minute(), second->iso_second(), second->iso_millisecond(), second->iso_microsecond(), second->iso_nanosecond()); + + // 12. Set result to (? RoundDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]]. + auto rounded_result = TRY(round_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, rounding_increment, *smallest_unit, rounding_mode)).duration_record; + + // 13. Set result to ? BalanceDuration(0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]], largestUnit). + result = MUST(balance_duration(global_object, 0, sign * rounded_result.hours, sign * rounded_result.minutes, sign * rounded_result.seconds, sign * rounded_result.milliseconds, sign * rounded_result.microseconds, Crypto::SignedBigInteger { (i32)(sign * rounded_result.nanoseconds) }, *largest_unit)); + + // 14. Return ! CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). + return MUST(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); +} + +// 4.5.15 AddDurationToOrSubtractDurationFromPlainTime ( operation, temporalTime, temporalDurationLike ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoorsubtractdurationfromplaintime ThrowCompletionOr add_duration_to_or_subtract_duration_from_plain_time(GlobalObject& global_object, ArithmeticOperation operation, PlainTime const& temporal_time, Value temporal_duration_like) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h index fcf74a0ea2..aac33d1051 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTime.h @@ -106,6 +106,7 @@ String temporal_time_to_string(u8 hour, u8 minute, u8 second, u16 millisecond, u i8 compare_temporal_time(u8 hour1, u8 minute1, u8 second1, u16 millisecond1, u16 microsecond1, u16 nanosecond1, u8 hour2, u8 minute2, u8 second2, u16 millisecond2, u16 microsecond2, u16 nanosecond2); DaysAndTime add_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds); DaysAndTime round_time(u8 hour, u8 minute, u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, u64 increment, StringView unit, StringView rounding_mode, Optional day_length_ns = {}); +ThrowCompletionOr difference_temporal_plain_time(GlobalObject&, DifferenceOperation, PlainTime const&, Value other, Value options); ThrowCompletionOr add_duration_to_or_subtract_duration_from_plain_time(GlobalObject&, ArithmeticOperation, PlainTime const&, Value temporal_duration_like); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp index 6dc23a3de8..18b0da79ab 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainTimePrototype.cpp @@ -237,92 +237,31 @@ JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::with) // 4.3.13 Temporal.PlainTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.until JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::until) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]). auto* temporal_time = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalTime(other). - auto* other = TRY(to_temporal_time(global_object, vm.argument(0))); - - // 4. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv)); - - // 6. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour"). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv)); - - // 7. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 9. Let maximum be ! MaximumTemporalDurationRoundingIncrement(smallestUnit). - auto maximum = maximum_temporal_duration_rounding_increment(*smallest_unit); - - // 10. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, maximum, false). - auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, Optional(maximum), false)); - - // 11. 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()); - - // 12. Set result to (? RoundDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], roundingIncrement, smallestUnit, roundingMode)).[[DurationRecord]]. - auto rounded_result = TRY(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; - - // 13. Set result to ! BalanceDuration(0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]], 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)); - - // 14. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); + // 3. Return ? DifferenceTemporalPlainTime(since, temporalTime, other, options). + // FIXME: until, not since (spec issue, see https://github.com/tc39/proposal-temporal/pull/2183) + return TRY(difference_temporal_plain_time(global_object, DifferenceOperation::Until, *temporal_time, other, options)); } // 4.3.14 Temporal.PlainTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.since JS_DEFINE_NATIVE_FUNCTION(PlainTimePrototype::since) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let temporalTime be the this value. // 2. Perform ? RequireInternalSlot(temporalTime, [[InitializedTemporalTime]]). auto* temporal_time = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalTime(other). - auto* other = TRY(to_temporal_time(global_object, vm.argument(0))); - - // 4. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « "year", "month", "week", "day" », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "nanosecond"sv)); - - // 6. Let largestUnit be ? ToLargestTemporalUnit(options, « "year", "month", "week", "day" », "auto", "hour"). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, { "year"sv, "month"sv, "week"sv, "day"sv }, "auto"sv, "hour"sv)); - - // 7. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 8. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 9. 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(maximum), false)); - - // 12. Let result be ! DifferenceTime(other.[[ISOHour]], other.[[ISOMinute]], other.[[ISOSecond]], other.[[ISOMillisecond]], other.[[ISOMicrosecond]], other.[[ISONanosecond]], temporalTime.[[ISOHour]], temporalTime.[[ISOMinute]], temporalTime.[[ISOSecond]], temporalTime.[[ISOMillisecond]], temporalTime.[[ISOMicrosecond]], temporalTime.[[ISONanosecond]]). - auto result = difference_time(other->iso_hour(), other->iso_minute(), other->iso_second(), other->iso_millisecond(), other->iso_microsecond(), other->iso_nanosecond(), temporal_time->iso_hour(), temporal_time->iso_minute(), temporal_time->iso_second(), temporal_time->iso_millisecond(), temporal_time->iso_microsecond(), temporal_time->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]]. - auto rounded_result = TRY(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; - - // 14. Set result to ! BalanceDuration(0, -result.[[Hours]], -result.[[Minutes]], -result.[[Seconds]], -result.[[Milliseconds]], -result.[[Microseconds]], -result.[[Nanoseconds]], 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)); - - // 15. Return ? CreateTemporalDuration(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); + // 3. Return ? DifferenceTemporalPlainTime(until, temporalTime, other, options). + // FIXME: since, not until (spec issue, see https://github.com/tc39/proposal-temporal/pull/2183) + return TRY(difference_temporal_plain_time(global_object, DifferenceOperation::Since, *temporal_time, other, options)); } // 4.3.15 Temporal.PlainTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.plaintime.prototype.round diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp index 8f0c1ab0d1..e4330e9407 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.cpp @@ -257,7 +257,91 @@ ThrowCompletionOr temporal_year_month_to_string(GlobalObject& global_obj return String::formatted("{}{}", result, calendar_string); } -// 9.5.9 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-addtemporalplainyearmonth +// 9.5.9 DifferenceTemporalPlainYearMonth ( operation, yearMonth, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalplainyearmonth +ThrowCompletionOr difference_temporal_plain_year_month(GlobalObject& global_object, DifferenceOperation operation, PlainYearMonth& year_month, 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. + i8 sign = operation == DifferenceOperation::Since ? -1 : 1; + + // 2. Set other to ? ToTemporalYearMonth(other). + auto* other = TRY(to_temporal_year_month(global_object, other_value)); + + // 3. Let calendar be yearMonth.[[Calendar]]. + auto& calendar = year_month.calendar(); + + // 4. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception. + if (!TRY(calendar_equals(global_object, calendar, other->calendar()))) + return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); + + // 5. Set options to ? GetOptionsObject(options). + auto const* options = TRY(get_options_object(global_object, options_value)); + + // 6. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ». + auto disallowed_units = Vector { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }; + + // 7. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month"). + auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv)); + + // 8. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year"). + auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year"sv)); + + // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). + TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_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 fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »). + auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv })); + + // 14. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»). + auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {})); + + // 15. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽). + MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1))); + + // 16. Let otherDate be ? CalendarDateFromFields(calendar, otherFields). + auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields)); + + // 17. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»). + auto* this_fields = TRY(prepare_temporal_fields(global_object, year_month, field_names, {})); + + // 18. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽). + MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1))); + + // 19. Let thisDate be ? CalendarDateFromFields(calendar, thisFields). + auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields)); + + // 20. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). + auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit)); + + // 21. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions). + 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 }; + + // 22. If smallestUnit is not "month" or roundingIncrement ≠ 1, then + if (smallest_unit != "month"sv || 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]]. + 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; + } + + // 23. 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)); +} + +// 9.5.10 AddDurationToOrSubtractDurationFromPlainYearMonth ( operation, yearMonth, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-addtemporalplainyearmonth ThrowCompletionOr add_duration_to_or_subtract_duration_from_plain_year_month(GlobalObject& global_object, ArithmeticOperation operation, PlainYearMonth& year_month, Value temporal_duration_like, Value options_value) { auto& vm = global_object.vm(); diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h index f2e3750b5e..d0245dffc5 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonth.h @@ -48,6 +48,7 @@ ISOYearMonth balance_iso_year_month(double year, double month); ISOYearMonth constrain_iso_year_month(double year, double month); ThrowCompletionOr create_temporal_year_month(GlobalObject&, i32 iso_year, u8 iso_month, Object& calendar, u8 reference_iso_day, FunctionObject const* new_target = nullptr); ThrowCompletionOr temporal_year_month_to_string(GlobalObject&, PlainYearMonth&, StringView show_calendar); +ThrowCompletionOr difference_temporal_plain_year_month(GlobalObject&, DifferenceOperation, PlainYearMonth&, Value other, Value options); ThrowCompletionOr add_duration_to_or_subtract_duration_from_plain_year_month(GlobalObject&, ArithmeticOperation, PlainYearMonth&, Value temporal_duration_like, Value options_value); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp index cf436ab5f2..015644e2ce 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/PlainYearMonthPrototype.cpp @@ -267,160 +267,29 @@ JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::subtract) // 9.3.14 Temporal.PlainYearMonth.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.until JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::until) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let yearMonth be the this value. // 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]). auto* year_month = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalYearMonth(other). - auto* other = TRY(to_temporal_year_month(global_object, vm.argument(0))); - - // 4. Let calendar be yearMonth.[[Calendar]]. - auto& calendar = year_month->calendar(); - - // 5. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception. - if (!TRY(calendar_equals(global_object, calendar, other->calendar()))) - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - - // 6. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 7. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ». - auto disallowed_units = Vector { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }; - - // 8. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv)); - - // 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year"). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year")); - - // 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 12. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false). - auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false)); - - // 13. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »). - auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv })); - - // 14. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»). - auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {})); - - // 15. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽). - MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1))); - - // 16. Let otherDate be ? CalendarDateFromFields(calendar, otherFields). - auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields)); - - // 17. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»). - auto* this_fields = TRY(prepare_temporal_fields(global_object, *year_month, field_names, {})); - - // 18. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽). - MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1))); - - // 19. Let thisDate be ? CalendarDateFromFields(calendar, thisFields). - auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields)); - - // 20. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). - auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit)); - - // 21. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions). - auto* result = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options)); - - // 22. If smallestUnit is "month" and roundingIncrement = 1, then - if (smallest_unit == "month"sv && rounding_increment == 1) { - // a. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). - return MUST(create_temporal_duration(global_object, result->years(), result->months(), 0, 0, 0, 0, 0, 0, 0, 0)); - } - - // 23. Let result be (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]]. - auto round_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; - - // 24. Return ! CreateTemporalDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). - return MUST(create_temporal_duration(global_object, round_result.years, round_result.months, 0, 0, 0, 0, 0, 0, 0, 0)); + // 3. Return ? DifferenceTemporalPlainYearMonth(until, yearMonth, other, options). + return TRY(difference_temporal_plain_year_month(global_object, DifferenceOperation::Until, *year_month, other, options)); } // 9.3.15 Temporal.PlainYearMonth.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.since JS_DEFINE_NATIVE_FUNCTION(PlainYearMonthPrototype::since) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let yearMonth be the this value. // 2. Perform ? RequireInternalSlot(yearMonth, [[InitializedTemporalYearMonth]]). auto* year_month = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalYearMonth(other). - auto* other = TRY(to_temporal_year_month(global_object, vm.argument(0))); - - // 4. Let calendar be yearMonth.[[Calendar]]. - auto& calendar = year_month->calendar(); - - // 5. If ? CalendarEquals(calendar, other.[[Calendar]]) is false, throw a RangeError exception. - if (!TRY(calendar_equals(global_object, calendar, other->calendar()))) - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - - // 6. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 7. Let disallowedUnits be « "week", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond" ». - auto disallowed_units = Vector { "week"sv, "day"sv, "hour"sv, "minute"sv, "second"sv, "millisecond"sv, "microsecond"sv, "nanosecond"sv }; - - // 8. Let smallestUnit be ? ToSmallestTemporalUnit(options, disallowedUnits, "month"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, disallowed_units, "month"sv)); - - // 9. Let largestUnit be ? ToLargestTemporalUnit(options, disallowedUnits, "auto", "year"). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, disallowed_units, "auto"sv, "year")); - - // 10. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 11. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 12. Set roundingMode to ! NegateTemporalRoundingMode(roundingMode). - rounding_mode = negate_temporal_rounding_mode(rounding_mode); - - // 13. Let roundingIncrement be ? ToTemporalRoundingIncrement(options, undefined, false). - auto rounding_increment = TRY(to_temporal_rounding_increment(global_object, *options, {}, false)); - - // 14. Let fieldNames be ? CalendarFields(calendar, « "monthCode", "year" »). - auto field_names = TRY(calendar_fields(global_object, calendar, { "monthCode"sv, "year"sv })); - - // 15. Let otherFields be ? PrepareTemporalFields(other, fieldNames, «»). - auto* other_fields = TRY(prepare_temporal_fields(global_object, *other, field_names, {})); - - // 16. Perform ! CreateDataPropertyOrThrow(otherFields, "day", 1𝔽). - MUST(other_fields->create_data_property_or_throw(vm.names.day, Value(1))); - - // 17. Let otherDate be ? CalendarDateFromFields(calendar, otherFields). - auto* other_date = TRY(calendar_date_from_fields(global_object, calendar, *other_fields)); - - // 18. Let thisFields be ? PrepareTemporalFields(yearMonth, fieldNames, «»). - auto* this_fields = TRY(prepare_temporal_fields(global_object, *year_month, field_names, {})); - - // 19. Perform ! CreateDataPropertyOrThrow(thisFields, "day", 1𝔽). - MUST(this_fields->create_data_property_or_throw(vm.names.day, Value(1))); - - // 20. Let thisDate be ? CalendarDateFromFields(calendar, thisFields). - auto* this_date = TRY(calendar_date_from_fields(global_object, calendar, *this_fields)); - - // 21. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). - auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit)); - - // 22. Let result be ? CalendarDateUntil(calendar, thisDate, otherDate, untilOptions). - auto* result = TRY(calendar_date_until(global_object, calendar, this_date, other_date, *until_options)); - - // 23. If smallestUnit is "month" and roundingIncrement = 1, then - if (smallest_unit == "month"sv && rounding_increment == 1) { - // a. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). - return MUST(create_temporal_duration(global_object, -result->years(), -result->months(), 0, 0, 0, 0, 0, 0, 0, 0)); - } - - // 24. Let result be (? RoundDuration(result.[[Years]], result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0, roundingIncrement, smallestUnit, roundingMode, thisDate)).[[DurationRecord]]. - auto round_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; - - // 25. Return ! CreateTemporalDuration(-result.[[Years]], -result.[[Months]], 0, 0, 0, 0, 0, 0, 0, 0). - return MUST(create_temporal_duration(global_object, -round_result.years, -round_result.months, 0, 0, 0, 0, 0, 0, 0, 0)); + // 3. Return ? DifferenceTemporalPlainYearMonth(since, yearMonth, other, options). + return TRY(difference_temporal_plain_year_month(global_object, DifferenceOperation::Since, *year_month, other, options)); } // 9.3.16 Temporal.PlainYearMonth.prototype.equals ( other ), https://tc39.es/proposal-temporal/#sec-temporal.plainyearmonth.prototype.equals diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp index 4c16896799..74037236c0 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.cpp @@ -537,7 +537,90 @@ ThrowCompletionOr nanoseconds_to_days(GlobalObject& glo return NanosecondsToDaysResult { .days = days, .nanoseconds = move(nanoseconds), .day_length = fabs(day_length_ns.to_double()) }; } -// 6.5.8 AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime +// 6.5.8 DifferenceTemporalZonedDateTime ( operation, zonedDateTime, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalzoneddatetime +ThrowCompletionOr difference_temporal_zoned_date_time(GlobalObject& global_object, DifferenceOperation operation, ZonedDateTime& zoned_date_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. + i8 sign = operation == DifferenceOperation::Since ? -1 : 1; + + // 2. Set other to ? ToTemporalZonedDateTime(other). + auto* other = TRY(to_temporal_zoned_date_time(global_object, other_value)); + + // 3. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then + if (!TRY(calendar_equals(global_object, zoned_date_time.calendar(), other->calendar()))) { + // a. Throw a RangeError exception. + return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); + } + + // 4. Set options to ? GetOptionsObject(options). + auto const* options = TRY(get_options_object(global_object, options_value)); + + // 5. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond"). + auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv)); + + // 6. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit). + auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit); + + // 7. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit). + auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit)); + + // 8. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). + TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_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(maximum), false)); + + // 13. 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) ≤ 1.728 × 10^22. + + // c. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit). + auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *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]]). + 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)); + } + + // 14. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then + if (!TRY(time_zone_equals(global_object, zoned_date_time.time_zone(), other->time_zone()))) { + // a. Throw a RangeError exception. + return vm.throw_completion(global_object, ErrorType::TemporalDifferentTimeZones); + } + + // 15. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). + auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit)); + + // 16. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], 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)); + + // 17. 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]]. + 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; + + // 18. 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). + 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)); + + // 19. 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)); +} + +// 6.5.9 AddDurationToOrSubtractDurationFromZonedDateTime ( operation, zonedDateTime, temporalDurationLike, options ), https://tc39.es/proposal-temporal/#sec-temporal-adddurationtoOrsubtractdurationfromzoneddatetime ThrowCompletionOr add_duration_to_or_subtract_duration_from_zoned_date_time(GlobalObject& global_object, ArithmeticOperation operation, ZonedDateTime& zoned_date_time, Value temporal_duration_like, Value options_value) { // 1. If operation is subtract, let sign be -1. Otherwise, let sign be 1. diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h index 8a75f6efa3..978691f4aa 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTime.h @@ -57,6 +57,7 @@ ThrowCompletionOr temporal_zoned_date_time_to_string(GlobalObject&, Zone ThrowCompletionOr add_zoned_date_time(GlobalObject&, BigInt const& epoch_nanoseconds, Value time_zone, Object& calendar, double years, double months, double weeks, double days, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds, Object* options = nullptr); ThrowCompletionOr difference_zoned_date_time(GlobalObject&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, Object& time_zone, Object& calendar, StringView largest_unit, Object const* options = nullptr); ThrowCompletionOr nanoseconds_to_days(GlobalObject&, Crypto::SignedBigInteger nanoseconds, Value relative_to); +ThrowCompletionOr difference_temporal_zoned_date_time(GlobalObject&, DifferenceOperation, ZonedDateTime&, Value other, Value options); ThrowCompletionOr add_duration_to_or_subtract_duration_from_zoned_date_time(GlobalObject&, ArithmeticOperation, ZonedDateTime&, Value temporal_duration_like, Value options_value); } diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp index d7b9f61dd4..ff806a551b 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/ZonedDateTimePrototype.cpp @@ -923,152 +923,29 @@ JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::subtract) // 6.3.37 Temporal.ZonedDateTime.prototype.until ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.until JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::until) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]). auto* zoned_date_time = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalZonedDateTime(other). - auto* other = TRY(to_temporal_zoned_date_time(global_object, vm.argument(0))); - - // 4. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then - if (!TRY(calendar_equals(global_object, zoned_date_time->calendar(), other->calendar()))) { - // a. Throw a RangeError exception. - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - } - - // 5. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv)); - - // 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit); - - // 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit)); - - // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 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(maximum), false)); - - // 13. 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. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit). - auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *largest_unit)); - - // c. Return ? CreateTemporalDuration(0, 0, 0, 0, balanceResult.[[Hours]], balanceResult.[[Minutes]], balanceResult.[[Seconds]], balanceResult.[[Milliseconds]], balanceResult.[[Microseconds]], balanceResult.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, balance_result.hours, balance_result.minutes, balance_result.seconds, balance_result.milliseconds, balance_result.microseconds, balance_result.nanoseconds)); - } - - // 14. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then - if (!TRY(time_zone_equals(global_object, zoned_date_time->time_zone(), other->time_zone()))) { - // a. Throw a RangeError exception. - return vm.throw_completion(global_object, ErrorType::TemporalDifferentTimeZones); - } - - // 15. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). - auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit)); - - // 16. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], 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)); - - // 17. 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]]. - 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; - - // 18. 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). - 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)); - - // 19. 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(global_object, result.years, result.months, result.weeks, result.days, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds)); + // 3. Return ? DifferenceTemporalZonedDateTime(until, zonedDateTime, other, options). + return TRY(difference_temporal_zoned_date_time(global_object, DifferenceOperation::Until, *zoned_date_time, other, options)); } // 6.3.38 Temporal.ZonedDateTime.prototype.since ( other [ , options ] ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.since JS_DEFINE_NATIVE_FUNCTION(ZonedDateTimePrototype::since) { + auto other = vm.argument(0); + auto options = vm.argument(1); + // 1. Let zonedDateTime be the this value. // 2. Perform ? RequireInternalSlot(zonedDateTime, [[InitializedTemporalZonedDateTime]]). auto* zoned_date_time = TRY(typed_this_object(global_object)); - // 3. Set other to ? ToTemporalZonedDateTime(other). - auto* other = TRY(to_temporal_zoned_date_time(global_object, vm.argument(0))); - - // 4. If ? CalendarEquals(zonedDateTime.[[Calendar]], other.[[Calendar]]) is false, then - if (!TRY(calendar_equals(global_object, zoned_date_time->calendar(), other->calendar()))) { - // a. Throw a RangeError exception. - return vm.throw_completion(global_object, ErrorType::TemporalDifferentCalendars); - } - - // 5. Set options to ? GetOptionsObject(options). - auto* options = TRY(get_options_object(global_object, vm.argument(1))); - - // 6. Let smallestUnit be ? ToSmallestTemporalUnit(options, « », "nanosecond"). - auto smallest_unit = TRY(to_smallest_temporal_unit(global_object, *options, {}, "nanosecond"sv)); - - // 7. Let defaultLargestUnit be ! LargerOfTwoTemporalUnits("hour", smallestUnit). - auto default_largest_unit = larger_of_two_temporal_units("hour"sv, *smallest_unit); - - // 8. Let largestUnit be ? ToLargestTemporalUnit(options, « », "auto", defaultLargestUnit). - auto largest_unit = TRY(to_largest_temporal_unit(global_object, *options, {}, "auto"sv, default_largest_unit)); - - // 9. Perform ? ValidateTemporalUnitRange(largestUnit, smallestUnit). - TRY(validate_temporal_unit_range(global_object, *largest_unit, *smallest_unit)); - - // 10. Let roundingMode be ? ToTemporalRoundingMode(options, "trunc"). - auto rounding_mode = TRY(to_temporal_rounding_mode(global_object, *options, "trunc"sv)); - - // 11. 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(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. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, largestUnit). - auto balance_result = MUST(balance_duration(global_object, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), *largest_unit)); - - // c. Return ? CreateTemporalDuration(0, 0, 0, 0, -balanceResult.[[Hours]], -balanceResult.[[Minutes]], -balanceResult.[[Seconds]], -balanceResult.[[Milliseconds]], -balanceResult.[[Microseconds]], -balanceResult.[[Nanoseconds]]). - return TRY(create_temporal_duration(global_object, 0, 0, 0, 0, -balance_result.hours, -balance_result.minutes, -balance_result.seconds, -balance_result.milliseconds, -balance_result.microseconds, -balance_result.nanoseconds)); - } - - // 15. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then - if (!TRY(time_zone_equals(global_object, zoned_date_time->time_zone(), other->time_zone()))) { - // a. Throw a RangeError exception. - return vm.throw_completion(global_object, ErrorType::TemporalDifferentTimeZones); - } - - // 16. Let untilOptions be ? MergeLargestUnitOption(options, largestUnit). - auto* until_options = TRY(merge_largest_unit_option(global_object, options, *largest_unit)); - - // 17. Let difference be ? DifferenceZonedDateTime(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], zonedDateTime.[[TimeZone]], zonedDateTime.[[Calendar]], 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)); - - // 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]]. - 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; - - // 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). - 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)); - - // 20. 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(global_object, -result.years, -result.months, -result.weeks, -result.days, -result.hours, -result.minutes, -result.seconds, -result.milliseconds, -result.microseconds, -result.nanoseconds)); + // 3. Return ? DifferenceTemporalZonedDateTime(since, zonedDateTime, other, options). + return TRY(difference_temporal_zoned_date_time(global_object, DifferenceOperation::Since, *zoned_date_time, other, options)); } // 6.3.39 Temporal.ZonedDateTime.prototype.round ( roundTo ), https://tc39.es/proposal-temporal/#sec-temporal.zoneddatetime.prototype.round