mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 00:47:45 +00:00
LibJS: Update DifferenceInstant and its callers to latest spec
This is a bit tangled in that updating these functions involves a slew of other spec changes. However those spec updates fix a bunch of rounding issues, fixing 32 test cases. Diff Tests: +32 ✅ -32 ❌
This commit is contained in:
parent
c4f37c1bfa
commit
0ed352e44e
4 changed files with 51 additions and 36 deletions
|
@ -1331,16 +1331,11 @@ ThrowCompletionOr<DurationRecord> add_duration(VM& vm, double years1, double mon
|
||||||
|
|
||||||
// 11. If largestUnit is not one of "year", "month", "week", or "day", then
|
// 11. 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)) {
|
if (!largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
||||||
// a. Let diffNs be ! DifferenceInstant(relativeTo.[[Nanoseconds]], endNs, 1, "nanosecond", "halfExpand").
|
// a. Let result be DifferenceInstant(zonedRelativeTo.[[Nanoseconds]], endNs, 1, "nanosecond", largestUnit, "halfExpand").
|
||||||
auto* diff_ns = difference_instant(vm, relative_to.nanoseconds(), *end_ns, 1, "nanosecond"sv, "halfExpand"sv);
|
auto result = difference_instant(vm, relative_to.nanoseconds(), *end_ns, 1, "nanosecond"sv, largest_unit, "halfExpand"sv);
|
||||||
|
|
||||||
// b. Assert: The following steps cannot fail due to overflow in the Number domain because abs(diffNs) ≤ 2 × nsMaxInstant.
|
// b. Return ! CreateDurationRecord(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
||||||
|
return create_duration_record(0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds);
|
||||||
// c. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, diffNs, largestUnit).
|
|
||||||
auto result = MUST(balance_duration(vm, 0, 0, 0, 0, 0, 0, diff_ns->big_integer(), largest_unit));
|
|
||||||
|
|
||||||
// d. Return ? CreateDurationRecord(0, 0, 0, 0, result.[[Hours]], result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], result.[[Microseconds]], result.[[Nanoseconds]]).
|
|
||||||
return create_duration_record(vm, 0, 0, 0, 0, result.hours, result.minutes, result.seconds, result.milliseconds, result.microseconds, result.nanoseconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12. Return ? DifferenceZonedDateTime(relativeTo.[[Nanoseconds]], endNs, timeZone, calendar, largestUnit, OrdinaryObjectCreate(null)).
|
// 12. Return ? DifferenceZonedDateTime(relativeTo.[[Nanoseconds]], endNs, timeZone, calendar, largestUnit, OrdinaryObjectCreate(null)).
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org>
|
||||||
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
|
||||||
|
* Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -186,14 +187,40 @@ ThrowCompletionOr<BigInt*> add_instant(VM& vm, BigInt const& epoch_nanoseconds,
|
||||||
return result.ptr();
|
return result.ptr();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.5.7 DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
|
// 8.5.6 DifferenceInstant ( ns1, ns2, roundingIncrement, smallestUnit, largestUnit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
|
||||||
BigInt* difference_instant(VM& vm, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode)
|
TimeDurationRecord difference_instant(VM& vm, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView largest_unit, StringView rounding_mode)
|
||||||
{
|
{
|
||||||
// 1. Assert: Type(ns1) is BigInt.
|
static const Crypto::UnsignedBigInteger BIGINT_ONE_THOUSAND { 1'000 };
|
||||||
// 2. Assert: Type(ns2) is BigInt.
|
|
||||||
|
|
||||||
// 3. Return ! RoundTemporalInstant(ns2 - ns1, roundingIncrement, smallestUnit, roundingMode).
|
// 1. Let difference be ℝ(ns2) - ℝ(ns1).
|
||||||
return round_temporal_instant(vm, BigInt::create(vm, nanoseconds2.big_integer().minus(nanoseconds1.big_integer())), rounding_increment, smallest_unit, rounding_mode);
|
auto difference = nanoseconds2.big_integer().minus(nanoseconds1.big_integer());
|
||||||
|
|
||||||
|
// 2. Let nanoseconds be remainder(difference, 1000).
|
||||||
|
auto nanoseconds = remainder(difference, BIGINT_ONE_THOUSAND);
|
||||||
|
|
||||||
|
// 3. Let microseconds be remainder(truncate(difference / 1000), 1000).
|
||||||
|
auto microseconds = remainder(difference.divided_by(BIGINT_ONE_THOUSAND).quotient, BIGINT_ONE_THOUSAND);
|
||||||
|
|
||||||
|
// 4. Let milliseconds be remainder(truncate(difference / 10^6), 1000).
|
||||||
|
auto milliseconds = remainder(difference.divided_by(Crypto::UnsignedBigInteger { 1'000'000 }).quotient, BIGINT_ONE_THOUSAND);
|
||||||
|
|
||||||
|
// 5. Let seconds be truncate(difference / 10^9).
|
||||||
|
auto seconds = difference.divided_by(Crypto::UnsignedBigInteger { 1'000'000'000 }).quotient;
|
||||||
|
|
||||||
|
// 6. If smallestUnit is "nanosecond" and roundingIncrement is 1, then
|
||||||
|
if (smallest_unit == "nanosecond"sv && rounding_increment == 1) {
|
||||||
|
// a. Return ! BalanceTimeDuration(0, 0, 0, seconds, milliseconds, microseconds, nanoseconds, largestUnit).
|
||||||
|
return MUST(balance_time_duration(vm, 0, 0, 0, seconds.to_double(), milliseconds.to_double(), microseconds.to_double(), nanoseconds, largest_unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Let roundResult be ! RoundDuration(0, 0, 0, 0, 0, 0, seconds, milliseconds, microseconds, nanoseconds, roundingIncrement, smallestUnit, roundingMode).
|
||||||
|
auto round_result = MUST(round_duration(vm, 0, 0, 0, 0, 0, 0, seconds.to_double(), milliseconds.to_double(), microseconds.to_double(), nanoseconds.to_double(), rounding_increment, smallest_unit, rounding_mode)).duration_record;
|
||||||
|
|
||||||
|
// 8. Assert: roundResult.[[Days]] is 0.
|
||||||
|
VERIFY(round_result.days == 0);
|
||||||
|
|
||||||
|
// 9. Return ! BalanceTimeDuration(0, roundResult.[[Hours]], roundResult.[[Minutes]], roundResult.[[Seconds]], roundResult.[[Milliseconds]], roundResult.[[Microseconds]], roundResult.[[Nanoseconds]], largestUnit).
|
||||||
|
return MUST(balance_time_duration(vm, 0, round_result.hours, round_result.minutes, round_result.seconds, round_result.milliseconds, round_result.microseconds, Crypto::SignedBigInteger { round_result.nanoseconds }, largest_unit));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
|
// 8.5.8 RoundTemporalInstant ( ns, increment, unit, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
|
||||||
|
@ -284,8 +311,8 @@ ThrowCompletionOr<String> temporal_instant_to_string(VM& vm, Instant& instant, V
|
||||||
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}", date_time_string, time_zone_string));
|
return TRY_OR_THROW_OOM(vm, String::formatted("{}{}", date_time_string, time_zone_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8.5.10 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
|
// 8.5.9 DifferenceTemporalInstant ( operation, instant, other, options ), https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
|
||||||
ThrowCompletionOr<Duration*> difference_temporal_instant(VM& vm, DifferenceOperation operation, Instant const& instant, Value other_value, Value options_value)
|
ThrowCompletionOr<Duration*> difference_temporal_instant(VM& vm, DifferenceOperation operation, Instant const& instant, Value other_value, Value options)
|
||||||
{
|
{
|
||||||
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
// 1. If operation is since, let sign be -1. Otherwise, let sign be 1.
|
||||||
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
i8 sign = operation == DifferenceOperation::Since ? -1 : 1;
|
||||||
|
@ -293,18 +320,16 @@ ThrowCompletionOr<Duration*> difference_temporal_instant(VM& vm, DifferenceOpera
|
||||||
// 2. Set other to ? ToTemporalInstant(other).
|
// 2. Set other to ? ToTemporalInstant(other).
|
||||||
auto* other = TRY(to_temporal_instant(vm, other_value));
|
auto* other = TRY(to_temporal_instant(vm, other_value));
|
||||||
|
|
||||||
// 3. Let settings be ? GetDifferenceSettings(operation, options, time, « », "nanosecond", "second").
|
// 3. Let resolvedOptions be ? SnapshotOwnProperties(? GetOptionsObject(options), null).
|
||||||
auto settings = TRY(get_difference_settings(vm, operation, options_value, UnitGroup::Time, {}, { "nanosecond"sv }, "second"sv));
|
auto resolved_options = TRY(TRY(get_options_object(vm, options))->snapshot_own_properties(vm, nullptr));
|
||||||
|
|
||||||
// 4. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
|
// 4. Let settings be ? GetDifferenceSettings(operation, resolvedOptions, time, « », "nanosecond", "second").
|
||||||
auto* rounded_ns = difference_instant(vm, instant.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.rounding_mode);
|
auto settings = TRY(get_difference_settings(vm, operation, resolved_options, UnitGroup::Time, {}, { "nanosecond"sv }, "second"sv));
|
||||||
|
|
||||||
// 5. Assert: The following steps cannot fail due to overflow in the Number domain because abs(roundedNs) ≤ 2 × nsMaxInstant.
|
// 5. Let result be DifferenceInstant(instant.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[LargestUnit]], settings.[[RoundingMode]]).
|
||||||
|
auto result = difference_instant(vm, instant.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode);
|
||||||
|
|
||||||
// 6. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs, settings.[[LargestUnit]]).
|
// 6. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
|
||||||
auto result = MUST(balance_duration(vm, 0, 0, 0, 0, 0, 0, rounded_ns->big_integer(), settings.largest_unit));
|
|
||||||
|
|
||||||
// 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
|
|
||||||
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
|
return MUST(create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ ThrowCompletionOr<Instant*> to_temporal_instant(VM&, Value item);
|
||||||
ThrowCompletionOr<BigInt*> parse_temporal_instant(VM&, StringView iso_string);
|
ThrowCompletionOr<BigInt*> parse_temporal_instant(VM&, StringView iso_string);
|
||||||
i32 compare_epoch_nanoseconds(BigInt const&, BigInt const&);
|
i32 compare_epoch_nanoseconds(BigInt const&, BigInt const&);
|
||||||
ThrowCompletionOr<BigInt*> add_instant(VM&, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
ThrowCompletionOr<BigInt*> add_instant(VM&, BigInt const& epoch_nanoseconds, double hours, double minutes, double seconds, double milliseconds, double microseconds, double nanoseconds);
|
||||||
BigInt* difference_instant(VM&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView rounding_mode);
|
TimeDurationRecord difference_instant(VM&, BigInt const& nanoseconds1, BigInt const& nanoseconds2, u64 rounding_increment, StringView smallest_unit, StringView largest_unit, StringView rounding_mode);
|
||||||
BigInt* round_temporal_instant(VM&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode);
|
BigInt* round_temporal_instant(VM&, BigInt const& nanoseconds, u64 increment, StringView unit, StringView rounding_mode);
|
||||||
ThrowCompletionOr<String> temporal_instant_to_string(VM&, Instant&, Value time_zone, Variant<StringView, u8> const& precision);
|
ThrowCompletionOr<String> temporal_instant_to_string(VM&, Instant&, Value time_zone, Variant<StringView, u8> const& precision);
|
||||||
ThrowCompletionOr<Duration*> difference_temporal_instant(VM&, DifferenceOperation, Instant const&, Value other, Value options);
|
ThrowCompletionOr<Duration*> difference_temporal_instant(VM&, DifferenceOperation, Instant const&, Value other, Value options);
|
||||||
|
|
|
@ -590,16 +590,11 @@ ThrowCompletionOr<Duration*> difference_temporal_zoned_date_time(VM& vm, Differe
|
||||||
|
|
||||||
// 5. If settings.[[LargestUnit]] is not one of "year", "month", "week", or "day", then
|
// 5. If settings.[[LargestUnit]] is not one of "year", "month", "week", or "day", then
|
||||||
if (!settings.largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
if (!settings.largest_unit.is_one_of("year"sv, "month"sv, "week"sv, "day"sv)) {
|
||||||
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[RoundingMode]]).
|
// a. Let result be DifferenceInstant(zonedDateTime.[[Nanoseconds]], other.[[Nanoseconds]], settings.[[RoundingIncrement]], settings.[[SmallestUnit]], settings.[[LargestUnit]], settings.[[RoundingMode]]).
|
||||||
auto* difference_ns = difference_instant(vm, zoned_date_time.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.rounding_mode);
|
auto result = difference_instant(vm, zoned_date_time.nanoseconds(), other->nanoseconds(), settings.rounding_increment, settings.smallest_unit, settings.largest_unit, settings.rounding_mode);
|
||||||
|
|
||||||
// b. Assert: The following steps cannot fail due to overflow in the Number domain because abs(differenceNs) ≤ 2 × nsMaxInstant.
|
// b. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]], sign × result.[[Minutes]], sign × result.[[Seconds]], sign × result.[[Milliseconds]], sign × result.[[Microseconds]], sign × result.[[Nanoseconds]]).
|
||||||
|
return create_temporal_duration(vm, 0, 0, 0, 0, sign * result.hours, sign * result.minutes, sign * result.seconds, sign * result.milliseconds, sign * result.microseconds, sign * result.nanoseconds);
|
||||||
// c. Let balanceResult be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs, settings.[[LargestUnit]]).
|
|
||||||
auto balance_result = MUST(balance_duration(vm, 0, 0, 0, 0, 0, 0, difference_ns->big_integer(), settings.largest_unit));
|
|
||||||
|
|
||||||
// d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign × balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign × balanceResult.[[Microseconds]], sign × balanceResult.[[Nanoseconds]]).
|
|
||||||
return MUST(create_temporal_duration(vm, 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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
|
// 6. If ? TimeZoneEquals(zonedDateTime.[[TimeZone]], other.[[TimeZone]]) is false, then
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue