Skip to content

Commit

Permalink
Polyfill: Implement rounding towards Big Bang in epoch time getters
Browse files Browse the repository at this point in the history
See #2423 for the issue and #2424 for the spec text change, which was
already adopted some time ago. I thought the reference code already worked
like this, but it does not.

Anba pointed this out in tc39/test262#3802 and
provided correct test262 tests for the feature.

Note that the big-integer library doesn't have a floor division function.
We roll our own. If the divisor or dividend is negative (but not both) and
there is a remainder, then we subtract 1 from the result (round towards
negative infinity instead of 0).

UPSTREAM_COMMIT=6e0ad2030170540084a4a400005cc2bc8c791bdb
  • Loading branch information
ptomato authored and justingrant committed Apr 25, 2023
1 parent 04f9195 commit 10b557f
Show file tree
Hide file tree
Showing 4 changed files with 16 additions and 13 deletions.
10 changes: 10 additions & 0 deletions lib/ecmascript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6268,6 +6268,8 @@ export function CompareISODate(y1: number, m1: number, d1: number, y2: number, m
return 0;
}

// Not abstract operations from the spec

function NonNegativeBigIntDivmod(x: JSBI, y: JSBI) {
let { quotient, remainder } = divmod(x, y);
if (JSBI.lessThan(remainder, ZERO)) {
Expand All @@ -6277,6 +6279,14 @@ function NonNegativeBigIntDivmod(x: JSBI, y: JSBI) {
return { quotient, remainder };
}

export function BigIntFloorDiv(left: JSBI, right: JSBI) {
const { quotient, remainder } = divmod(left, right);
if (!isZero(remainder) && !isNegativeJSBI(left) != !isNegativeJSBI(right)) {
return JSBI.subtract(quotient, ONE);
}
return quotient;
}

/** Divide two JSBIs, and return the result as a Number, including the remainder. */
export function BigIntDivideToNumber(dividend: JSBI, divisor: JSBI) {
const { quotient, remainder } = divmod(dividend, divisor);
Expand Down
6 changes: 3 additions & 3 deletions lib/instant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,17 @@ export class Instant implements Temporal.Instant {
get epochSeconds(): Return['epochSeconds'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const value = GetSlot(this, EPOCHNANOSECONDS);
return JSBI.toNumber(JSBI.divide(value, BILLION));
return JSBI.toNumber(ES.BigIntFloorDiv(value, BILLION));
}
get epochMilliseconds(): Return['epochMilliseconds'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const value = JSBI.BigInt(GetSlot(this, EPOCHNANOSECONDS));
return JSBI.toNumber(JSBI.divide(value, MILLION));
return JSBI.toNumber(ES.BigIntFloorDiv(value, MILLION));
}
get epochMicroseconds(): Return['epochMicroseconds'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const value = JSBI.BigInt(GetSlot(this, EPOCHNANOSECONDS));
return ES.ToBigIntExternal(JSBI.divide(value, THOUSAND));
return ES.ToBigIntExternal(ES.BigIntFloorDiv(value, THOUSAND));
}
get epochNanoseconds(): Return['epochNanoseconds'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
Expand Down
6 changes: 3 additions & 3 deletions lib/zoneddatetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,17 @@ export class ZonedDateTime implements Temporal.ZonedDateTime {
get epochSeconds(): Return['epochSeconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const value = GetSlot(this, EPOCHNANOSECONDS);
return JSBI.toNumber(JSBI.divide(value, BILLION));
return JSBI.toNumber(ES.BigIntFloorDiv(value, BILLION));
}
get epochMilliseconds(): Return['epochMilliseconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const value = GetSlot(this, EPOCHNANOSECONDS);
return JSBI.toNumber(JSBI.divide(value, MILLION));
return JSBI.toNumber(ES.BigIntFloorDiv(value, MILLION));
}
get epochMicroseconds(): Return['epochMicroseconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const value = GetSlot(this, EPOCHNANOSECONDS);
return ES.ToBigIntExternal(JSBI.divide(value, THOUSAND));
return ES.ToBigIntExternal(ES.BigIntFloorDiv(value, THOUSAND));
}
get epochNanoseconds(): Return['epochNanoseconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
Expand Down
7 changes: 0 additions & 7 deletions test/expected-failures-todo-migrated-code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js
built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js
built-ins/Temporal/Duration/prototype/total/order-of-operations.js
built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js
built-ins/Temporal/Instant/prototype/epochMicroseconds/basic.js
built-ins/Temporal/Instant/prototype/epochMilliseconds/basic.js
built-ins/Temporal/Instant/prototype/epochSeconds/basic.js
built-ins/Temporal/Instant/prototype/toString/order-of-operations.js
built-ins/Temporal/Instant/prototype/toString/timezone.js
built-ins/Temporal/Instant/prototype/toZonedDateTime/calendar-wrong-type.js
Expand Down Expand Up @@ -125,16 +122,12 @@ built-ins/Temporal/TimeZone/prototype/getInstantFor/argument-propertybag-calenda
built-ins/Temporal/TimeZone/prototype/getInstantFor/order-of-operations.js
built-ins/Temporal/TimeZone/prototype/getPlainDateTimeFor/calendar-wrong-type.js
built-ins/Temporal/TimeZone/prototype/getPossibleInstantsFor/argument-propertybag-calendar-wrong-type.js
built-ins/Temporal/TimeZone/prototype/toJSON/returns-identifier-slot.js
built-ins/Temporal/ZonedDateTime/calendar-wrong-type.js
built-ins/Temporal/ZonedDateTime/compare/argument-propertybag-calendar-wrong-type.js
built-ins/Temporal/ZonedDateTime/compare/order-of-operations.js
built-ins/Temporal/ZonedDateTime/from/argument-propertybag-calendar-wrong-type.js
built-ins/Temporal/ZonedDateTime/from/order-of-operations.js
built-ins/Temporal/ZonedDateTime/prototype/add/order-of-operations.js
built-ins/Temporal/ZonedDateTime/prototype/epochMicroseconds/basic.js
built-ins/Temporal/ZonedDateTime/prototype/epochMilliseconds/basic.js
built-ins/Temporal/ZonedDateTime/prototype/epochSeconds/basic.js
built-ins/Temporal/ZonedDateTime/prototype/equals/argument-propertybag-calendar-wrong-type.js
built-ins/Temporal/ZonedDateTime/prototype/equals/order-of-operations.js
built-ins/Temporal/ZonedDateTime/prototype/since/argument-propertybag-calendar-wrong-type.js
Expand Down

0 comments on commit 10b557f

Please sign in to comment.