Skip to content

Commit 44b00a3

Browse files
ptomatojustingrant
authored andcommitted
Normative: Require NanosecondsToDays remainder less than day length
When converting a number of nanoseconds to a number of days and a nanoseconds remainder, the remainder shouldn't be longer than the length of the last day. This could happen due to shenanigans in a custom time zone's getOffsetNanosecondsFor or getPossibleInstantsFor methods, or a custom calendar's dateAdd method. See: #2357 UPSTREAM_COMMIT=ac69b63a5904620d0271238f028b4cee068bfcad
1 parent 0b238cc commit 44b00a3

File tree

2 files changed

+15
-9
lines changed

2 files changed

+15
-9
lines changed

lib/ecmascript.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ function divmod(x: JSBI, y: JSBI): { quotient: JSBI; remainder: JSBI } {
237237
return { quotient, remainder };
238238
}
239239

240+
function isNegativeJSBI(value: JSBI): boolean {
241+
return JSBI.lessThan(value, ZERO);
242+
}
243+
244+
function signJSBI(value: JSBI): 1 | 0 | -1 {
245+
if (isZero(value)) return 0;
246+
if (isNegativeJSBI(value)) return -1;
247+
return 1;
248+
}
240249
function abs(x: JSBI): JSBI {
241250
if (JSBI.lessThan(x, ZERO)) return JSBI.multiply(x, NEGATIVE_ONE);
242251
return x;
@@ -3461,7 +3470,7 @@ function NanosecondsToDays(nanosecondsParam: JSBI, relativeTo: ReturnType<typeof
34613470
const oneDayFartherNs = AddZonedDateTime(relativeInstant, timeZone, calendar, 0, 0, 0, sign, 0, 0, 0, 0, 0, 0);
34623471
const relativeNs = GetSlot(relativeInstant, EPOCHNANOSECONDS);
34633472
dayLengthNs = JSBI.toNumber(JSBI.subtract(oneDayFartherNs, relativeNs));
3464-
isOverflow = JSBI.greaterThan(
3473+
isOverflow = JSBI.greaterThanOrEqual(
34653474
JSBI.multiply(JSBI.subtract(nanoseconds, JSBI.BigInt(dayLengthNs)), JSBI.BigInt(sign)),
34663475
ZERO
34673476
);
@@ -3471,12 +3480,15 @@ function NanosecondsToDays(nanosecondsParam: JSBI, relativeTo: ReturnType<typeof
34713480
daysBigInt = JSBI.add(daysBigInt, JSBI.BigInt(sign));
34723481
}
34733482
} while (isOverflow);
3474-
if (!JSBI.equal(daysBigInt, ZERO) && MathSign(JSBI.toNumber(daysBigInt)) != sign) {
3483+
if (!isZero(daysBigInt) && signJSBI(daysBigInt) !== sign) {
34753484
throw new RangeError('Time zone or calendar converted nanoseconds into a number of days with the opposite sign');
34763485
}
3477-
if (!JSBI.equal(nanoseconds, ZERO) && MathSign(JSBI.toNumber(nanoseconds)) != sign) {
3486+
if (!isZero(nanoseconds) && signJSBI(nanoseconds) !== sign) {
34783487
throw new RangeError('Time zone or calendar ended up with a remainder of nanoseconds with the opposite sign');
34793488
}
3489+
if (JSBI.greaterThanOrEqual(abs(nanoseconds), JSBI.BigInt(MathAbs(dayLengthNs)))) {
3490+
throw new RangeError('Time zone or calendar ended up with a remainder of nanoseconds longer than the day length');
3491+
}
34803492
return { days: JSBI.toNumber(daysBigInt), nanoseconds, dayLengthNs: MathAbs(dayLengthNs) };
34813493
}
34823494

test/expected-failures-todo-migrated-code.txt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ built-ins/Temporal/Duration/prototype/add/order-of-operations.js
9494
built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-fields-undefined.js
9595
built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js
9696
built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js
97-
built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js
9897
built-ins/Temporal/Duration/prototype/round/order-of-operations.js
9998
built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-fields-undefined.js
10099
built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js
@@ -369,12 +368,7 @@ intl402/Temporal/Calendar/prototype/yearOfWeek/infinity-throws-rangeerror.js
369368
intl402/Temporal/TimeZone/prototype/getNextTransition/transition-at-instant-boundaries.js
370369
intl402/Temporal/TimeZone/prototype/getPreviousTransition/transition-at-instant-boundaries.js
371370
intl402/Temporal/ZonedDateTime/prototype/withCalendar/calendar-case-insensitive.js
372-
built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-2.js
373-
built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-2.js
374-
built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-2.js
375371
built-ins/Temporal/PlainDate/argument-convert.js
376-
built-ins/Temporal/ZonedDateTime/prototype/since/nanoseconds-to-days-loop-indefinitely-2.js
377-
built-ins/Temporal/ZonedDateTime/prototype/until/nanoseconds-to-days-loop-indefinitely-2.js
378372
built-ins/Temporal/Calendar/prototype/dateUntil/argument-propertybag-calendar-case-insensitive.js
379373
built-ins/Temporal/Calendar/prototype/yearOfWeek/argument-propertybag-calendar-case-insensitive.js
380374
built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js

0 commit comments

Comments
 (0)