@js-temporal/polyfill
Version:
Polyfill for Tc39 Stage 3 proposal Temporal (https://github.com/tc39/proposal-temporal)
164 lines (152 loc) • 6.58 kB
text/typescript
import { DEBUG } from './debug';
import * as ES from './ecmascript';
import { GetIntrinsic, MakeIntrinsicClass } from './intrinsicclass';
import {
TIMEZONE_ID,
EPOCHNANOSECONDS,
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND,
CreateSlots,
GetSlot,
SetSlot
} from './slots';
import JSBI from 'jsbi';
import type { Temporal } from '..';
import type { TimeZoneParams as Params, TimeZoneReturn as Return } from './internaltypes';
export class TimeZone implements Temporal.TimeZone {
constructor(timeZoneIdentifierParam: string) {
// Note: if the argument is not passed, GetCanonicalTimeZoneIdentifier(undefined) will throw.
// This check exists only to improve the error message.
if (arguments.length < 1) {
throw new RangeError('missing argument: identifier is required');
}
const timeZoneIdentifier = ES.GetCanonicalTimeZoneIdentifier(timeZoneIdentifierParam);
CreateSlots(this);
SetSlot(this, TIMEZONE_ID, timeZoneIdentifier);
if (DEBUG) {
Object.defineProperty(this, '_repr_', {
value: `${this[Symbol.toStringTag]} <${timeZoneIdentifier}>`,
writable: false,
enumerable: false,
configurable: false
});
}
}
get id(): Return['id'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
return ES.ToString(this);
}
getOffsetNanosecondsFor(instantParam: Params['getOffsetNanosecondsFor'][0]): Return['getOffsetNanosecondsFor'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const instant = ES.ToTemporalInstant(instantParam);
const id = GetSlot(this, TIMEZONE_ID);
if (ES.TestTimeZoneOffsetString(id)) {
return ES.ParseTimeZoneOffsetString(id);
}
return ES.GetIANATimeZoneOffsetNanoseconds(GetSlot(instant, EPOCHNANOSECONDS), id);
}
getOffsetStringFor(instantParam: Params['getOffsetStringFor'][0]): Return['getOffsetStringFor'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const instant = ES.ToTemporalInstant(instantParam);
return ES.BuiltinTimeZoneGetOffsetStringFor(this, instant);
}
getPlainDateTimeFor(
instantParam: Params['getPlainDateTimeFor'][0],
calendarParam: Params['getPlainDateTimeFor'][1] = ES.GetISO8601Calendar()
): Return['getPlainDateTimeFor'] {
const instant = ES.ToTemporalInstant(instantParam);
const calendar = ES.ToTemporalCalendar(calendarParam);
return ES.BuiltinTimeZoneGetPlainDateTimeFor(this, instant, calendar);
}
getInstantFor(
dateTimeParam: Params['getInstantFor'][0],
optionsParam: Params['getInstantFor'][1] = undefined
): Return['getInstantFor'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const dateTime = ES.ToTemporalDateTime(dateTimeParam);
const options = ES.GetOptionsObject(optionsParam);
const disambiguation = ES.ToTemporalDisambiguation(options);
return ES.BuiltinTimeZoneGetInstantFor(this, dateTime, disambiguation);
}
getPossibleInstantsFor(dateTimeParam: Params['getPossibleInstantsFor'][0]): Return['getPossibleInstantsFor'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const dateTime = ES.ToTemporalDateTime(dateTimeParam);
const Instant = GetIntrinsic('%Temporal.Instant%');
const id = GetSlot(this, TIMEZONE_ID);
if (ES.TestTimeZoneOffsetString(id)) {
const epochNs = ES.GetEpochFromISOParts(
GetSlot(dateTime, ISO_YEAR),
GetSlot(dateTime, ISO_MONTH),
GetSlot(dateTime, ISO_DAY),
GetSlot(dateTime, ISO_HOUR),
GetSlot(dateTime, ISO_MINUTE),
GetSlot(dateTime, ISO_SECOND),
GetSlot(dateTime, ISO_MILLISECOND),
GetSlot(dateTime, ISO_MICROSECOND),
GetSlot(dateTime, ISO_NANOSECOND)
);
if (epochNs === null) throw new RangeError('DateTime outside of supported range');
const offsetNs = ES.ParseTimeZoneOffsetString(id);
return [new Instant(JSBI.subtract(epochNs, JSBI.BigInt(offsetNs)))];
}
const possibleEpochNs = ES.GetIANATimeZoneEpochValue(
id,
GetSlot(dateTime, ISO_YEAR),
GetSlot(dateTime, ISO_MONTH),
GetSlot(dateTime, ISO_DAY),
GetSlot(dateTime, ISO_HOUR),
GetSlot(dateTime, ISO_MINUTE),
GetSlot(dateTime, ISO_SECOND),
GetSlot(dateTime, ISO_MILLISECOND),
GetSlot(dateTime, ISO_MICROSECOND),
GetSlot(dateTime, ISO_NANOSECOND)
);
return possibleEpochNs.map((ns) => new Instant(ns));
}
getNextTransition(startingPointParam: Params['getNextTransition'][0]): Return['getNextTransition'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const startingPoint = ES.ToTemporalInstant(startingPointParam);
const id = GetSlot(this, TIMEZONE_ID);
// Offset time zones or UTC have no transitions
if (ES.TestTimeZoneOffsetString(id) || id === 'UTC') {
return null;
}
let epochNanoseconds: JSBI | null = GetSlot(startingPoint, EPOCHNANOSECONDS);
const Instant = GetIntrinsic('%Temporal.Instant%');
epochNanoseconds = ES.GetIANATimeZoneNextTransition(epochNanoseconds, id);
return epochNanoseconds === null ? null : new Instant(epochNanoseconds);
}
getPreviousTransition(startingPointParam: Params['getPreviousTransition'][0]): Return['getPreviousTransition'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const startingPoint = ES.ToTemporalInstant(startingPointParam);
const id = GetSlot(this, TIMEZONE_ID);
// Offset time zones or UTC have no transitions
if (ES.TestTimeZoneOffsetString(id) || id === 'UTC') {
return null;
}
let epochNanoseconds: JSBI | null = GetSlot(startingPoint, EPOCHNANOSECONDS);
const Instant = GetIntrinsic('%Temporal.Instant%');
epochNanoseconds = ES.GetIANATimeZonePreviousTransition(epochNanoseconds, id);
return epochNanoseconds === null ? null : new Instant(epochNanoseconds);
}
toString(): string {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
return ES.ToString(GetSlot(this, TIMEZONE_ID));
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
return ES.ToString(this);
}
static from(item: Params['from'][0]): Return['from'] {
return ES.ToTemporalTimeZone(item);
}
[Symbol.toStringTag]!: 'Temporal.TimeZone';
}
MakeIntrinsicClass(TimeZone, 'Temporal.TimeZone');