[Temporal] Fix Instant rounding

Sync with 2210 and 2240
https://github.com/tc39/proposal-temporal/pull/2210
https://github.com/tc39/proposal-temporal/pull/2400

Add AO: RoundNumberToIncrementAsIfPositive
Change AO parameter: DifferenceInstant

Spec:
https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrementasifpositive
https://tc39.es/proposal-temporal/#sec-temporal-roundtemporalinstant
https://tc39.es/proposal-temporal/#sec-temporal-differenceinstant
https://tc39.es/proposal-temporal/#sec-temporal-addduration
https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalinstant
https://tc39.es/proposal-temporal/#sec-temporal-differencetemporalzoneddatetime

Bug: v8:11544
Change-Id: I6b613bd19014d770852b9ba587278e714f9ac110
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3857451
Reviewed-by: Adam Klein <adamk@chromium.org>
Commit-Queue: Frank Tang <ftang@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83204}
This commit is contained in:
Frank Tang 2022-09-14 12:00:05 -07:00 committed by V8 LUCI CQ
parent d7fcbba80e
commit c8b4b66fe9
2 changed files with 98 additions and 94 deletions

View File

@ -6513,10 +6513,12 @@ Handle<BigInt> RoundTemporalInstant(Isolate* isolate, Handle<BigInt> ns,
RoundingMode rounding_mode); RoundingMode rounding_mode);
// #sec-temporal-differenceinstant // #sec-temporal-differenceinstant
Handle<BigInt> DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1, TimeDurationRecord DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1,
Handle<BigInt> ns2, double rounding_increment, Handle<BigInt> ns2,
Unit smallest_unit, double rounding_increment,
RoundingMode rounding_mode); Unit smallest_unit, Unit largest_unit,
RoundingMode rounding_mode,
const char* method_name);
// #sec-temporal-differencezoneddatetime // #sec-temporal-differencezoneddatetime
Maybe<DurationRecord> DifferenceZonedDateTime( Maybe<DurationRecord> DifferenceZonedDateTime(
@ -8376,12 +8378,6 @@ Maybe<DurationRecord> DifferenceZonedDateTime(
.ToChecked()); .ToChecked());
} }
// #sec-temporal-differenceinstant
Handle<BigInt> DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1,
Handle<BigInt> ns2, double rounding_increment,
Unit smallest_unit,
RoundingMode rounding_mode);
Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1, Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1,
const DurationRecord& dur2, const DurationRecord& dur2,
Handle<Object> relative_to_obj, Handle<Object> relative_to_obj,
@ -8548,17 +8544,13 @@ Maybe<DurationRecord> AddDuration(Isolate* isolate, const DurationRecord& dur1,
// 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 == Unit::kYear || largest_unit == Unit::kMonth || if (!(largest_unit == Unit::kYear || largest_unit == Unit::kMonth ||
largest_unit == Unit::kWeek || largest_unit == Unit::kDay)) { largest_unit == Unit::kWeek || largest_unit == Unit::kDay)) {
// i. Let diffNs be ! DifferenceInstant(relativeTo.[[Nanoseconds]], endNs, // a. Let result be ! DifferenceInstant(relativeTo.[[Nanoseconds]], endNs,
// 1, "nanosecond", "halfExpand"). // 1, *"nanosecond"*, largestUnit, *"halfExpand"*).
Handle<BigInt> diff_ns = DifferenceInstant(
isolate, handle(relative_to->nanoseconds(), isolate), end_ns, 1,
Unit::kNanosecond, RoundingMode::kHalfExpand);
// ii. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, diffNs,
// largestUnit).
result.time_duration = result.time_duration =
BalanceDuration(isolate, largest_unit, diff_ns, method_name) DifferenceInstant(isolate, handle(relative_to->nanoseconds(), isolate),
.ToChecked(); end_ns, 1, Unit::kNanosecond, largest_unit,
// d. Return ! CreateDurationRecord(0, 0, 0, 0, result.[[Hours]], RoundingMode::kHalfExpand, method_name);
// b. Return ! CreateDurationRecord(0, 0, 0, 0, result.[[Hours]],
// result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]], // result.[[Minutes]], result.[[Seconds]], result.[[Milliseconds]],
// result.[[Microseconds]], result.[[Nanoseconds]]). // result.[[Microseconds]], result.[[Nanoseconds]]).
result.time_duration.days = 0; result.time_duration.days = 0;
@ -15338,11 +15330,13 @@ Handle<BigInt> ApplyUnsignedRoundingMode(
// 7. Let d2 be r2 x. // 7. Let d2 be r2 x.
Handle<BigInt> dd2 = BigInt::Subtract(isolate, rr2, num).ToHandleChecked(); Handle<BigInt> dd2 = BigInt::Subtract(isolate, rr2, num).ToHandleChecked();
// 8. If d1 < d2, return r1. // 8. If d1 < d2, return r1.
if (BigInt::CompareToBigInt(dd1, dd2) == ComparisonResult::kLessThan) if (BigInt::CompareToBigInt(dd1, dd2) == ComparisonResult::kLessThan) {
return r1; return r1;
}
// 9. If d2 < d1, return r2. // 9. If d2 < d1, return r2.
if (BigInt::CompareToBigInt(dd2, dd1) == ComparisonResult::kLessThan) if (BigInt::CompareToBigInt(dd2, dd1) == ComparisonResult::kLessThan) {
return r2; return r2;
}
// 10. Assert: d1 is equal to d2. // 10. Assert: d1 is equal to d2.
DCHECK_EQ(BigInt::CompareToBigInt(dd1, dd2), ComparisonResult::kEqual); DCHECK_EQ(BigInt::CompareToBigInt(dd1, dd2), ComparisonResult::kEqual);
// 11. If unsignedRoundingMode is half-zero, return r1. // 11. If unsignedRoundingMode is half-zero, return r1.
@ -15402,48 +15396,43 @@ double RoundNumberToIncrement(Isolate* isolate, double x, double increment,
return rounded * increment; return rounded * increment;
} }
// For the case that x and return are BigInt. // #sec-temporal-roundnumbertoincrementasifpositive
Handle<BigInt> RoundNumberToIncrement(Isolate* isolate, Handle<BigInt> x, Handle<BigInt> RoundNumberToIncrementAsIfPositive(Isolate* isolate,
double increment, Handle<BigInt> x,
RoundingMode rounding_mode) { double increment,
RoundingMode rounding_mode) {
TEMPORAL_ENTER_FUNC(); TEMPORAL_ENTER_FUNC();
// 1. Let quotient be x / increment. // 1. Let quotient be x / increment.
bool is_negative; // 2. Let unsignedRoundingMode be GetUnsignedRoundingMode(roundingMode,
// 2. If quotient < 0, then // false).
if (x->IsNegative() != (increment < 0)) {
// a. Let isNegative be true.
is_negative = true;
// b. Set quotient to -quotient.
x = BigInt::UnaryMinus(isolate, x);
// 3. Else,
} else {
// a. Let isNegative be false.
is_negative = false;
}
// 4. Let unsignedRoundingMode be GetUnsignedRoundingMode(roundingMode,
// isNegative).
UnsignedRoundingMode unsigned_rounding_mode = UnsignedRoundingMode unsigned_rounding_mode =
GetUnsignedRoundingMode(rounding_mode, is_negative); GetUnsignedRoundingMode(rounding_mode, false);
// 5. Let r1 be the largest integer such that r1 ≤ quotient.
Handle<BigInt> increment_bigint = Handle<BigInt> increment_bigint =
BigInt::FromNumber(isolate, isolate->factory()->NewNumber(increment)) BigInt::FromNumber(isolate, isolate->factory()->NewNumber(increment))
.ToHandleChecked(); .ToHandleChecked();
// 3. Let r1 be the largest integer such that r1 ≤ quotient.
Handle<BigInt> r1 = Handle<BigInt> r1 =
BigInt::Divide(isolate, x, increment_bigint).ToHandleChecked(); BigInt::Divide(isolate, x, increment_bigint).ToHandleChecked();
// 6. Let r2 be the smallest integer such that r2 > quotient.
// Adjust for negative quotient.
if (r1->IsNegative() && BigInt::Remainder(isolate, x, increment_bigint)
.ToHandleChecked()
->ToBoolean()) {
r1 = BigInt::Decrement(isolate, r1).ToHandleChecked();
}
// 4. Let r2 be the smallest integer such that r2 > quotient.
Handle<BigInt> r2 = BigInt::Increment(isolate, r1).ToHandleChecked(); Handle<BigInt> r2 = BigInt::Increment(isolate, r1).ToHandleChecked();
// 7. Let rounded be ApplyUnsignedRoundingMode(quotient, r1, r2, // 5. Let rounded be ApplyUnsignedRoundingMode(quotient, r1, r2,
// unsignedRoundingMode). // unsignedRoundingMode).
Handle<BigInt> rounded = ApplyUnsignedRoundingMode( Handle<BigInt> rounded = ApplyUnsignedRoundingMode(
isolate, x, increment_bigint, r1, r2, unsigned_rounding_mode); isolate, x, increment_bigint, r1, r2, unsigned_rounding_mode);
// 8. If isNegative is true, set rounded to -rounded. // 6. Return rounded × increment.
if (is_negative) { Handle<BigInt> result =
rounded = BigInt::UnaryMinus(isolate, rounded); BigInt::Multiply(isolate, rounded, increment_bigint).ToHandleChecked();
} return result;
// 9. Return rounded × increment.
return BigInt::Multiply(isolate, rounded, increment_bigint).ToHandleChecked();
} }
DateTimeRecordCommon RoundTime(Isolate* isolate, const TimeRecordCommon& time, DateTimeRecordCommon RoundTime(Isolate* isolate, const TimeRecordCommon& time,
@ -17271,11 +17260,6 @@ MaybeHandle<JSTemporalZonedDateTime> JSTemporalZonedDateTime::Subtract(
namespace { namespace {
Handle<BigInt> DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1,
Handle<BigInt> ns2, double rounding_increment,
Unit smallest_unit,
RoundingMode rounding_mode);
// #sec-temporal-differencetemporalzoneddatetime // #sec-temporal-differencetemporalzoneddatetime
MaybeHandle<JSTemporalDuration> DifferenceTemporalZonedDateTime( MaybeHandle<JSTemporalDuration> DifferenceTemporalZonedDateTime(
Isolate* isolate, TimePreposition operation, Isolate* isolate, TimePreposition operation,
@ -17318,21 +17302,15 @@ MaybeHandle<JSTemporalDuration> DifferenceTemporalZonedDateTime(
settings.largest_unit != Unit::kMonth && settings.largest_unit != Unit::kMonth &&
settings.largest_unit != Unit::kWeek && settings.largest_unit != Unit::kWeek &&
settings.largest_unit != Unit::kDay) { settings.largest_unit != Unit::kDay) {
// a. Let differenceNs be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]], // 1. Let result be ! DifferenceInstant(zonedDateTime.[[Nanoseconds]],
// other.[[Nanoseconds]], settings.[[RoundingIncrement]], // other.[[Nanoseconds]], settings.[[RoundingIncrement]],
// settings.[[SmallestUnit]], settings.[[RoundingMode]]). // settings.[[SmallestUnit]], settings.[[LargestUnit]],
Handle<BigInt> difference_ns = DifferenceInstant( // settings.[[RoundingMode]]).
TimeDurationRecord balance_result = DifferenceInstant(
isolate, handle(zoned_date_time->nanoseconds(), isolate), isolate, handle(zoned_date_time->nanoseconds(), isolate),
handle(other->nanoseconds(), isolate), settings.rounding_increment, handle(other->nanoseconds(), isolate), settings.rounding_increment,
settings.smallest_unit, settings.rounding_mode); settings.smallest_unit, settings.largest_unit, settings.rounding_mode,
// b. Assert: The following steps cannot fail due to overflow in the Number method_name);
// domain because abs(differenceNs) ≤ 2 × nsMaxInstant. c. Let balanceResult
// be ! BalanceDuration(0, 0, 0, 0, 0, 0, differenceNs,
// settings.[[LargestUnit]]).
TimeDurationRecord balance_result =
BalanceDuration(isolate, settings.largest_unit, difference_ns,
method_name)
.ToChecked();
// d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × // d. Return ! CreateTemporalDuration(0, 0, 0, 0, sign ×
// balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign × // balanceResult.[[Hours]], sign × balanceResult.[[Minutes]], sign ×
// balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign × // balanceResult.[[Seconds]], sign × balanceResult.[[Milliseconds]], sign ×
@ -17985,8 +17963,10 @@ Handle<BigInt> RoundTemporalInstant(Isolate* isolate, Handle<BigInt> ns,
default: default:
UNREACHABLE(); UNREACHABLE();
} }
// 8. Return ! RoundNumberToIncrement((ns), incrementNs, roundingMode). // 8. Return ! RoundNumberToIncrementAsIfPositive((ns), incrementNs,
return RoundNumberToIncrement(isolate, ns, increment_ns, rounding_mode); // roundingMode).
return RoundNumberToIncrementAsIfPositive(isolate, ns, increment_ns,
rounding_mode);
} }
} // namespace } // namespace
@ -18546,17 +18526,50 @@ Maybe<DifferenceSettings> GetDifferenceSettings(
} }
// #sec-temporal-differenceinstant // #sec-temporal-differenceinstant
Handle<BigInt> DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1, TimeDurationRecord DifferenceInstant(Isolate* isolate, Handle<BigInt> ns1,
Handle<BigInt> ns2, double rounding_increment, Handle<BigInt> ns2,
Unit smallest_unit, double rounding_increment,
RoundingMode rounding_mode) { Unit smallest_unit, Unit largest_unit,
RoundingMode rounding_mode,
const char* method_name) {
// 1. Assert: Type(ns1) is BigInt. // 1. Assert: Type(ns1) is BigInt.
// 2. Assert: Type(ns2) is BigInt. // 2. Assert: Type(ns2) is BigInt.
// 3. Return ! RoundTemporalInstant(ns2 - ns1, roundingIncrement, // 3. Assert: The following step cannot fail due to overflow in the Number
// smallestUnit, roundingMode). // domain because abs(ns2 - ns1) <= 2 x nsMaxInstant.
return RoundTemporalInstant(
isolate, BigInt::Subtract(isolate, ns2, ns1).ToHandleChecked(), // 4. Let roundResult be ! RoundDuration(0, 0, 0, 0, 0, 0, 0, 0, 0, ns2 - ns1,
rounding_increment, smallest_unit, rounding_mode); // roundingIncrement, smallestUnit, roundingMode).[[DurationRecord]].
Handle<BigInt> diff = BigInt::Subtract(isolate, ns2, ns1).ToHandleChecked();
// Note: Since diff could be very big and over the precision of double can
// hold, break diff into diff_hours and diff_nanoseconds before pass into
// RoundDuration.
Handle<BigInt> nanoseconds_in_a_hour =
BigInt::FromUint64(isolate, 3600000000000);
double diff_hours =
BigInt::ToNumber(isolate,
BigInt::Divide(isolate, diff, nanoseconds_in_a_hour)
.ToHandleChecked())
->Number();
double diff_nanoseconds =
BigInt::ToNumber(isolate,
BigInt::Remainder(isolate, diff, nanoseconds_in_a_hour)
.ToHandleChecked())
->Number();
DurationRecordWithRemainder round_record =
RoundDuration(
isolate, {0, 0, 0, {0, diff_hours, 0, 0, 0, 0, diff_nanoseconds}},
rounding_increment, smallest_unit, rounding_mode, method_name)
.ToChecked();
// 5. Assert: roundResult.[[Days]] is 0.
DCHECK_EQ(0, round_record.record.time_duration.days);
// 6. Return ! BalanceDuration(0, roundResult.[[Hours]],
// roundResult.[[Minutes]], roundResult.[[Seconds]],
// roundResult.[[Milliseconds]], roundResult.[[Microseconds]],
// roundResult.[[Nanoseconds]], largestUnit).
return BalanceDuration(isolate, largest_unit,
isolate->factory()->undefined_value(),
round_record.record.time_duration, method_name)
.ToChecked();
} }
// #sec-temporal-differencetemporalinstant // #sec-temporal-differencetemporalinstant
@ -18581,22 +18594,16 @@ MaybeHandle<JSTemporalDuration> DifferenceTemporalInstant(
DisallowedUnitsInDifferenceSettings::kNone, DisallowedUnitsInDifferenceSettings::kNone,
Unit::kNanosecond, Unit::kSecond, method_name), Unit::kNanosecond, Unit::kSecond, method_name),
Handle<JSTemporalDuration>()); Handle<JSTemporalDuration>());
// 4. Let roundedNs be ! DifferenceInstant(instant.[[Nanoseconds]], // 4. Let result be ! DifferenceInstant(instant.[[Nanoseconds]],
// other.[[Nanoseconds]], settings.[[RoundingIncrement]], // other.[[Nanoseconds]], settings.[[RoundingIncrement]],
// settings.[[SmallestUnit]], settings.[[RoundingMode]]). // settings.[[SmallestUnit]], settings.[[LargestUnit]],
Handle<BigInt> rounded_ns = DifferenceInstant( // settings.[[RoundingMode]]).
TimeDurationRecord result = DifferenceInstant(
isolate, handle(instant->nanoseconds(), isolate), isolate, handle(instant->nanoseconds(), isolate),
handle(other->nanoseconds(), isolate), settings.rounding_increment, handle(other->nanoseconds(), isolate), settings.rounding_increment,
settings.smallest_unit, settings.rounding_mode); settings.smallest_unit, settings.largest_unit, settings.rounding_mode,
// 5. Assert: The following steps cannot fail due to overflow in the Number method_name);
// domain because abs(roundedNs) ≤ 2 × nsMaxInstant. // 5. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]],
// 6. Let result be ! BalanceDuration(0, 0, 0, 0, 0, 0, roundedNs,
// settings.[[LargestUnit]]).
TimeDurationRecord result =
BalanceDuration(isolate, settings.largest_unit, rounded_ns, method_name)
.ToChecked();
// 7. Return ! CreateTemporalDuration(0, 0, 0, 0, sign × result.[[Hours]],
// sign × result.[[Minutes]], sign × result.[[Seconds]], sign × // sign × result.[[Minutes]], sign × result.[[Seconds]], sign ×
// result.[[Milliseconds]], sign × result.[[Microseconds]], sign × // result.[[Milliseconds]], sign × result.[[Microseconds]], sign ×
// result.[[Nanoseconds]]). // result.[[Nanoseconds]]).

View File

@ -453,13 +453,10 @@
'intl402/Temporal/Duration/prototype/total/relativeto-string-datetime': [FAIL], 'intl402/Temporal/Duration/prototype/total/relativeto-string-datetime': [FAIL],
'intl402/Temporal/PlainYearMonth/from/argument-object': [FAIL], 'intl402/Temporal/PlainYearMonth/from/argument-object': [FAIL],
'built-ins/Temporal/Instant/prototype/round/rounding-direction': [FAIL],
'built-ins/Temporal/Instant/prototype/toString/rounding-direction': [FAIL],
'built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil-basic': [FAIL], 'built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-ceil-basic': [FAIL],
'built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor-basic': [FAIL], 'built-ins/Temporal/PlainDateTime/prototype/since/roundingmode-floor-basic': [FAIL],
'built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil-basic': [FAIL], 'built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-ceil-basic': [FAIL],
'built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor-basic': [FAIL], 'built-ins/Temporal/PlainDateTime/prototype/until/roundingmode-floor-basic': [FAIL],
'built-ins/Temporal/ZonedDateTime/prototype/toString/rounding-direction': [FAIL],
'intl402/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar': [FAIL], 'intl402/Temporal/PlainDateTime/prototype/withPlainDate/argument-string-calendar': [FAIL],
# https://github.com/tc39/test262/issues/3553 # https://github.com/tc39/test262/issues/3553