Skip to content

Commit 6e485c5

Browse files
committed
Less repetition in converting epoch milliseconds / epoch nanoseconds
This applies DRY to the many times we convert epoch milliseconds to epoch nanoseconds and vice versa.
1 parent 07298e8 commit 6e485c5

File tree

3 files changed

+25
-26
lines changed

3 files changed

+25
-26
lines changed

lib/ecmascript.ts

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ export const DAY_NANOS = DAY_MS * 1e6;
6666
const MINUTE_NANOS = 60e9;
6767
// Instant range is 100 million days (inclusive) before or after epoch.
6868
const MS_MAX = DAY_MS * 1e8;
69-
const NS_MIN = JSBI.multiply(DAY_NANOS_JSBI, JSBI.BigInt(-1e8));
70-
const NS_MAX = JSBI.multiply(DAY_NANOS_JSBI, JSBI.BigInt(1e8));
69+
const NS_MAX = epochMsToNs(MS_MAX);
70+
const NS_MIN = JSBI.unaryMinus(NS_MAX);
7171
// PlainDateTime range is 24 hours wider (exclusive) than the Instant range on
7272
// both ends, to allow for valid Instant=>PlainDateTime conversion for all
7373
// built-in time zones (whose offsets must have a magnitude less than 24 hours).
@@ -2681,13 +2681,12 @@ function GetUTCEpochMilliseconds({
26812681
function GetUTCEpochNanoseconds(isoDateTime: ISODateTime) {
26822682
const ms = GetUTCEpochMilliseconds(isoDateTime);
26832683
const subMs = isoDateTime.time.microsecond * 1e3 + isoDateTime.time.nanosecond;
2684-
return JSBI.add(JSBI.multiply(JSBI.BigInt(ms), MILLION), JSBI.BigInt(subMs));
2684+
return JSBI.add(epochMsToNs(ms), JSBI.BigInt(subMs));
26852685
}
26862686

26872687
export function GetISOPartsFromEpoch(epochNanoseconds: JSBI) {
2688-
const { quotient, remainder } = divmod(epochNanoseconds, MILLION);
2689-
let epochMilliseconds = JSBI.toNumber(quotient);
2690-
let nanos = JSBI.toNumber(remainder);
2688+
let epochMilliseconds = epochNsToMs(epochNanoseconds, 'trunc');
2689+
let nanos = JSBI.toNumber(JSBI.remainder(epochNanoseconds, MILLION));
26912690
if (nanos < 0) {
26922691
nanos += 1e6;
26932692
epochMilliseconds -= 1;
@@ -2729,7 +2728,7 @@ export function GetNamedTimeZoneNextTransition(id: string, epochNanoseconds: JSB
27292728
// time zone transitions don't happen in the middle of a millisecond.
27302729
const epochMilliseconds = epochNsToMs(epochNanoseconds, 'floor');
27312730
if (epochMilliseconds < BEFORE_FIRST_DST) {
2732-
return GetNamedTimeZoneNextTransition(id, JSBI.multiply(JSBI.BigInt(BEFORE_FIRST_DST), MILLION));
2731+
return GetNamedTimeZoneNextTransition(id, epochMsToNs(BEFORE_FIRST_DST));
27332732
}
27342733

27352734
// Optimization: the farthest that we'll look for a next transition is 3 years
@@ -2759,7 +2758,7 @@ export function GetNamedTimeZoneNextTransition(id: string, epochNanoseconds: JSB
27592758
leftOffsetNs,
27602759
rightOffsetNs
27612760
);
2762-
return JSBI.multiply(JSBI.BigInt(result), MILLION);
2761+
return epochMsToNs(result);
27632762
}
27642763

27652764
export function GetNamedTimeZonePreviousTransition(id: string, epochNanoseconds: JSBI): JSBI | null {
@@ -2776,8 +2775,8 @@ export function GetNamedTimeZonePreviousTransition(id: string, epochNanoseconds:
27762775
const now = Date.now();
27772776
const lookahead = now + DAY_MS * 366 * 3;
27782777
if (epochMilliseconds > lookahead) {
2779-
const prevBeforeLookahead = GetNamedTimeZonePreviousTransition(id, JSBI.multiply(JSBI.BigInt(lookahead), MILLION));
2780-
if (prevBeforeLookahead === null || JSBI.lessThan(prevBeforeLookahead, JSBI.multiply(JSBI.BigInt(now), MILLION))) {
2778+
const prevBeforeLookahead = GetNamedTimeZonePreviousTransition(id, epochMsToNs(lookahead));
2779+
if (prevBeforeLookahead === null || JSBI.lessThan(prevBeforeLookahead, epochMsToNs(now))) {
27812780
return prevBeforeLookahead;
27822781
}
27832782
}
@@ -2792,7 +2791,7 @@ export function GetNamedTimeZonePreviousTransition(id: string, epochNanoseconds:
27922791
if (id === 'Africa/Casablanca' || id === 'Africa/El_Aaiun') {
27932792
const lastPrecomputed = Date.UTC(2088, 0, 1); // 2088-01-01T00Z
27942793
if (lastPrecomputed < epochMilliseconds) {
2795-
return GetNamedTimeZonePreviousTransition(id, JSBI.multiply(JSBI.BigInt(lastPrecomputed), MILLION));
2794+
return GetNamedTimeZonePreviousTransition(id, epochMsToNs(lastPrecomputed));
27962795
}
27972796
}
27982797

@@ -2817,7 +2816,7 @@ export function GetNamedTimeZonePreviousTransition(id: string, epochNanoseconds:
28172816
leftOffsetNs,
28182817
rightOffsetNs
28192818
);
2820-
return JSBI.multiply(JSBI.BigInt(result), MILLION);
2819+
return epochMsToNs(result);
28212820
}
28222821

28232822
// ts-prune-ignore-next TODO: remove this after tests are converted to TS
@@ -4744,8 +4743,8 @@ export function ToBigIntExternal(arg: unknown): ExternalBigInt {
47444743
return jsbiBI as unknown as ExternalBigInt;
47454744
}
47464745

4747-
// rounding modes supported: floor, ceil
4748-
export function epochNsToMs(epochNanosecondsParam: JSBI | bigint, mode: 'floor' | 'ceil') {
4746+
// rounding modes supported: floor, ceil, trunc
4747+
export function epochNsToMs(epochNanosecondsParam: JSBI | bigint, mode: 'floor' | 'ceil' | 'trunc') {
47494748
const epochNanoseconds = ensureJSBI(epochNanosecondsParam);
47504749
const { quotient, remainder } = divmod(epochNanoseconds, MILLION);
47514750
let epochMilliseconds = JSBI.toNumber(quotient);
@@ -4754,6 +4753,11 @@ export function epochNsToMs(epochNanosecondsParam: JSBI | bigint, mode: 'floor'
47544753
return epochMilliseconds;
47554754
}
47564755

4756+
export function epochMsToNs(epochMilliseconds: number) {
4757+
if (!Number.isInteger(epochMilliseconds)) throw new RangeError('epoch milliseconds must be an integer');
4758+
return JSBI.multiply(JSBI.BigInt(epochMilliseconds), MILLION);
4759+
}
4760+
47574761
export function ToBigInt(arg: unknown): JSBI {
47584762
let prim = arg;
47594763
if (typeof arg === 'object') {
@@ -4789,8 +4793,9 @@ export function ToBigInt(arg: unknown): JSBI {
47894793
export const SystemUTCEpochNanoSeconds: () => JSBI = (() => {
47904794
let ns = JSBI.BigInt(Date.now() % 1e6);
47914795
return () => {
4792-
const ms = JSBI.BigInt(Date.now());
4793-
const result = JSBI.add(JSBI.multiply(ms, MILLION), ns);
4796+
const now = Date.now();
4797+
const ms = JSBI.BigInt(now);
4798+
const result = JSBI.add(epochMsToNs(now), ns);
47944799
ns = JSBI.remainder(ms, MILLION);
47954800
if (JSBI.greaterThan(result, NS_MAX)) return NS_MAX;
47964801
if (JSBI.lessThan(result, NS_MIN)) return NS_MIN;

lib/instant.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { DateTimeFormat } from './intl';
77
import type { InstantParams as Params, InstantReturn as Return } from './internaltypes';
88

99
import JSBI from 'jsbi';
10-
import { MILLION } from './bigintmath';
1110

1211
export class Instant implements Temporal.Instant {
1312
constructor(epochNanoseconds: bigint | JSBI) {
@@ -125,11 +124,8 @@ export class Instant implements Temporal.Instant {
125124
return ES.CreateTemporalZonedDateTime(GetSlot(this, EPOCHNANOSECONDS), timeZone, 'iso8601');
126125
}
127126

128-
static fromEpochMilliseconds(
129-
epochMillisecondsParam: Params['fromEpochMilliseconds'][0]
130-
): Return['fromEpochMilliseconds'] {
131-
const epochMilliseconds = ES.ToNumber(epochMillisecondsParam);
132-
const epochNanoseconds = JSBI.multiply(JSBI.BigInt(epochMilliseconds), MILLION);
127+
static fromEpochMilliseconds(epochMilliseconds: Params['fromEpochMilliseconds'][0]): Return['fromEpochMilliseconds'] {
128+
const epochNanoseconds = ES.epochMsToNs(ES.ToNumber(epochMilliseconds));
133129
ES.ValidateEpochNanoseconds(epochNanoseconds);
134130
return new Instant(epochNanoseconds);
135131
}

lib/legacydate.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1+
import * as ES from './ecmascript';
12
import { Instant } from './instant';
23

3-
import JSBI from 'jsbi';
4-
import { MILLION } from './bigintmath';
5-
64
// By default, a plain function can be called as a constructor. A method such as
75
// Date.prototype.toTemporalInstant should not be able to. We could check
86
// new.target in the body of toTemporalInstant, but that is not sufficient for
@@ -12,7 +10,7 @@ import { MILLION } from './bigintmath';
1210

1311
class LegacyDateImpl {
1412
toTemporalInstant(this: Date) {
15-
const epochNanoseconds = JSBI.multiply(JSBI.BigInt(Date.prototype.valueOf.call(this)), MILLION);
13+
const epochNanoseconds = ES.epochMsToNs(Date.prototype.valueOf.call(this));
1614
return new Instant(epochNanoseconds);
1715
}
1816
}

0 commit comments

Comments
 (0)