Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
File diff suppressed because it is too large Load Diff
+5
View File
@@ -0,0 +1,5 @@
interface GlobalDebugInfo {
__debug__?: boolean;
}
export const DEBUG = !!(globalThis as GlobalDebugInfo).__debug__;
+483
View File
@@ -0,0 +1,483 @@
import { DEBUG } from './debug';
import * as ES from './ecmascript';
import { MakeIntrinsicClass } from './intrinsicclass';
import {
YEARS,
MONTHS,
WEEKS,
DAYS,
HOURS,
MINUTES,
SECONDS,
MILLISECONDS,
MICROSECONDS,
NANOSECONDS,
CreateSlots,
GetSlot,
SetSlot
} from './slots';
import type { Temporal } from '..';
import type { DurationParams as Params, DurationReturn as Return } from './internaltypes';
import JSBI from 'jsbi';
export class Duration implements Temporal.Duration {
constructor(
yearsParam: Params['constructor'][0] = 0,
monthsParam: Params['constructor'][1] = 0,
weeksParam: Params['constructor'][2] = 0,
daysParam: Params['constructor'][3] = 0,
hoursParam: Params['constructor'][4] = 0,
minutesParam: Params['constructor'][5] = 0,
secondsParam: Params['constructor'][6] = 0,
millisecondsParam: Params['constructor'][7] = 0,
microsecondsParam: Params['constructor'][8] = 0,
nanosecondsParam: Params['constructor'][9] = 0
) {
const years = yearsParam === undefined ? 0 : ES.ToIntegerIfIntegral(yearsParam);
const months = monthsParam === undefined ? 0 : ES.ToIntegerIfIntegral(monthsParam);
const weeks = weeksParam === undefined ? 0 : ES.ToIntegerIfIntegral(weeksParam);
const days = daysParam === undefined ? 0 : ES.ToIntegerIfIntegral(daysParam);
const hours = hoursParam === undefined ? 0 : ES.ToIntegerIfIntegral(hoursParam);
const minutes = minutesParam === undefined ? 0 : ES.ToIntegerIfIntegral(minutesParam);
const seconds = secondsParam === undefined ? 0 : ES.ToIntegerIfIntegral(secondsParam);
const milliseconds = millisecondsParam === undefined ? 0 : ES.ToIntegerIfIntegral(millisecondsParam);
const microseconds = microsecondsParam === undefined ? 0 : ES.ToIntegerIfIntegral(microsecondsParam);
const nanoseconds = nanosecondsParam === undefined ? 0 : ES.ToIntegerIfIntegral(nanosecondsParam);
ES.RejectDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
CreateSlots(this);
SetSlot(this, YEARS, years);
SetSlot(this, MONTHS, months);
SetSlot(this, WEEKS, weeks);
SetSlot(this, DAYS, days);
SetSlot(this, HOURS, hours);
SetSlot(this, MINUTES, minutes);
SetSlot(this, SECONDS, seconds);
SetSlot(this, MILLISECONDS, milliseconds);
SetSlot(this, MICROSECONDS, microseconds);
SetSlot(this, NANOSECONDS, nanoseconds);
if (DEBUG) {
Object.defineProperty(this, '_repr_', {
value: `${this[Symbol.toStringTag]} <${ES.TemporalDurationToString(this)}>`,
writable: false,
enumerable: false,
configurable: false
});
}
}
get years(): Return['years'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, YEARS);
}
get months(): Return['months'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, MONTHS);
}
get weeks(): Return['weeks'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, WEEKS);
}
get days(): Return['days'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, DAYS);
}
get hours(): Return['hours'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, HOURS);
}
get minutes(): Return['minutes'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, MINUTES);
}
get seconds(): Return['seconds'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, SECONDS);
}
get milliseconds(): Return['milliseconds'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, MILLISECONDS);
}
get microseconds(): Return['microseconds'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, MICROSECONDS);
}
get nanoseconds(): Return['nanoseconds'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return GetSlot(this, NANOSECONDS);
}
get sign(): Return['sign'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return ES.DurationSign(
GetSlot(this, YEARS),
GetSlot(this, MONTHS),
GetSlot(this, WEEKS),
GetSlot(this, DAYS),
GetSlot(this, HOURS),
GetSlot(this, MINUTES),
GetSlot(this, SECONDS),
GetSlot(this, MILLISECONDS),
GetSlot(this, MICROSECONDS),
GetSlot(this, NANOSECONDS)
);
}
get blank(): Return['blank'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return (
ES.DurationSign(
GetSlot(this, YEARS),
GetSlot(this, MONTHS),
GetSlot(this, WEEKS),
GetSlot(this, DAYS),
GetSlot(this, HOURS),
GetSlot(this, MINUTES),
GetSlot(this, SECONDS),
GetSlot(this, MILLISECONDS),
GetSlot(this, MICROSECONDS),
GetSlot(this, NANOSECONDS)
) === 0
);
}
with(durationLike: Params['with'][0]): Return['with'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
const partialDuration = ES.PrepareTemporalFields(
durationLike,
// NOTE: Field order here is important.
[
'days',
'hours',
'microseconds',
'milliseconds',
'minutes',
'months',
'nanoseconds',
'seconds',
'weeks',
'years'
],
'partial'
);
const {
years = GetSlot(this, YEARS),
months = GetSlot(this, MONTHS),
weeks = GetSlot(this, WEEKS),
days = GetSlot(this, DAYS),
hours = GetSlot(this, HOURS),
minutes = GetSlot(this, MINUTES),
seconds = GetSlot(this, SECONDS),
milliseconds = GetSlot(this, MILLISECONDS),
microseconds = GetSlot(this, MICROSECONDS),
nanoseconds = GetSlot(this, NANOSECONDS)
} = partialDuration;
return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
negated(): Return['negated'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return ES.CreateNegatedTemporalDuration(this);
}
abs(): Return['abs'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return new Duration(
Math.abs(GetSlot(this, YEARS)),
Math.abs(GetSlot(this, MONTHS)),
Math.abs(GetSlot(this, WEEKS)),
Math.abs(GetSlot(this, DAYS)),
Math.abs(GetSlot(this, HOURS)),
Math.abs(GetSlot(this, MINUTES)),
Math.abs(GetSlot(this, SECONDS)),
Math.abs(GetSlot(this, MILLISECONDS)),
Math.abs(GetSlot(this, MICROSECONDS)),
Math.abs(GetSlot(this, NANOSECONDS))
);
}
add(other: Params['add'][0], options: Params['add'][1] = undefined): Return['add'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromDuration('add', this, other, options);
}
subtract(other: Params['subtract'][0], options: Params['subtract'][1] = undefined): Return['subtract'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromDuration('subtract', this, other, options);
}
round(roundToParam: Params['round'][0]): Return['round'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
if (roundToParam === undefined) throw new TypeError('options parameter is required');
let years = GetSlot(this, YEARS);
let months = GetSlot(this, MONTHS);
let weeks = GetSlot(this, WEEKS);
let days = GetSlot(this, DAYS);
let hours = GetSlot(this, HOURS);
let minutes = GetSlot(this, MINUTES);
let seconds = GetSlot(this, SECONDS);
let milliseconds = GetSlot(this, MILLISECONDS);
let microseconds = GetSlot(this, MICROSECONDS);
let nanoseconds = GetSlot(this, NANOSECONDS);
let defaultLargestUnit = ES.DefaultTemporalLargestUnit(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds
);
const roundTo =
typeof roundToParam === 'string'
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
: ES.GetOptionsObject(roundToParam);
let largestUnit = ES.GetTemporalUnit(roundTo, 'largestUnit', 'datetime', undefined, ['auto']);
let relativeTo = ES.ToRelativeTemporalObject(roundTo);
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
let smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'datetime', undefined);
let smallestUnitPresent = true;
if (!smallestUnit) {
smallestUnitPresent = false;
smallestUnit = 'nanosecond';
}
defaultLargestUnit = ES.LargerOfTwoTemporalUnits(defaultLargestUnit, smallestUnit);
let largestUnitPresent = true;
if (!largestUnit) {
largestUnitPresent = false;
largestUnit = defaultLargestUnit;
}
if (largestUnit === 'auto') largestUnit = defaultLargestUnit;
if (!smallestUnitPresent && !largestUnitPresent) {
throw new RangeError('at least one of smallestUnit or largestUnit is required');
}
if (ES.LargerOfTwoTemporalUnits(largestUnit, smallestUnit) !== largestUnit) {
throw new RangeError(`largestUnit ${largestUnit} cannot be smaller than smallestUnit ${smallestUnit}`);
}
const maximumIncrements = {
hour: 24,
minute: 60,
second: 60,
millisecond: 1000,
microsecond: 1000,
nanosecond: 1000
} as { [k in Temporal.DateTimeUnit]?: number };
const maximum = maximumIncrements[smallestUnit];
if (maximum !== undefined) ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, false);
({ years, months, weeks, days } = ES.UnbalanceDurationRelative(
years,
months,
weeks,
days,
largestUnit,
relativeTo
));
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.RoundDuration(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
roundingIncrement,
smallestUnit,
roundingMode,
relativeTo
));
({ years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } =
ES.AdjustRoundedDurationDays(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
roundingIncrement,
smallestUnit,
roundingMode,
relativeTo
));
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = ES.BalanceDuration(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
largestUnit,
relativeTo
));
({ years, months, weeks, days } = ES.BalanceDurationRelative(years, months, weeks, days, largestUnit, relativeTo));
return new Duration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds);
}
total(optionsParam: Params['total'][0]): Return['total'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
let years = GetSlot(this, YEARS);
let months = GetSlot(this, MONTHS);
let weeks = GetSlot(this, WEEKS);
let days = GetSlot(this, DAYS);
let hours = GetSlot(this, HOURS);
let minutes = GetSlot(this, MINUTES);
let seconds = GetSlot(this, SECONDS);
let milliseconds = GetSlot(this, MILLISECONDS);
let microseconds = GetSlot(this, MICROSECONDS);
let nanoseconds = GetSlot(this, NANOSECONDS);
if (optionsParam === undefined) throw new TypeError('options argument is required');
const options =
typeof optionsParam === 'string'
? (ES.CreateOnePropObject('unit', optionsParam) as Exclude<typeof optionsParam, string>)
: ES.GetOptionsObject(optionsParam);
const relativeTo = ES.ToRelativeTemporalObject(options);
const unit = ES.GetTemporalUnit(options, 'unit', 'datetime', ES.REQUIRED);
// Convert larger units down to days
({ years, months, weeks, days } = ES.UnbalanceDurationRelative(years, months, weeks, days, unit, relativeTo));
// If the unit we're totalling is smaller than `days`, convert days down to that unit.
let intermediate;
if (ES.IsTemporalZonedDateTime(relativeTo)) {
intermediate = ES.MoveRelativeZonedDateTime(relativeTo, years, months, weeks, 0);
}
let balanceResult = ES.BalancePossiblyInfiniteDuration(
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
unit,
intermediate
);
if (balanceResult === 'positive overflow') {
return Infinity;
} else if (balanceResult === 'negative overflow') {
return -Infinity;
}
({ days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds } = balanceResult);
// Finally, truncate to the correct unit and calculate remainder
const { total } = ES.RoundDuration(
years,
months,
weeks,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
1,
unit,
'trunc',
relativeTo
);
return total;
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const digits = ES.ToFractionalSecondDigits(options);
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
if (smallestUnit === 'hour' || smallestUnit === 'minute') {
throw new RangeError('smallestUnit must be a time unit other than "hours" or "minutes"');
}
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
ES.uncheckedAssertNarrowedType<Exclude<typeof precision, 'minute'>>(
precision,
'Precision cannot be "minute" because of RangeError above'
);
return ES.TemporalDurationToString(this, precision, { unit, increment, roundingMode });
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
return ES.TemporalDurationToString(this);
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalDuration(this)) throw new TypeError('invalid receiver');
if (typeof Intl !== 'undefined' && typeof (Intl as any).DurationFormat !== 'undefined') {
return new (Intl as any).DurationFormat(locales, options).format(this);
}
console.warn('Temporal.Duration.prototype.toLocaleString() requires Intl.DurationFormat.');
return ES.TemporalDurationToString(this);
}
valueOf(): never {
throw new TypeError('use compare() to compare Temporal.Duration');
}
static from(item: Params['from'][0]): Return['from'] {
if (ES.IsTemporalDuration(item)) {
return new Duration(
GetSlot(item, YEARS),
GetSlot(item, MONTHS),
GetSlot(item, WEEKS),
GetSlot(item, DAYS),
GetSlot(item, HOURS),
GetSlot(item, MINUTES),
GetSlot(item, SECONDS),
GetSlot(item, MILLISECONDS),
GetSlot(item, MICROSECONDS),
GetSlot(item, NANOSECONDS)
);
}
return ES.ToTemporalDuration(item);
}
static compare(
oneParam: Params['compare'][0],
twoParam: Params['compare'][1],
optionsParam: Params['compare'][2] = undefined
) {
const one = ES.ToTemporalDuration(oneParam);
const two = ES.ToTemporalDuration(twoParam);
const options = ES.GetOptionsObject(optionsParam);
const relativeTo = ES.ToRelativeTemporalObject(options);
const y1 = GetSlot(one, YEARS);
const mon1 = GetSlot(one, MONTHS);
const w1 = GetSlot(one, WEEKS);
let d1 = GetSlot(one, DAYS);
const h1 = GetSlot(one, HOURS);
const min1 = GetSlot(one, MINUTES);
const s1 = GetSlot(one, SECONDS);
const ms1 = GetSlot(one, MILLISECONDS);
const µs1 = GetSlot(one, MICROSECONDS);
let ns1 = GetSlot(one, NANOSECONDS);
const y2 = GetSlot(two, YEARS);
const mon2 = GetSlot(two, MONTHS);
const w2 = GetSlot(two, WEEKS);
let d2 = GetSlot(two, DAYS);
const h2 = GetSlot(two, HOURS);
const min2 = GetSlot(two, MINUTES);
const s2 = GetSlot(two, SECONDS);
const ms2 = GetSlot(two, MILLISECONDS);
const µs2 = GetSlot(two, MICROSECONDS);
let ns2 = GetSlot(two, NANOSECONDS);
const shift1 = ES.CalculateOffsetShift(relativeTo, y1, mon1, w1, d1);
const shift2 = ES.CalculateOffsetShift(relativeTo, y2, mon2, w2, d2);
if (y1 !== 0 || y2 !== 0 || mon1 !== 0 || mon2 !== 0 || w1 !== 0 || w2 !== 0) {
({ days: d1 } = ES.UnbalanceDurationRelative(y1, mon1, w1, d1, 'day', relativeTo));
({ days: d2 } = ES.UnbalanceDurationRelative(y2, mon2, w2, d2, 'day', relativeTo));
}
const totalNs1 = ES.TotalDurationNanoseconds(d1, h1, min1, s1, ms1, µs1, ns1, shift1);
const totalNs2 = ES.TotalDurationNanoseconds(d2, h2, min2, s2, ms2, µs2, ns2, shift2);
return ES.ComparisonResult(JSBI.toNumber(JSBI.subtract(totalNs1, totalNs2)));
}
[Symbol.toStringTag]!: 'Temporal.Duration';
}
MakeIntrinsicClass(Duration, 'Temporal.Duration');
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
// This entry point treats Temporal as a library, and does not polyfill it onto
// the global object.
// This is in order to avoid breaking the web in the future, if the polyfill
// gains wide adoption before the API is finalized. We do not want checks such
// as `if (typeof Temporal === 'undefined')` in the wild, until browsers start
// shipping the finalized API.
import * as Temporal from './temporal';
import * as Intl from './intl';
import { toTemporalInstant } from './legacydate';
// Work around https://github.com/babel/babel/issues/2025.
const types = [
Temporal.Instant,
Temporal.Calendar,
Temporal.PlainDate,
Temporal.PlainDateTime,
Temporal.Duration,
Temporal.PlainMonthDay,
// Temporal.Now, // plain object (not a constructor), so no `prototype`
Temporal.PlainTime,
Temporal.TimeZone,
Temporal.PlainYearMonth,
Temporal.ZonedDateTime
];
for (const type of types) {
const descriptor = Object.getOwnPropertyDescriptor(type, 'prototype') as PropertyDescriptor;
if (descriptor.configurable || descriptor.enumerable || descriptor.writable) {
descriptor.configurable = false;
descriptor.enumerable = false;
descriptor.writable = false;
Object.defineProperty(type, 'prototype', descriptor);
}
}
export { Temporal, Intl, toTemporalInstant };
+43
View File
@@ -0,0 +1,43 @@
// This is an alternate entry point that polyfills Temporal onto the global
// object. This is used only for the browser playground and the test262 tests.
// See the note in index.mjs.
import * as Temporal from './temporal';
import * as Intl from './intl';
import { toTemporalInstant } from './legacydate';
Object.defineProperty(globalThis, 'Temporal', {
value: {},
writable: true,
enumerable: false,
configurable: true
});
const globalTemporal = (globalThis as unknown as { Temporal: typeof Temporal }).Temporal;
copy(globalTemporal, Temporal);
Object.defineProperty(globalTemporal, Symbol.toStringTag, {
value: 'Temporal',
writable: false,
enumerable: false,
configurable: true
});
copy(globalTemporal.Now, Temporal.Now);
copy(globalThis.Intl, Intl);
Object.defineProperty(globalThis.Date.prototype, 'toTemporalInstant', {
value: toTemporalInstant,
writable: true,
enumerable: false,
configurable: true
});
function copy(target: Record<string | number | symbol, unknown>, source: Record<string | number | symbol, unknown>) {
for (const prop of Object.getOwnPropertyNames(source)) {
Object.defineProperty(target, prop, {
value: source[prop],
writable: true,
enumerable: false,
configurable: true
});
}
}
export { Temporal, Intl, toTemporalInstant };
+201
View File
@@ -0,0 +1,201 @@
import { DEBUG } from './debug';
import * as ES from './ecmascript';
import { MakeIntrinsicClass } from './intrinsicclass';
import { EPOCHNANOSECONDS, CreateSlots, GetSlot, SetSlot } from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { InstantParams as Params, InstantReturn as Return } from './internaltypes';
import JSBI from 'jsbi';
import { BILLION, MILLION, THOUSAND } from './ecmascript';
export class Instant implements Temporal.Instant {
constructor(epochNanoseconds: bigint | JSBI) {
// Note: if the argument is not passed, ToBigInt(undefined) will throw. This check exists only
// to improve the error message.
if (arguments.length < 1) {
throw new TypeError('missing argument: epochNanoseconds is required');
}
const ns = ES.ToBigInt(epochNanoseconds);
ES.ValidateEpochNanoseconds(ns);
CreateSlots(this);
SetSlot(this, EPOCHNANOSECONDS, ns);
if (DEBUG) {
const repr = ES.TemporalInstantToString(this, undefined, 'auto');
Object.defineProperty(this, '_repr_', {
value: `${this[Symbol.toStringTag]} <${repr}>`,
writable: false,
enumerable: false,
configurable: false
});
}
}
get epochSeconds(): Return['epochSeconds'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const value = GetSlot(this, EPOCHNANOSECONDS);
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(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(ES.BigIntFloorDiv(value, THOUSAND));
}
get epochNanoseconds(): Return['epochNanoseconds'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return ES.ToBigIntExternal(JSBI.BigInt(GetSlot(this, EPOCHNANOSECONDS)));
}
add(temporalDurationLike: Params['add'][0]): Return['add'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromInstant('add', this, temporalDurationLike);
}
subtract(temporalDurationLike: Params['subtract'][0]): Return['subtract'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromInstant('subtract', this, temporalDurationLike);
}
until(other: Params['until'][0], options: Params['until'][1] = undefined): Return['until'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalInstant('until', this, other, options);
}
since(other: Params['since'][0], options: Params['since'][1] = undefined): Return['since'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalInstant('since', this, other, options);
}
round(roundToParam: Params['round'][0]): Return['round'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
if (roundToParam === undefined) throw new TypeError('options parameter is required');
const roundTo =
typeof roundToParam === 'string'
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
: ES.GetOptionsObject(roundToParam);
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
const maximumIncrements = {
hour: 24,
minute: 1440,
second: 86400,
millisecond: 86400e3,
microsecond: 86400e6,
nanosecond: 86400e9
};
ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximumIncrements[smallestUnit], true);
const ns = GetSlot(this, EPOCHNANOSECONDS);
const roundedNs = ES.RoundInstant(ns, roundingIncrement, smallestUnit, roundingMode);
return new Instant(roundedNs);
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalInstant(otherParam);
const one = GetSlot(this, EPOCHNANOSECONDS);
const two = GetSlot(other, EPOCHNANOSECONDS);
return JSBI.equal(JSBI.BigInt(one), JSBI.BigInt(two));
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const digits = ES.ToFractionalSecondDigits(options);
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
let timeZone = options.timeZone;
if (timeZone !== undefined) timeZone = ES.ToTemporalTimeZoneSlotValue(timeZone);
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
const ns = GetSlot(this, EPOCHNANOSECONDS);
const roundedNs = ES.RoundInstant(ns, increment, unit, roundingMode);
const roundedInstant = new Instant(roundedNs);
return ES.TemporalInstantToString(roundedInstant, timeZone as Temporal.TimeZoneProtocol, precision);
}
toJSON(): string {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return ES.TemporalInstantToString(this, undefined, 'auto');
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
return new DateTimeFormat(locales, options).format(this);
}
valueOf(): never {
throw new TypeError('use compare() or equals() to compare Temporal.Instant');
}
toZonedDateTime(item: Params['toZonedDateTime'][0]): Return['toZonedDateTime'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(item)) {
throw new TypeError('invalid argument in toZonedDateTime');
}
const calendarLike = item.calendar;
if (calendarLike === undefined) {
throw new TypeError('missing calendar property in toZonedDateTime');
}
const calendar = ES.ToTemporalCalendarSlotValue(calendarLike);
const temporalTimeZoneLike = item.timeZone;
if (temporalTimeZoneLike === undefined) {
throw new TypeError('missing timeZone property in toZonedDateTime');
}
const timeZone = ES.ToTemporalTimeZoneSlotValue(temporalTimeZoneLike);
return ES.CreateTemporalZonedDateTime(GetSlot(this, EPOCHNANOSECONDS), timeZone, calendar);
}
toZonedDateTimeISO(timeZoneParam: Params['toZonedDateTimeISO'][0]): Return['toZonedDateTimeISO'] {
if (!ES.IsTemporalInstant(this)) throw new TypeError('invalid receiver');
const timeZone = ES.ToTemporalTimeZoneSlotValue(timeZoneParam);
return ES.CreateTemporalZonedDateTime(GetSlot(this, EPOCHNANOSECONDS), timeZone, 'iso8601');
}
static fromEpochSeconds(epochSecondsParam: Params['fromEpochSeconds'][0]): Return['fromEpochSeconds'] {
const epochSeconds = ES.ToNumber(epochSecondsParam);
const epochNanoseconds = JSBI.multiply(JSBI.BigInt(epochSeconds), BILLION);
ES.ValidateEpochNanoseconds(epochNanoseconds);
return new Instant(epochNanoseconds);
}
static fromEpochMilliseconds(
epochMillisecondsParam: Params['fromEpochMilliseconds'][0]
): Return['fromEpochMilliseconds'] {
const epochMilliseconds = ES.ToNumber(epochMillisecondsParam);
const epochNanoseconds = JSBI.multiply(JSBI.BigInt(epochMilliseconds), MILLION);
ES.ValidateEpochNanoseconds(epochNanoseconds);
return new Instant(epochNanoseconds);
}
static fromEpochMicroseconds(
epochMicrosecondsParam: Params['fromEpochMicroseconds'][0]
): Return['fromEpochMicroseconds'] {
const epochMicroseconds = ES.ToBigInt(epochMicrosecondsParam);
const epochNanoseconds = JSBI.multiply(epochMicroseconds, THOUSAND);
ES.ValidateEpochNanoseconds(epochNanoseconds);
return new Instant(epochNanoseconds);
}
static fromEpochNanoseconds(
epochNanosecondsParam: Params['fromEpochNanoseconds'][0]
): Return['fromEpochNanoseconds'] {
const epochNanoseconds = ES.ToBigInt(epochNanosecondsParam);
ES.ValidateEpochNanoseconds(epochNanoseconds);
return new Instant(epochNanoseconds);
}
static from(item: Params['from'][0]): Return['from'] {
if (ES.IsTemporalInstant(item)) {
return new Instant(GetSlot(item, EPOCHNANOSECONDS));
}
return ES.ToTemporalInstant(item);
}
static compare(oneParam: Params['compare'][0], twoParam: Params['compare'][1]): Return['compare'] {
const one = ES.ToTemporalInstant(oneParam);
const two = ES.ToTemporalInstant(twoParam);
const oneNs = GetSlot(one, EPOCHNANOSECONDS);
const twoNs = GetSlot(two, EPOCHNANOSECONDS);
if (JSBI.lessThan(oneNs, twoNs)) return -1;
if (JSBI.greaterThan(oneNs, twoNs)) return 1;
return 0;
}
[Symbol.toStringTag]!: 'Temporal.Instant';
}
MakeIntrinsicClass(Instant, 'Temporal.Instant');
@@ -0,0 +1,205 @@
import type { Intl, Temporal } from '..';
export type BuiltinCalendarId =
| 'iso8601'
| 'hebrew'
| 'islamic'
| 'islamic-umalqura'
| 'islamic-tbla'
| 'islamic-civil'
| 'islamic-rgsa'
| 'islamicc'
| 'persian'
| 'ethiopic'
| 'ethioaa'
| 'coptic'
| 'chinese'
| 'dangi'
| 'roc'
| 'indian'
| 'buddhist'
| 'japanese'
| 'gregory';
export type AnyTemporalType =
| Temporal.Calendar
| Temporal.Duration
| Temporal.Instant
| Temporal.PlainDate
| Temporal.PlainDateTime
| Temporal.PlainMonthDay
| Temporal.PlainTime
| Temporal.PlainYearMonth
| Temporal.TimeZone
| Temporal.ZonedDateTime;
/*
// unused, but uncomment if this is needed later
export type AnyTemporalConstructor =
| typeof Temporal.Calendar
| typeof Temporal.Duration
| typeof Temporal.Instant
| typeof Temporal.PlainDate
| typeof Temporal.PlainDateTime
| typeof Temporal.PlainMonthDay
| typeof Temporal.PlainTime
| typeof Temporal.PlainYearMonth
| typeof Temporal.TimeZone
| typeof Temporal.ZonedDateTime;
*/
export type CalendarSlot = string | Temporal.CalendarProtocol;
export type TimeZoneSlot = string | Temporal.TimeZoneProtocol;
// Used in AnyTemporalLikeType
// ts-prune-ignore-next
type AllTemporalLikeTypes = [
Temporal.DurationLike,
Temporal.PlainDateLike,
Temporal.PlainDateTimeLike,
Temporal.PlainMonthDayLike,
Temporal.PlainTimeLike,
Temporal.PlainYearMonthLike,
Temporal.ZonedDateTimeLike
];
export type AnyTemporalLikeType = AllTemporalLikeTypes[number];
// Keys is a conditionally-mapped version of keyof
export type Keys<T> = T extends Record<string, unknown> ? keyof T : never;
export type AnyTemporalKey = Exclude<Keys<AnyTemporalLikeType>, symbol>;
// The properties below are all the names of Temporal properties that can be set with `with`.
// `timeZone` and `calendar` are not on the list because they have special methods to set them.
// Used in PrimitiveFieldsOf
// ts-prune-ignore-next
type PrimitivePropertyNames =
| 'year'
| 'month'
| 'monthCode'
| 'day'
| 'hour'
| 'minute'
| 'second'
| 'millisecond'
| 'microsecond'
| 'nanosecond'
| 'years'
| 'months'
| 'weeks'
| 'days'
| 'hours'
| 'minutes'
| 'seconds'
| 'milliseconds'
| 'microseconds'
| 'nanoseconds'
| 'era'
| 'eraYear'
| 'offset';
export type PrimitiveFieldsOf<T extends AnyTemporalLikeType> = Pick<T, keyof T & PrimitivePropertyNames>;
export type UnitSmallerThanOrEqualTo<T extends Temporal.DateTimeUnit> = T extends 'year'
? Temporal.DateTimeUnit
: T extends 'month'
? Exclude<Temporal.DateTimeUnit, 'year'>
: T extends 'week'
? Exclude<Temporal.DateTimeUnit, 'year' | 'month'>
: T extends 'day'
? Exclude<Temporal.DateTimeUnit, 'year' | 'month' | 'week'>
: T extends 'hour'
? Temporal.TimeUnit
: T extends 'minute'
? Exclude<Temporal.TimeUnit, 'hour'>
: T extends 'second'
? Exclude<Temporal.TimeUnit, 'hour' | 'minute'>
: T extends 'millisecond'
? Exclude<Temporal.TimeUnit, 'hour' | 'minute' | 'second'>
: T extends 'microsecond'
? 'nanosecond'
: never;
// ts-prune complains about the type definitions below, even though they're used
// by exported types Not sure why and don't have time to investigate, so just
// disabling the warnings for now.
// ts-prune-ignore-next
type Method = (...args: any) => any;
// ts-prune-ignore-next
type NonObjectKeys<T> = Exclude<keyof T, 'toString' | 'toLocaleString' | 'prototype'>;
// ts-prune-ignore-next
type MethodParams<Type extends new (...args: any) => any> = {
// constructor parameters
constructor: ConstructorParameters<Type>;
} & {
// static method parameters
[Key in NonObjectKeys<Type>]: Type[Key] extends Method ? Parameters<Type[Key]> : never;
} & {
// prototype method parameters
[Key in keyof InstanceType<Type>]: InstanceType<Type>[Key] extends Method
? Parameters<InstanceType<Type>[Key]>
: never;
};
// ts-prune-ignore-next
type MethodReturn<Type extends new (...args: any) => any> = {
constructor: InstanceType<Type>;
} & {
[Key in NonObjectKeys<Type>]: Type[Key] extends Method ? ReturnType<Type[Key]> : Type[Key];
} & {
[Key in keyof InstanceType<Type>]: InstanceType<Type>[Key] extends Method
? ReturnType<InstanceType<Type>[Key]>
: InstanceType<Type>[Key];
};
/* Currently unused, but may use later
type InterfaceReturn<Type> = {
[Key in keyof Type]: Type[Key] extends Method ? ReturnType<Type[Key]> : Type[Key];
};
*/
// ts-prune-ignore-next
type InterfaceParams<Type> = {
[Key in keyof Type]: Type[Key] extends Method ? Parameters<Type[Key]> : never;
};
// Parameters of each Temporal type. Examples:
// * InstantParams['compare'][1] - static methods
// * PlainDateParams['since'][0] - prototype methods
// * DurationParams['constructor'][3] - constructors
export interface ZonedDateTimeParams extends MethodParams<typeof Temporal.ZonedDateTime> {}
export interface CalendarParams extends MethodParams<typeof Temporal.Calendar> {}
export interface DurationParams extends MethodParams<typeof Temporal.Duration> {}
export interface InstantParams extends MethodParams<typeof Temporal.Instant> {}
export interface PlainDateParams extends MethodParams<typeof Temporal.PlainDate> {}
export interface PlainDateTimeParams extends MethodParams<typeof Temporal.PlainDateTime> {}
export interface PlainMonthDayParams extends MethodParams<typeof Temporal.PlainMonthDay> {}
export interface PlainTimeParams extends MethodParams<typeof Temporal.PlainTime> {}
export interface PlainYearMonthParams extends MethodParams<typeof Temporal.PlainYearMonth> {}
export interface TimeZoneParams extends MethodParams<typeof Temporal.TimeZone> {}
export interface ZonedDateTimeParams extends MethodParams<typeof Temporal.ZonedDateTime> {}
// Return types of static or instance methods
export interface ZonedDateTimeReturn extends MethodReturn<typeof Temporal.ZonedDateTime> {}
export interface CalendarReturn extends MethodReturn<typeof Temporal.Calendar> {}
export interface DurationReturn extends MethodReturn<typeof Temporal.Duration> {}
export interface InstantReturn extends MethodReturn<typeof Temporal.Instant> {}
export interface PlainDateReturn extends MethodReturn<typeof Temporal.PlainDate> {}
export interface PlainDateTimeReturn extends MethodReturn<typeof Temporal.PlainDateTime> {}
export interface PlainMonthDayReturn extends MethodReturn<typeof Temporal.PlainMonthDay> {}
export interface PlainTimeReturn extends MethodReturn<typeof Temporal.PlainTime> {}
export interface PlainYearMonthReturn extends MethodReturn<typeof Temporal.PlainYearMonth> {}
export interface TimeZoneReturn extends MethodReturn<typeof Temporal.TimeZone> {}
export interface ZonedDateTimeReturn extends MethodReturn<typeof Temporal.ZonedDateTime> {}
export interface CalendarProtocolParams extends InterfaceParams<Temporal.CalendarProtocol> {}
export interface TimeZoneProtocolParams extends InterfaceParams<Temporal.TimeZoneProtocol> {}
// UNUSED, BUT MAY USE LATER
// export interface TimeZoneProtocolReturn extends InterfaceReturn<Temporal.TimeZoneProtocol> {}
// export interface CalendarProtocolReturn extends InterfaceReturn<Temporal.CalendarProtocol> {}
export interface DateTimeFormatParams extends MethodParams<typeof Intl.DateTimeFormat> {}
export interface DateTimeFormatReturn extends MethodReturn<typeof Intl.DateTimeFormat> {}
+558
View File
@@ -0,0 +1,558 @@
import * as ES from './ecmascript';
import { GetIntrinsic } from './intrinsicclass';
import {
GetSlot,
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND,
CALENDAR
} from './slots';
import type { Temporal, Intl } from '..';
import type { DateTimeFormatParams as Params, DateTimeFormatReturn as Return } from './internaltypes';
const DATE = Symbol('date');
const YM = Symbol('ym');
const MD = Symbol('md');
const TIME = Symbol('time');
const DATETIME = Symbol('datetime');
const INST = Symbol('instant');
const ORIGINAL = Symbol('original');
const TZ_RESOLVED = Symbol('timezone');
const CAL_ID = Symbol('calendar-id');
const LOCALE = Symbol('locale');
const OPTIONS = Symbol('options');
const descriptor = <T extends (...args: any[]) => any>(value: T) => {
return {
value,
enumerable: true,
writable: false,
configurable: true
};
};
const IntlDateTimeFormat = globalThis.Intl.DateTimeFormat;
const ObjectAssign = Object.assign;
const ObjectHasOwnProperty = Object.prototype.hasOwnProperty;
const ReflectApply = Reflect.apply;
interface CustomFormatters {
[DATE]: typeof dateAmend | globalThis.Intl.DateTimeFormat;
[YM]: typeof yearMonthAmend | typeof globalThis.Intl.DateTimeFormat;
[MD]: typeof monthDayAmend | typeof globalThis.Intl.DateTimeFormat;
[TIME]: typeof timeAmend | typeof globalThis.Intl.DateTimeFormat;
[DATETIME]: typeof datetimeAmend | typeof globalThis.Intl.DateTimeFormat;
[INST]: typeof instantAmend | typeof globalThis.Intl.DateTimeFormat;
}
interface PrivateProps extends CustomFormatters {
[ORIGINAL]: globalThis.Intl.DateTimeFormat;
[TZ_RESOLVED]: string | Temporal.TimeZoneProtocol;
[CAL_ID]: globalThis.Intl.ResolvedDateTimeFormatOptions['calendar'];
[LOCALE]: globalThis.Intl.ResolvedDateTimeFormatOptions['locale'];
[OPTIONS]: Intl.DateTimeFormatOptions;
}
type OptionsAmenderFunction = (options: Intl.DateTimeFormatOptions) => globalThis.Intl.DateTimeFormatOptions;
type FormatterOrAmender = globalThis.Intl.DateTimeFormat | OptionsAmenderFunction;
// Construction of built-in Intl.DateTimeFormat objects is sloooooow,
// so we'll only create those instances when we need them.
// See https://bugs.chromium.org/p/v8/issues/detail?id=6528
function getPropLazy<T extends PrivateProps, P extends keyof CustomFormatters>(
obj: T,
prop: P
): globalThis.Intl.DateTimeFormat {
let val = obj[prop] as FormatterOrAmender;
if (typeof val === 'function') {
// If we get here, `val` is an "amender function". It will take the user's
// options and transform them into suitable options to be passed into the
// built-in (non-polyfill) Intl.DateTimeFormat constructor. These options
// will vary depending on the Temporal type, so that's why we store separate
// formatters in separate props on the polyfill's DateTimeFormat instances.
// The efficiency happens because we don't create an (expensive) formatter
// until the user calls toLocaleString for that Temporal type.
val = new IntlDateTimeFormat(obj[LOCALE], val(obj[OPTIONS]));
// TODO: can this be typed more cleanly?
(obj[prop] as globalThis.Intl.DateTimeFormat) = val;
}
return val;
}
type DateTimeFormatImpl = Intl.DateTimeFormat & PrivateProps;
function DateTimeFormatImpl(
this: Intl.DateTimeFormat & PrivateProps,
locale: Params['constructor'][0] = undefined,
optionsParam: Params['constructor'][1] = {}
) {
if (!(this instanceof DateTimeFormatImpl)) {
type Construct = new (
locale: Params['constructor'][0],
optionsParam: Params['constructor'][1]
) => Intl.DateTimeFormat;
return new (DateTimeFormatImpl as unknown as Construct)(locale, optionsParam);
}
const hasOptions = typeof optionsParam !== 'undefined';
const options = hasOptions ? ObjectAssign({}, optionsParam) : {};
// TODO: remove type assertion after Temporal types land in TS lib types
const original = new IntlDateTimeFormat(locale, options as globalThis.Intl.DateTimeFormatOptions);
const ro = original.resolvedOptions();
// DateTimeFormat instances are very expensive to create. Therefore, they will
// be lazily created only when needed, using the locale and options provided.
// But it's possible for callers to mutate those inputs before lazy creation
// happens. For this reason, we clone the inputs instead of caching the
// original objects. To avoid the complexity of deep cloning any inputs that
// are themselves objects (e.g. the locales array, or options property values
// that will be coerced to strings), we rely on `resolvedOptions()` to do the
// coercion and cloning for us. Unfortunately, we can't just use the resolved
// options as-is because our options-amending logic adds additional fields if
// the user doesn't supply any unit fields like year, month, day, hour, etc.
// Therefore, we limit the properties in the clone to properties that were
// present in the original input.
if (hasOptions) {
const clonedResolved = ObjectAssign({}, ro);
for (const prop in clonedResolved) {
if (!ReflectApply(ObjectHasOwnProperty, options, [prop])) {
delete clonedResolved[prop as keyof typeof clonedResolved];
}
}
this[OPTIONS] = clonedResolved as Intl.DateTimeFormatOptions;
} else {
this[OPTIONS] = options;
}
this[LOCALE] = ro.locale;
this[ORIGINAL] = original;
this[TZ_RESOLVED] = ro.timeZone;
this[CAL_ID] = ro.calendar;
this[DATE] = dateAmend;
this[YM] = yearMonthAmend;
this[MD] = monthDayAmend;
this[TIME] = timeAmend;
this[DATETIME] = datetimeAmend;
this[INST] = instantAmend;
return undefined; // TODO: I couldn't satisfy TS without adding this. Is there another way?
}
Object.defineProperty(DateTimeFormatImpl, 'name', {
writable: true,
value: 'DateTimeFormat'
});
DateTimeFormatImpl.supportedLocalesOf = function (
locales: Params['supportedLocalesOf'][0],
options: Params['supportedLocalesOf'][1]
) {
return IntlDateTimeFormat.supportedLocalesOf(locales, options as globalThis.Intl.DateTimeFormatOptions);
};
const propertyDescriptors: Partial<Record<keyof Intl.DateTimeFormat, PropertyDescriptor>> = {
resolvedOptions: descriptor(resolvedOptions),
format: descriptor(format),
formatRange: descriptor(formatRange)
};
if ('formatToParts' in IntlDateTimeFormat.prototype) {
propertyDescriptors.formatToParts = descriptor(formatToParts);
}
if ('formatRangeToParts' in IntlDateTimeFormat.prototype) {
propertyDescriptors.formatRangeToParts = descriptor(formatRangeToParts);
}
DateTimeFormatImpl.prototype = Object.create(IntlDateTimeFormat.prototype, propertyDescriptors);
// Ensure that the prototype isn't writeable.
Object.defineProperty(DateTimeFormatImpl, 'prototype', {
writable: false,
enumerable: false,
configurable: false
});
export const DateTimeFormat = DateTimeFormatImpl as unknown as typeof Intl.DateTimeFormat;
function resolvedOptions(this: DateTimeFormatImpl): Return['resolvedOptions'] {
return this[ORIGINAL].resolvedOptions();
}
// TODO: investigate why there's a rest parameter here. Does this function really need to accept extra params?
// And if so, why doesn't formatRange also accept extra params?
function format<P extends readonly unknown[]>(
this: DateTimeFormatImpl,
datetime: Params['format'][0],
...rest: P
): Return['format'] {
let { instant, formatter } = extractOverrides(datetime, this);
if (instant && formatter) {
return formatter.format(instant.epochMilliseconds);
}
// Support spreading additional args for future expansion of this Intl method
type AllowExtraParams = (datetime: Parameters<Intl.DateTimeFormat['format']>[0], ...rest: P) => Return['format'];
return (this[ORIGINAL].format as unknown as AllowExtraParams)(datetime, ...rest);
}
function formatToParts<P extends readonly unknown[]>(
this: DateTimeFormatImpl,
datetime: Params['formatToParts'][0],
...rest: P
): Return['formatToParts'] {
let { instant, formatter } = extractOverrides(datetime, this);
if (instant && formatter) {
return formatter.formatToParts(instant.epochMilliseconds);
}
// Support spreading additional args for future expansion of this Intl method
type AllowExtraParams = (
datetime: Parameters<Intl.DateTimeFormat['formatToParts']>[0],
...rest: P
) => Return['formatToParts'];
return (this[ORIGINAL].formatToParts as unknown as AllowExtraParams)(datetime, ...rest);
}
function formatRange(this: DateTimeFormatImpl, a: Params['formatRange'][0], b: Params['formatRange'][1]) {
if (isTemporalObject(a) || isTemporalObject(b)) {
if (!sameTemporalType(a, b)) {
throw new TypeError('Intl.DateTimeFormat.formatRange accepts two values of the same type');
}
const { instant: aa, formatter: aformatter } = extractOverrides(a as unknown as TypesWithToLocaleString, this);
const { instant: bb, formatter: bformatter } = extractOverrides(b as unknown as TypesWithToLocaleString, this);
if (aa && bb && aformatter && bformatter && aformatter === bformatter) {
// TODO: Remove type assertion after this method lands in TS lib types
return (aformatter as Intl.DateTimeFormat).formatRange(aa.epochMilliseconds, bb.epochMilliseconds);
}
}
// TODO: Remove type assertion after this method lands in TS lib types
return (this[ORIGINAL] as Intl.DateTimeFormat).formatRange(a, b);
}
function formatRangeToParts(
this: DateTimeFormatImpl,
a: Params['formatRangeToParts'][0],
b: Params['formatRangeToParts'][1]
) {
if (isTemporalObject(a) || isTemporalObject(b)) {
if (!sameTemporalType(a, b)) {
throw new TypeError('Intl.DateTimeFormat.formatRangeToParts accepts two values of the same type');
}
const { instant: aa, formatter: aformatter } = extractOverrides(a, this);
const { instant: bb, formatter: bformatter } = extractOverrides(b, this);
if (aa && bb && aformatter && bformatter && aformatter === bformatter) {
// TODO: Remove type assertion after this method lands in TS lib types
return (aformatter as Intl.DateTimeFormat).formatRangeToParts(aa.epochMilliseconds, bb.epochMilliseconds);
}
}
// TODO: Remove type assertion after this method lands in TS lib types
return (this[ORIGINAL] as Intl.DateTimeFormat).formatRangeToParts(a, b);
}
// "false" is a signal to delete this option
type MaybeFalseOptions = {
[K in keyof Intl.DateTimeFormatOptions]?: Intl.DateTimeFormatOptions[K] | false;
};
function amend(optionsParam: Intl.DateTimeFormatOptions = {}, amended: MaybeFalseOptions = {}) {
const options = ObjectAssign({}, optionsParam);
for (const opt of [
'year',
'month',
'day',
'hour',
'minute',
'second',
'weekday',
'dayPeriod',
'timeZoneName',
'dateStyle',
'timeStyle'
] as const) {
// TODO: can this be typed more cleanly?
type OptionMaybeFalse = typeof options[typeof opt] | false;
(options[opt] as OptionMaybeFalse) = opt in amended ? amended[opt] : options[opt];
if ((options[opt] as OptionMaybeFalse) === false || options[opt] === undefined) delete options[opt];
}
return options as globalThis.Intl.DateTimeFormatOptions;
}
type OptionsType<T extends TypesWithToLocaleString> = NonNullable<Parameters<T['toLocaleString']>[1]>;
function timeAmend(optionsParam: OptionsType<Temporal.PlainTime>) {
let options = amend(optionsParam, {
year: false,
month: false,
day: false,
weekday: false,
timeZoneName: false,
dateStyle: false
});
if (!hasTimeOptions(options)) {
options = ObjectAssign({}, options, {
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
}
return options;
}
function yearMonthAmend(optionsParam: OptionsType<Temporal.PlainYearMonth>) {
let options = amend(optionsParam, {
day: false,
hour: false,
minute: false,
second: false,
weekday: false,
dayPeriod: false,
timeZoneName: false,
dateStyle: false,
timeStyle: false
});
if (!('year' in options || 'month' in options)) {
options = ObjectAssign(options, { year: 'numeric', month: 'numeric' });
}
return options;
}
function monthDayAmend(optionsParam: OptionsType<Temporal.PlainMonthDay>) {
let options = amend(optionsParam, {
year: false,
hour: false,
minute: false,
second: false,
weekday: false,
dayPeriod: false,
timeZoneName: false,
dateStyle: false,
timeStyle: false
});
if (!('month' in options || 'day' in options)) {
options = ObjectAssign({}, options, { month: 'numeric', day: 'numeric' });
}
return options;
}
function dateAmend(optionsParam: OptionsType<Temporal.PlainDate>) {
let options = amend(optionsParam, {
hour: false,
minute: false,
second: false,
dayPeriod: false,
timeZoneName: false,
timeStyle: false
});
if (!hasDateOptions(options)) {
options = ObjectAssign({}, options, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
});
}
return options;
}
function datetimeAmend(optionsParam: OptionsType<Temporal.PlainDateTime>) {
let options = amend(optionsParam, { timeZoneName: false });
if (!hasTimeOptions(options) && !hasDateOptions(options)) {
options = ObjectAssign({}, options, {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
}
return options;
}
function instantAmend(optionsParam: OptionsType<Temporal.Instant>) {
let options = optionsParam;
if (!hasTimeOptions(options) && !hasDateOptions(options)) {
options = ObjectAssign({}, options, {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
}
return options;
}
function hasDateOptions(options: OptionsType<TypesWithToLocaleString>) {
return 'year' in options || 'month' in options || 'day' in options || 'weekday' in options || 'dateStyle' in options;
}
function hasTimeOptions(options: OptionsType<TypesWithToLocaleString>) {
return (
'hour' in options || 'minute' in options || 'second' in options || 'timeStyle' in options || 'dayPeriod' in options
);
}
function isTemporalObject(
obj: unknown
): obj is
| Temporal.PlainDate
| Temporal.PlainTime
| Temporal.PlainDateTime
| Temporal.ZonedDateTime
| Temporal.PlainYearMonth
| Temporal.PlainMonthDay
| Temporal.Instant {
return (
ES.IsTemporalDate(obj) ||
ES.IsTemporalTime(obj) ||
ES.IsTemporalDateTime(obj) ||
ES.IsTemporalZonedDateTime(obj) ||
ES.IsTemporalYearMonth(obj) ||
ES.IsTemporalMonthDay(obj) ||
ES.IsTemporalInstant(obj)
);
}
function sameTemporalType(x: unknown, y: unknown) {
if (!isTemporalObject(x) || !isTemporalObject(y)) return false;
if (ES.IsTemporalTime(x) && !ES.IsTemporalTime(y)) return false;
if (ES.IsTemporalDate(x) && !ES.IsTemporalDate(y)) return false;
if (ES.IsTemporalDateTime(x) && !ES.IsTemporalDateTime(y)) return false;
if (ES.IsTemporalZonedDateTime(x) && !ES.IsTemporalZonedDateTime(y)) return false;
if (ES.IsTemporalYearMonth(x) && !ES.IsTemporalYearMonth(y)) return false;
if (ES.IsTemporalMonthDay(x) && !ES.IsTemporalMonthDay(y)) return false;
if (ES.IsTemporalInstant(x) && !ES.IsTemporalInstant(y)) return false;
return true;
}
type TypesWithToLocaleString =
| Temporal.PlainDateTime
| Temporal.PlainDate
| Temporal.PlainTime
| Temporal.PlainYearMonth
| Temporal.PlainMonthDay
| Temporal.ZonedDateTime
| Temporal.Instant;
function extractOverrides(temporalObj: Params['format'][0], main: DateTimeFormatImpl) {
const DateTime = GetIntrinsic('%Temporal.PlainDateTime%');
if (ES.IsTemporalTime(temporalObj)) {
const hour = GetSlot(temporalObj, ISO_HOUR);
const minute = GetSlot(temporalObj, ISO_MINUTE);
const second = GetSlot(temporalObj, ISO_SECOND);
const millisecond = GetSlot(temporalObj, ISO_MILLISECOND);
const microsecond = GetSlot(temporalObj, ISO_MICROSECOND);
const nanosecond = GetSlot(temporalObj, ISO_NANOSECOND);
const datetime = new DateTime(1970, 1, 1, hour, minute, second, millisecond, microsecond, nanosecond, main[CAL_ID]);
return {
instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
formatter: getPropLazy(main, TIME)
};
}
if (ES.IsTemporalYearMonth(temporalObj)) {
const isoYear = GetSlot(temporalObj, ISO_YEAR);
const isoMonth = GetSlot(temporalObj, ISO_MONTH);
const referenceISODay = GetSlot(temporalObj, ISO_DAY);
const calendar = ES.ToTemporalCalendarIdentifier(GetSlot(temporalObj, CALENDAR));
if (calendar !== main[CAL_ID]) {
throw new RangeError(
`cannot format PlainYearMonth with calendar ${calendar} in locale with calendar ${main[CAL_ID]}`
);
}
const datetime = new DateTime(isoYear, isoMonth, referenceISODay, 12, 0, 0, 0, 0, 0, calendar);
return {
instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
formatter: getPropLazy(main, YM)
};
}
if (ES.IsTemporalMonthDay(temporalObj)) {
const referenceISOYear = GetSlot(temporalObj, ISO_YEAR);
const isoMonth = GetSlot(temporalObj, ISO_MONTH);
const isoDay = GetSlot(temporalObj, ISO_DAY);
const calendar = ES.ToTemporalCalendarIdentifier(GetSlot(temporalObj, CALENDAR));
if (calendar !== main[CAL_ID]) {
throw new RangeError(
`cannot format PlainMonthDay with calendar ${calendar} in locale with calendar ${main[CAL_ID]}`
);
}
const datetime = new DateTime(referenceISOYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0, calendar);
return {
instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
formatter: getPropLazy(main, MD)
};
}
if (ES.IsTemporalDate(temporalObj)) {
const isoYear = GetSlot(temporalObj, ISO_YEAR);
const isoMonth = GetSlot(temporalObj, ISO_MONTH);
const isoDay = GetSlot(temporalObj, ISO_DAY);
const calendar = ES.ToTemporalCalendarIdentifier(GetSlot(temporalObj, CALENDAR));
if (calendar !== 'iso8601' && calendar !== main[CAL_ID]) {
throw new RangeError(`cannot format PlainDate with calendar ${calendar} in locale with calendar ${main[CAL_ID]}`);
}
const datetime = new DateTime(isoYear, isoMonth, isoDay, 12, 0, 0, 0, 0, 0, main[CAL_ID]);
return {
instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
formatter: getPropLazy(main, DATE)
};
}
if (ES.IsTemporalDateTime(temporalObj)) {
const isoYear = GetSlot(temporalObj, ISO_YEAR);
const isoMonth = GetSlot(temporalObj, ISO_MONTH);
const isoDay = GetSlot(temporalObj, ISO_DAY);
const hour = GetSlot(temporalObj, ISO_HOUR);
const minute = GetSlot(temporalObj, ISO_MINUTE);
const second = GetSlot(temporalObj, ISO_SECOND);
const millisecond = GetSlot(temporalObj, ISO_MILLISECOND);
const microsecond = GetSlot(temporalObj, ISO_MICROSECOND);
const nanosecond = GetSlot(temporalObj, ISO_NANOSECOND);
const calendar = ES.ToTemporalCalendarIdentifier(GetSlot(temporalObj, CALENDAR));
if (calendar !== 'iso8601' && calendar !== main[CAL_ID]) {
throw new RangeError(
`cannot format PlainDateTime with calendar ${calendar} in locale with calendar ${main[CAL_ID]}`
);
}
let datetime = temporalObj;
if (calendar === 'iso8601') {
datetime = new DateTime(
isoYear,
isoMonth,
isoDay,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
main[CAL_ID]
);
}
return {
instant: ES.GetInstantFor(main[TZ_RESOLVED], datetime, 'compatible'),
formatter: getPropLazy(main, DATETIME)
};
}
if (ES.IsTemporalZonedDateTime(temporalObj)) {
throw new TypeError(
'Temporal.ZonedDateTime not supported in DateTimeFormat methods. Use toLocaleString() instead.'
);
}
if (ES.IsTemporalInstant(temporalObj)) {
return {
instant: temporalObj,
formatter: getPropLazy(main, INST)
};
}
return {};
}
@@ -0,0 +1,165 @@
import type JSBI from 'jsbi';
import type { Temporal } from '..';
import { DEBUG } from './debug';
type OmitConstructor<T> = { [P in keyof T as T[P] extends new (...args: any[]) => any ? P : never]: T[P] };
type TemporalIntrinsics = Omit<typeof Temporal, 'Now' | 'Instant' | 'ZonedDateTime'> & {
Instant: OmitConstructor<Temporal.Instant> &
(new (epochNanoseconds: JSBI) => Temporal.Instant) & { prototype: typeof Temporal.Instant.prototype };
ZonedDateTime: OmitConstructor<Temporal.ZonedDateTime> &
(new (
epochNanoseconds: JSBI,
timeZone: string | Temporal.TimeZoneProtocol,
calendar?: string | Temporal.CalendarProtocol
) => Temporal.ZonedDateTime) & {
prototype: typeof Temporal.ZonedDateTime.prototype;
from: typeof Temporal.ZonedDateTime.from;
compare: typeof Temporal.ZonedDateTime.compare;
};
};
type TemporalIntrinsicRegistrations = {
[key in keyof TemporalIntrinsics as `Temporal.${key}`]: TemporalIntrinsics[key];
};
type TemporalIntrinsicPrototypeRegistrations = {
[key in keyof TemporalIntrinsics as `Temporal.${key}.prototype`]: TemporalIntrinsics[key]['prototype'];
};
type TemporalIntrinsicRegisteredKeys = {
[key in keyof TemporalIntrinsicRegistrations as `%${key}%`]: TemporalIntrinsicRegistrations[key];
};
type TemporalIntrinsicPrototypeRegisteredKeys = {
[key in keyof TemporalIntrinsicPrototypeRegistrations as `%${key}%`]: TemporalIntrinsicPrototypeRegistrations[key];
};
type CalendarPrototypeKeys = keyof Omit<Temporal.Calendar, typeof Symbol.toStringTag>;
type TemporalCalendarIntrinsicRegistrations = {
[key in CalendarPrototypeKeys as `Temporal.Calendar.prototype.${key}`]: Temporal.Calendar[key];
} & {
'Temporal.Calendar.from': typeof Temporal.Calendar.from;
};
type TemporalCalendarIntrinsicRegisteredKeys = {
[key in keyof TemporalCalendarIntrinsicRegistrations as `%${key}%`]: TemporalCalendarIntrinsicRegistrations[key];
};
type TimeZonePrototypeKeys = 'getOffsetNanosecondsFor' | 'getPossibleInstantsFor';
type TemporalTimeZoneIntrinsicRegistrations = {
[key in TimeZonePrototypeKeys as `Temporal.TimeZone.prototype.${key}`]: Temporal.TimeZone[key];
} & {
'Temporal.TimeZone.from': typeof Temporal.TimeZone.from;
};
type TemporalTimeZoneIntrinsicRegisteredKeys = {
[key in keyof TemporalTimeZoneIntrinsicRegistrations as `%${key}%`]: TemporalTimeZoneIntrinsicRegistrations[key];
};
const INTRINSICS = {} as TemporalIntrinsicRegisteredKeys &
TemporalIntrinsicPrototypeRegisteredKeys &
TemporalTimeZoneIntrinsicRegisteredKeys &
TemporalCalendarIntrinsicRegisteredKeys;
type customFormatFunction<T> = (
this: T,
depth: number,
options: { stylize: (value: unknown, type: 'number' | 'special') => string }
) => string;
const customUtilInspectFormatters: Partial<{
[key in keyof TemporalIntrinsicRegistrations]: customFormatFunction<
InstanceType<TemporalIntrinsicRegistrations[key]>
>;
}> = {
['Temporal.Duration'](depth, options) {
const descr = options.stylize(`${this[Symbol.toStringTag]} <${this}>`, 'special');
if (depth < 1) return descr;
const entries = [];
for (const prop of [
'years',
'months',
'weeks',
'days',
'hours',
'minutes',
'seconds',
'milliseconds',
'microseconds',
'nanoseconds'
] as const) {
if (this[prop] !== 0) entries.push(` ${prop}: ${options.stylize(this[prop], 'number')}`);
}
return descr + ' {\n' + entries.join(',\n') + '\n}';
}
};
type InspectFormatterOptions = { stylize: (str: string, styleType: string) => string };
function defaultUtilInspectFormatter(this: any, depth: number, options: InspectFormatterOptions) {
return options.stylize(`${this[Symbol.toStringTag]} <${this}>`, 'special');
}
export function MakeIntrinsicClass(
Class: TemporalIntrinsicRegistrations[typeof name],
name: keyof TemporalIntrinsicRegistrations
) {
Object.defineProperty(Class.prototype, Symbol.toStringTag, {
value: name,
writable: false,
enumerable: false,
configurable: true
});
if (DEBUG) {
Object.defineProperty(Class.prototype, Symbol.for('nodejs.util.inspect.custom'), {
value: customUtilInspectFormatters[name] || defaultUtilInspectFormatter,
writable: false,
enumerable: false,
configurable: true
});
}
for (const prop of Object.getOwnPropertyNames(Class)) {
// we know that `prop` is present, so the descriptor is never undefined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const desc = Object.getOwnPropertyDescriptor(Class, prop)!;
if (!desc.configurable || !desc.enumerable) continue;
desc.enumerable = false;
Object.defineProperty(Class, prop, desc);
}
for (const prop of Object.getOwnPropertyNames(Class.prototype)) {
// we know that `prop` is present, so the descriptor is never undefined
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const desc = Object.getOwnPropertyDescriptor(Class.prototype, prop)!;
if (!desc.configurable || !desc.enumerable) continue;
desc.enumerable = false;
Object.defineProperty(Class.prototype, prop, desc);
}
DefineIntrinsic(name, Class);
DefineIntrinsic(`${name}.prototype`, Class.prototype);
}
type IntrinsicDefinitionKeys =
| keyof TemporalIntrinsicRegistrations
| keyof TemporalIntrinsicPrototypeRegistrations
| keyof TemporalCalendarIntrinsicRegistrations
| keyof TemporalTimeZoneIntrinsicRegistrations;
export function DefineIntrinsic<KeyT extends keyof TemporalIntrinsicRegistrations>(
name: KeyT,
value: TemporalIntrinsicRegistrations[KeyT]
): void;
export function DefineIntrinsic<KeyT extends keyof TemporalIntrinsicPrototypeRegistrations>(
name: KeyT,
value: TemporalIntrinsicPrototypeRegistrations[KeyT]
): void;
export function DefineIntrinsic<KeyT extends keyof TemporalCalendarIntrinsicRegistrations>(
name: KeyT,
value: TemporalCalendarIntrinsicRegistrations[KeyT]
): void;
export function DefineIntrinsic<KeyT extends keyof TemporalTimeZoneIntrinsicRegistrations>(
name: KeyT,
value: TemporalTimeZoneIntrinsicRegistrations[KeyT]
): void;
export function DefineIntrinsic<KeyT>(name: KeyT, value: never): void;
export function DefineIntrinsic<KeyT extends IntrinsicDefinitionKeys>(name: KeyT, value: unknown): void {
const key: `%${IntrinsicDefinitionKeys}%` = `%${name}%`;
if (INTRINSICS[key] !== undefined) throw new Error(`intrinsic ${name} already exists`);
INTRINSICS[key] = value;
}
export function GetIntrinsic<KeyT extends keyof typeof INTRINSICS>(intrinsic: KeyT): typeof INTRINSICS[KeyT] {
return INTRINSICS[intrinsic];
}
+10
View File
@@ -0,0 +1,10 @@
import { Instant } from './instant';
import JSBI from 'jsbi';
import { MILLION } from './ecmascript';
export function toTemporalInstant(this: Date) {
// Observable access to valueOf is not correct here, but unavoidable
const epochNanoseconds = JSBI.multiply(JSBI.BigInt(+this), MILLION);
return new Instant(epochNanoseconds);
}
+64
View File
@@ -0,0 +1,64 @@
import * as ES from './ecmascript';
import { GetIntrinsic } from './intrinsicclass';
import type { Temporal } from '..';
const instant: typeof Temporal.Now['instant'] = () => {
const Instant = GetIntrinsic('%Temporal.Instant%');
return new Instant(ES.SystemUTCEpochNanoSeconds());
};
const plainDateTime: typeof Temporal.Now['plainDateTime'] = (
calendarLike,
temporalTimeZoneLike = ES.DefaultTimeZone()
) => {
const tZ = ES.ToTemporalTimeZoneSlotValue(temporalTimeZoneLike);
const calendar = ES.ToTemporalCalendarSlotValue(calendarLike);
const inst = instant();
return ES.GetPlainDateTimeFor(tZ, inst, calendar);
};
const plainDateTimeISO: typeof Temporal.Now['plainDateTimeISO'] = (temporalTimeZoneLike = ES.DefaultTimeZone()) => {
const tZ = ES.ToTemporalTimeZoneSlotValue(temporalTimeZoneLike);
const inst = instant();
return ES.GetPlainDateTimeFor(tZ, inst, 'iso8601');
};
const zonedDateTime: typeof Temporal.Now['zonedDateTime'] = (
calendarLike,
temporalTimeZoneLike = ES.DefaultTimeZone()
) => {
const tZ = ES.ToTemporalTimeZoneSlotValue(temporalTimeZoneLike);
const calendar = ES.ToTemporalCalendarSlotValue(calendarLike);
return ES.CreateTemporalZonedDateTime(ES.SystemUTCEpochNanoSeconds(), tZ, calendar);
};
const zonedDateTimeISO: typeof Temporal.Now['zonedDateTimeISO'] = (temporalTimeZoneLike = ES.DefaultTimeZone()) => {
return zonedDateTime('iso8601', temporalTimeZoneLike);
};
const plainDate: typeof Temporal.Now['plainDate'] = (calendarLike, temporalTimeZoneLike = ES.DefaultTimeZone()) => {
return ES.TemporalDateTimeToDate(plainDateTime(calendarLike, temporalTimeZoneLike));
};
const plainDateISO: typeof Temporal.Now['plainDateISO'] = (temporalTimeZoneLike = ES.DefaultTimeZone()) => {
return ES.TemporalDateTimeToDate(plainDateTimeISO(temporalTimeZoneLike));
};
const plainTimeISO: typeof Temporal.Now['plainTimeISO'] = (temporalTimeZoneLike = ES.DefaultTimeZone()) => {
return ES.TemporalDateTimeToTime(plainDateTimeISO(temporalTimeZoneLike));
};
const timeZoneId: typeof Temporal.Now['timeZoneId'] = () => {
return ES.DefaultTimeZone();
};
export const Now: typeof Temporal.Now = {
instant,
plainDateTime,
plainDateTimeISO,
plainDate,
plainDateISO,
plainTimeISO,
timeZoneId,
zonedDateTime,
zonedDateTimeISO,
[Symbol.toStringTag]: 'Temporal.Now'
};
Object.defineProperty(Now, Symbol.toStringTag, {
value: 'Temporal.Now',
writable: false,
enumerable: false,
configurable: true
});
+333
View File
@@ -0,0 +1,333 @@
import * as ES from './ecmascript';
import { MakeIntrinsicClass } from './intrinsicclass';
import {
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND,
CALENDAR,
EPOCHNANOSECONDS,
GetSlot
} from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { PlainDateParams as Params, PlainDateReturn as Return } from './internaltypes';
export class PlainDate implements Temporal.PlainDate {
constructor(
isoYearParam: Params['constructor'][0],
isoMonthParam: Params['constructor'][1],
isoDayParam: Params['constructor'][2],
calendarParam: Params['constructor'][3] = 'iso8601'
) {
const isoYear = ES.ToIntegerWithTruncation(isoYearParam);
const isoMonth = ES.ToIntegerWithTruncation(isoMonthParam);
const isoDay = ES.ToIntegerWithTruncation(isoDayParam);
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
ES.CreateTemporalDateSlots(this, isoYear, isoMonth, isoDay, calendar);
}
get calendarId(): Return['calendarId'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarIdentifier(GetSlot(this, CALENDAR));
}
get era(): Return['era'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarEra(GetSlot(this, CALENDAR), this);
}
get eraYear(): Return['eraYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarEraYear(GetSlot(this, CALENDAR), this);
}
get year(): Return['year'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarYear(GetSlot(this, CALENDAR), this);
}
get month(): Return['month'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonth(GetSlot(this, CALENDAR), this);
}
get monthCode(): Return['monthCode'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthCode(GetSlot(this, CALENDAR), this);
}
get day(): Return['day'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarDay(GetSlot(this, CALENDAR), this);
}
get dayOfWeek(): Return['dayOfWeek'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarDayOfWeek(GetSlot(this, CALENDAR), this);
}
get dayOfYear(): Return['dayOfYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarDayOfYear(GetSlot(this, CALENDAR), this);
}
get weekOfYear(): Return['weekOfYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarWeekOfYear(GetSlot(this, CALENDAR), this);
}
get yearOfWeek(): Return['weekOfYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarYearOfWeek(GetSlot(this, CALENDAR), this);
}
get daysInWeek(): Return['daysInWeek'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInWeek(GetSlot(this, CALENDAR), this);
}
get daysInMonth(): Return['daysInMonth'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInMonth(GetSlot(this, CALENDAR), this);
}
get daysInYear(): Return['daysInYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInYear(GetSlot(this, CALENDAR), this);
}
get monthsInYear(): Return['monthsInYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthsInYear(GetSlot(this, CALENDAR), this);
}
get inLeapYear(): Return['inLeapYear'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.CalendarInLeapYear(GetSlot(this, CALENDAR), this);
}
with(temporalDateLike: Params['with'][0], optionsParam: Params['with'][1] = undefined): Return['with'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(temporalDateLike)) {
throw new TypeError('invalid argument');
}
ES.RejectTemporalLikeObject(temporalDateLike);
const options = ES.GetOptionsObject(optionsParam);
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['day', 'month', 'monthCode', 'year'] as const);
let fields = ES.PrepareTemporalFields(this, fieldNames, []);
const partialDate = ES.PrepareTemporalFields(temporalDateLike, fieldNames, 'partial');
fields = ES.CalendarMergeFields(calendar, fields, partialDate);
fields = ES.PrepareTemporalFields(fields, fieldNames, []);
return ES.CalendarDateFromFields(calendar, fields, options);
}
withCalendar(calendarParam: Params['withCalendar'][0]): Return['withCalendar'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
return new PlainDate(GetSlot(this, ISO_YEAR), GetSlot(this, ISO_MONTH), GetSlot(this, ISO_DAY), calendar);
}
add(temporalDurationLike: Params['add'][0], optionsParam: Params['add'][1] = undefined): Return['add'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const duration = ES.ToTemporalDuration(temporalDurationLike);
const options = ES.GetOptionsObject(optionsParam);
return ES.CalendarDateAdd(GetSlot(this, CALENDAR), this, duration, options);
}
subtract(
temporalDurationLike: Params['subtract'][0],
optionsParam: Params['subtract'][1] = undefined
): Return['subtract'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const duration = ES.CreateNegatedTemporalDuration(ES.ToTemporalDuration(temporalDurationLike));
const options = ES.GetOptionsObject(optionsParam);
return ES.CalendarDateAdd(GetSlot(this, CALENDAR), this, duration, options);
}
until(other: Params['until'][0], options: Params['until'][1] = undefined): Return['until'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainDate('until', this, other, options);
}
since(other: Params['since'][0], options: Params['since'][1] = undefined): Return['since'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainDate('since', this, other, options);
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalDate(otherParam);
for (const slot of [ISO_YEAR, ISO_MONTH, ISO_DAY]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return ES.CalendarEquals(GetSlot(this, CALENDAR), GetSlot(other, CALENDAR));
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const showCalendar = ES.ToCalendarNameOption(options);
return ES.TemporalDateToString(this, showCalendar);
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.TemporalDateToString(this);
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return new DateTimeFormat(locales, options).format(this);
}
valueOf(): never {
throw new TypeError('use compare() or equals() to compare Temporal.PlainDate');
}
toPlainDateTime(temporalTimeParam: Params['toPlainDateTime'][0] = undefined): Return['toPlainDateTime'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const year = GetSlot(this, ISO_YEAR);
const month = GetSlot(this, ISO_MONTH);
const day = GetSlot(this, ISO_DAY);
const calendar = GetSlot(this, CALENDAR);
if (temporalTimeParam === undefined) return ES.CreateTemporalDateTime(year, month, day, 0, 0, 0, 0, 0, 0, calendar);
const temporalTime = ES.ToTemporalTime(temporalTimeParam);
const hour = GetSlot(temporalTime, ISO_HOUR);
const minute = GetSlot(temporalTime, ISO_MINUTE);
const second = GetSlot(temporalTime, ISO_SECOND);
const millisecond = GetSlot(temporalTime, ISO_MILLISECOND);
const microsecond = GetSlot(temporalTime, ISO_MICROSECOND);
const nanosecond = GetSlot(temporalTime, ISO_NANOSECOND);
return ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
}
toZonedDateTime(item: Params['toZonedDateTime'][0]): Return['toZonedDateTime'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
type TimeZoneAndPlainTimeProps = Exclude<typeof item, string | Temporal.TimeZoneProtocol>;
let timeZone: Temporal.TimeZoneLike, temporalTime: TimeZoneAndPlainTimeProps['plainTime'];
if (ES.IsObject(item)) {
if (ES.IsTemporalTimeZone(item)) {
timeZone = item;
} else {
const timeZoneLike = (item as TimeZoneAndPlainTimeProps).timeZone;
if (timeZoneLike === undefined) {
ES.uncheckedAssertNarrowedType<Temporal.TimeZoneProtocol>(
item,
"if no timeZone property, then assume it's a custom time zone object"
);
timeZone = ES.ToTemporalTimeZoneSlotValue(item);
} else {
timeZone = ES.ToTemporalTimeZoneSlotValue(timeZoneLike);
ES.uncheckedAssertNarrowedType<TimeZoneAndPlainTimeProps>(
item,
"it's a property bag with a timeZone and optional plainTime"
);
temporalTime = item.plainTime;
}
}
} else {
timeZone = ES.ToTemporalTimeZoneSlotValue(item);
}
const year = GetSlot(this, ISO_YEAR);
const month = GetSlot(this, ISO_MONTH);
const day = GetSlot(this, ISO_DAY);
const calendar = GetSlot(this, CALENDAR);
let hour = 0,
minute = 0,
second = 0,
millisecond = 0,
microsecond = 0,
nanosecond = 0;
if (temporalTime !== undefined) {
temporalTime = ES.ToTemporalTime(temporalTime);
ES.uncheckedAssertNarrowedType<Temporal.PlainTime>(
temporalTime,
'ToTemporalTime above always returns a PlainTime'
);
hour = GetSlot(temporalTime, ISO_HOUR);
minute = GetSlot(temporalTime, ISO_MINUTE);
second = GetSlot(temporalTime, ISO_SECOND);
millisecond = GetSlot(temporalTime, ISO_MILLISECOND);
microsecond = GetSlot(temporalTime, ISO_MICROSECOND);
nanosecond = GetSlot(temporalTime, ISO_NANOSECOND);
}
const dt = ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
const instant = ES.GetInstantFor(timeZone, dt, 'compatible');
return ES.CreateTemporalZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendar);
}
toPlainYearMonth(): Return['toPlainYearMonth'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['monthCode', 'year'] as const);
const fields = ES.PrepareTemporalFields(this, fieldNames, []);
return ES.CalendarYearMonthFromFields(calendar, fields);
}
toPlainMonthDay(): Return['toPlainMonthDay'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['day', 'monthCode'] as const);
const fields = ES.PrepareTemporalFields(this, fieldNames, []);
return ES.CalendarMonthDayFromFields(calendar, fields);
}
getISOFields(): Return['getISOFields'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return {
calendar: GetSlot(this, CALENDAR),
isoDay: GetSlot(this, ISO_DAY),
isoMonth: GetSlot(this, ISO_MONTH),
isoYear: GetSlot(this, ISO_YEAR)
};
}
getCalendar(): Return['getCalendar'] {
if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarObject(GetSlot(this, CALENDAR));
}
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
const options = ES.GetOptionsObject(optionsParam);
if (ES.IsTemporalDate(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
return ES.CreateTemporalDate(
GetSlot(item, ISO_YEAR),
GetSlot(item, ISO_MONTH),
GetSlot(item, ISO_DAY),
GetSlot(item, CALENDAR)
);
}
return ES.ToTemporalDate(item, options);
}
static compare(oneParam: Params['compare'][0], twoParam: Params['compare'][1]): Return['compare'] {
const one = ES.ToTemporalDate(oneParam);
const two = ES.ToTemporalDate(twoParam);
return ES.CompareISODate(
GetSlot(one, ISO_YEAR),
GetSlot(one, ISO_MONTH),
GetSlot(one, ISO_DAY),
GetSlot(two, ISO_YEAR),
GetSlot(two, ISO_MONTH),
GetSlot(two, ISO_DAY)
);
}
[Symbol.toStringTag]!: 'Temporal.PlainDate';
}
MakeIntrinsicClass(PlainDate, 'Temporal.PlainDate');
+485
View File
@@ -0,0 +1,485 @@
import * as ES from './ecmascript';
import { MakeIntrinsicClass } from './intrinsicclass';
import {
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND,
CALENDAR,
EPOCHNANOSECONDS,
GetSlot
} from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { PlainDateTimeParams as Params, PlainDateTimeReturn as Return } from './internaltypes';
export class PlainDateTime implements Temporal.PlainDateTime {
constructor(
isoYearParam: Params['constructor'][0],
isoMonthParam: Params['constructor'][1],
isoDayParam: Params['constructor'][2],
hourParam: Params['constructor'][3] = 0,
minuteParam: Params['constructor'][4] = 0,
secondParam: Params['constructor'][5] = 0,
millisecondParam: Params['constructor'][6] = 0,
microsecondParam: Params['constructor'][7] = 0,
nanosecondParam: Params['constructor'][8] = 0,
calendarParam: Params['constructor'][9] = 'iso8601'
) {
const isoYear = ES.ToIntegerWithTruncation(isoYearParam);
const isoMonth = ES.ToIntegerWithTruncation(isoMonthParam);
const isoDay = ES.ToIntegerWithTruncation(isoDayParam);
const hour = hourParam === undefined ? 0 : ES.ToIntegerWithTruncation(hourParam);
const minute = minuteParam === undefined ? 0 : ES.ToIntegerWithTruncation(minuteParam);
const second = secondParam === undefined ? 0 : ES.ToIntegerWithTruncation(secondParam);
const millisecond = millisecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(millisecondParam);
const microsecond = microsecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(microsecondParam);
const nanosecond = nanosecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(nanosecondParam);
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
ES.CreateTemporalDateTimeSlots(
this,
isoYear,
isoMonth,
isoDay,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
}
get calendarId(): Return['calendarId'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarIdentifier(GetSlot(this, CALENDAR));
}
get year(): Return['year'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarYear(GetSlot(this, CALENDAR), this);
}
get month(): Return['month'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonth(GetSlot(this, CALENDAR), this);
}
get monthCode(): Return['monthCode'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthCode(GetSlot(this, CALENDAR), this);
}
get day(): Return['day'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDay(GetSlot(this, CALENDAR), this);
}
get hour(): Return['hour'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_HOUR);
}
get minute(): Return['minute'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_MINUTE);
}
get second(): Return['second'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_SECOND);
}
get millisecond(): Return['millisecond'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_MILLISECOND);
}
get microsecond(): Return['microsecond'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_MICROSECOND);
}
get nanosecond(): Return['nanosecond'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_NANOSECOND);
}
get era(): Return['era'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarEra(GetSlot(this, CALENDAR), this);
}
get eraYear(): Return['eraYear'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarEraYear(GetSlot(this, CALENDAR), this);
}
get dayOfWeek(): Return['dayOfWeek'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDayOfWeek(GetSlot(this, CALENDAR), this);
}
get dayOfYear(): Return['dayOfYear'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDayOfYear(GetSlot(this, CALENDAR), this);
}
get weekOfYear(): Return['weekOfYear'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarWeekOfYear(GetSlot(this, CALENDAR), this);
}
get yearOfWeek(): Return['yearOfWeek'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarYearOfWeek(GetSlot(this, CALENDAR), this);
}
get daysInWeek(): Return['daysInWeek'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInWeek(GetSlot(this, CALENDAR), this);
}
get daysInYear(): Return['daysInYear'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInYear(GetSlot(this, CALENDAR), this);
}
get daysInMonth(): Return['daysInMonth'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInMonth(GetSlot(this, CALENDAR), this);
}
get monthsInYear(): Return['monthsInYear'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthsInYear(GetSlot(this, CALENDAR), this);
}
get inLeapYear(): Return['inLeapYear'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarInLeapYear(GetSlot(this, CALENDAR), this);
}
with(temporalDateTimeLike: Params['with'][0], optionsParam: Params['with'][1] = undefined): Return['with'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(temporalDateTimeLike)) {
throw new TypeError('invalid argument');
}
ES.RejectTemporalLikeObject(temporalDateTimeLike);
const options = ES.GetOptionsObject(optionsParam);
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, [
'day',
'hour',
'microsecond',
'millisecond',
'minute',
'month',
'monthCode',
'nanosecond',
'second',
'year'
] as const);
let fields = ES.PrepareTemporalFields(this, fieldNames, []);
const partialDateTime = ES.PrepareTemporalFields(temporalDateTimeLike, fieldNames, 'partial');
fields = ES.CalendarMergeFields(calendar, fields, partialDateTime);
fields = ES.PrepareTemporalFields(fields, fieldNames, []);
const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } =
ES.InterpretTemporalDateTimeFields(calendar, fields, options);
return ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
}
withPlainTime(temporalTimeParam: Params['withPlainTime'][0] = undefined): Return['withPlainTime'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const year = GetSlot(this, ISO_YEAR);
const month = GetSlot(this, ISO_MONTH);
const day = GetSlot(this, ISO_DAY);
const calendar = GetSlot(this, CALENDAR);
if (temporalTimeParam === undefined) return ES.CreateTemporalDateTime(year, month, day, 0, 0, 0, 0, 0, 0, calendar);
const temporalTime = ES.ToTemporalTime(temporalTimeParam);
const hour = GetSlot(temporalTime, ISO_HOUR);
const minute = GetSlot(temporalTime, ISO_MINUTE);
const second = GetSlot(temporalTime, ISO_SECOND);
const millisecond = GetSlot(temporalTime, ISO_MILLISECOND);
const microsecond = GetSlot(temporalTime, ISO_MICROSECOND);
const nanosecond = GetSlot(temporalTime, ISO_NANOSECOND);
return ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
}
withPlainDate(temporalDateParam: Params['withPlainDate'][0]): Return['withPlainDate'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const temporalDate = ES.ToTemporalDate(temporalDateParam);
const year = GetSlot(temporalDate, ISO_YEAR);
const month = GetSlot(temporalDate, ISO_MONTH);
const day = GetSlot(temporalDate, ISO_DAY);
let calendar = GetSlot(temporalDate, CALENDAR);
const hour = GetSlot(this, ISO_HOUR);
const minute = GetSlot(this, ISO_MINUTE);
const second = GetSlot(this, ISO_SECOND);
const millisecond = GetSlot(this, ISO_MILLISECOND);
const microsecond = GetSlot(this, ISO_MICROSECOND);
const nanosecond = GetSlot(this, ISO_NANOSECOND);
calendar = ES.ConsolidateCalendars(GetSlot(this, CALENDAR), calendar);
return ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
}
withCalendar(calendarParam: Params['withCalendar'][0]): Return['withCalendar'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
return new PlainDateTime(
GetSlot(this, ISO_YEAR),
GetSlot(this, ISO_MONTH),
GetSlot(this, ISO_DAY),
GetSlot(this, ISO_HOUR),
GetSlot(this, ISO_MINUTE),
GetSlot(this, ISO_SECOND),
GetSlot(this, ISO_MILLISECOND),
GetSlot(this, ISO_MICROSECOND),
GetSlot(this, ISO_NANOSECOND),
calendar
);
}
add(temporalDurationLike: Params['add'][0], options: Params['add'][1] = undefined): Return['add'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromPlainDateTime('add', this, temporalDurationLike, options);
}
subtract(
temporalDurationLike: Params['subtract'][0],
options: Params['subtract'][1] = undefined
): Return['subtract'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromPlainDateTime('subtract', this, temporalDurationLike, options);
}
until(other: Params['until'][0], options: Params['until'][1] = undefined): Return['until'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainDateTime('until', this, other, options);
}
since(other: Params['since'][0], options: Params['since'][1] = undefined): Return['since'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainDateTime('since', this, other, options);
}
round(roundToParam: Params['round'][0]): Return['round'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
if (roundToParam === undefined) throw new TypeError('options parameter is required');
const roundTo =
typeof roundToParam === 'string'
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
: ES.GetOptionsObject(roundToParam);
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
const maximumIncrements = {
day: 1,
hour: 24,
minute: 60,
second: 60,
millisecond: 1000,
microsecond: 1000,
nanosecond: 1000
};
const maximum = maximumIncrements[smallestUnit];
const inclusive = maximum === 1;
ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, inclusive);
let year = GetSlot(this, ISO_YEAR);
let month = GetSlot(this, ISO_MONTH);
let day = GetSlot(this, ISO_DAY);
let hour = GetSlot(this, ISO_HOUR);
let minute = GetSlot(this, ISO_MINUTE);
let second = GetSlot(this, ISO_SECOND);
let millisecond = GetSlot(this, ISO_MILLISECOND);
let microsecond = GetSlot(this, ISO_MICROSECOND);
let nanosecond = GetSlot(this, ISO_NANOSECOND);
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.RoundISODateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
roundingIncrement,
smallestUnit,
roundingMode
));
return ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
GetSlot(this, CALENDAR)
);
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalDateTime(otherParam);
for (const slot of [
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND
]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return ES.CalendarEquals(GetSlot(this, CALENDAR), GetSlot(other, CALENDAR));
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const showCalendar = ES.ToCalendarNameOption(options);
const digits = ES.ToFractionalSecondDigits(options);
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
return ES.TemporalDateTimeToString(this, precision, showCalendar, { unit, increment, roundingMode });
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.TemporalDateTimeToString(this, 'auto');
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return new DateTimeFormat(locales, options).format(this);
}
valueOf(): never {
throw new TypeError('use compare() or equals() to compare Temporal.PlainDateTime');
}
toZonedDateTime(
temporalTimeZoneLike: Params['toZonedDateTime'][0],
optionsParam: Params['toZonedDateTime'][1] = undefined
): Return['toZonedDateTime'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const timeZone = ES.ToTemporalTimeZoneSlotValue(temporalTimeZoneLike);
const options = ES.GetOptionsObject(optionsParam);
const disambiguation = ES.ToTemporalDisambiguation(options);
const instant = ES.GetInstantFor(timeZone, this, disambiguation);
return ES.CreateTemporalZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, GetSlot(this, CALENDAR));
}
toPlainDate(): Return['toPlainDate'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.TemporalDateTimeToDate(this);
}
toPlainYearMonth(): Return['toPlainYearMonth'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['monthCode', 'year'] as const);
const fields = ES.PrepareTemporalFields(this, fieldNames, []);
return ES.CalendarYearMonthFromFields(calendar, fields);
}
toPlainMonthDay(): Return['toPlainMonthDay'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['day', 'monthCode'] as const);
const fields = ES.PrepareTemporalFields(this, fieldNames, []);
return ES.CalendarMonthDayFromFields(calendar, fields);
}
toPlainTime(): Return['toPlainTime'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.TemporalDateTimeToTime(this);
}
getISOFields(): Return['getISOFields'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return {
calendar: GetSlot(this, CALENDAR),
isoDay: GetSlot(this, ISO_DAY),
isoHour: GetSlot(this, ISO_HOUR),
isoMicrosecond: GetSlot(this, ISO_MICROSECOND),
isoMillisecond: GetSlot(this, ISO_MILLISECOND),
isoMinute: GetSlot(this, ISO_MINUTE),
isoMonth: GetSlot(this, ISO_MONTH),
isoNanosecond: GetSlot(this, ISO_NANOSECOND),
isoSecond: GetSlot(this, ISO_SECOND),
isoYear: GetSlot(this, ISO_YEAR)
};
}
getCalendar(): Return['getCalendar'] {
if (!ES.IsTemporalDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarObject(GetSlot(this, CALENDAR));
}
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
const options = ES.GetOptionsObject(optionsParam);
if (ES.IsTemporalDateTime(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
return ES.CreateTemporalDateTime(
GetSlot(item, ISO_YEAR),
GetSlot(item, ISO_MONTH),
GetSlot(item, ISO_DAY),
GetSlot(item, ISO_HOUR),
GetSlot(item, ISO_MINUTE),
GetSlot(item, ISO_SECOND),
GetSlot(item, ISO_MILLISECOND),
GetSlot(item, ISO_MICROSECOND),
GetSlot(item, ISO_NANOSECOND),
GetSlot(item, CALENDAR)
);
}
return ES.ToTemporalDateTime(item, options);
}
static compare(oneParam: Params['compare'][0], twoParam: Params['compare'][1]): Return['compare'] {
const one = ES.ToTemporalDateTime(oneParam);
const two = ES.ToTemporalDateTime(twoParam);
for (const slot of [
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND
] as const) {
const val1 = GetSlot(one, slot);
const val2 = GetSlot(two, slot);
if (val1 !== val2) return ES.ComparisonResult(val1 - val2);
}
return 0;
}
[Symbol.toStringTag]!: 'Temporal.PlainDateTime';
}
MakeIntrinsicClass(PlainDateTime, 'Temporal.PlainDateTime');
+134
View File
@@ -0,0 +1,134 @@
import * as ES from './ecmascript';
import { MakeIntrinsicClass } from './intrinsicclass';
import { ISO_MONTH, ISO_DAY, ISO_YEAR, CALENDAR, GetSlot } from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { PlainMonthDayParams as Params, PlainMonthDayReturn as Return } from './internaltypes';
const ObjectCreate = Object.create;
export class PlainMonthDay implements Temporal.PlainMonthDay {
constructor(
isoMonthParam: Params['constructor'][0],
isoDayParam: Params['constructor'][0],
calendarParam: string | Temporal.CalendarProtocol = 'iso8601',
referenceISOYearParam = 1972
) {
const isoMonth = ES.ToIntegerWithTruncation(isoMonthParam);
const isoDay = ES.ToIntegerWithTruncation(isoDayParam);
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
const referenceISOYear = ES.ToIntegerWithTruncation(referenceISOYearParam);
ES.CreateTemporalMonthDaySlots(this, isoMonth, isoDay, calendar, referenceISOYear);
}
get monthCode(): Return['monthCode'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthCode(GetSlot(this, CALENDAR), this);
}
get day(): Return['day'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return ES.CalendarDay(GetSlot(this, CALENDAR), this);
}
get calendarId(): Return['calendarId'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarIdentifier(GetSlot(this, CALENDAR));
}
with(temporalMonthDayLike: Params['with'][0], optionsParam: Params['with'][1] = undefined): Return['with'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(temporalMonthDayLike)) {
throw new TypeError('invalid argument');
}
ES.RejectTemporalLikeObject(temporalMonthDayLike);
const options = ES.GetOptionsObject(optionsParam);
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['day', 'month', 'monthCode', 'year'] as const);
let fields = ES.PrepareTemporalFields(this, fieldNames, []);
const partialMonthDay = ES.PrepareTemporalFields(temporalMonthDayLike, fieldNames, 'partial');
fields = ES.CalendarMergeFields(calendar, fields, partialMonthDay);
fields = ES.PrepareTemporalFields(fields, fieldNames, []);
return ES.CalendarMonthDayFromFields(calendar, fields, options);
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalMonthDay(otherParam);
for (const slot of [ISO_MONTH, ISO_DAY, ISO_YEAR]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return ES.CalendarEquals(GetSlot(this, CALENDAR), GetSlot(other, CALENDAR));
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const showCalendar = ES.ToCalendarNameOption(options);
return ES.TemporalMonthDayToString(this, showCalendar);
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return ES.TemporalMonthDayToString(this);
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return new DateTimeFormat(locales, options).format(this);
}
valueOf(): never {
throw new TypeError('use equals() to compare Temporal.PlainMonthDay');
}
toPlainDate(item: Params['toPlainDate'][0]): Return['toPlainDate'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(item)) throw new TypeError('argument should be an object');
const calendar = GetSlot(this, CALENDAR);
const receiverFieldNames = ES.CalendarFields(calendar, ['day', 'monthCode'] as const);
const fields = ES.PrepareTemporalFields(this, receiverFieldNames, []);
const inputFieldNames = ES.CalendarFields(calendar, ['year'] as const);
const inputFields = ES.PrepareTemporalFields(item, inputFieldNames, []);
let mergedFields = ES.CalendarMergeFields(calendar, fields, inputFields);
// TODO: Use MergeLists abstract operation.
const mergedFieldNames = [...new Set([...receiverFieldNames, ...inputFieldNames])];
mergedFields = ES.PrepareTemporalFields(mergedFields, mergedFieldNames, []);
const options = ObjectCreate(null);
options.overflow = 'reject';
return ES.CalendarDateFromFields(calendar, mergedFields, options);
}
getISOFields(): Return['getISOFields'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return {
calendar: GetSlot(this, CALENDAR),
isoDay: GetSlot(this, ISO_DAY),
isoMonth: GetSlot(this, ISO_MONTH),
isoYear: GetSlot(this, ISO_YEAR)
};
}
getCalendar(): Return['getCalendar'] {
if (!ES.IsTemporalMonthDay(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarObject(GetSlot(this, CALENDAR));
}
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
const options = ES.GetOptionsObject(optionsParam);
if (ES.IsTemporalMonthDay(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
return ES.CreateTemporalMonthDay(
GetSlot(item, ISO_MONTH),
GetSlot(item, ISO_DAY),
GetSlot(item, CALENDAR),
GetSlot(item, ISO_YEAR)
);
}
return ES.ToTemporalMonthDay(item, options);
}
[Symbol.toStringTag]!: 'Temporal.PlainMonthDay';
}
MakeIntrinsicClass(PlainMonthDay, 'Temporal.PlainMonthDay');
+357
View File
@@ -0,0 +1,357 @@
import { DEBUG } from './debug';
import * as ES from './ecmascript';
import { GetIntrinsic, MakeIntrinsicClass } from './intrinsicclass';
import {
ISO_YEAR,
ISO_MONTH,
ISO_DAY,
ISO_HOUR,
ISO_MINUTE,
ISO_SECOND,
ISO_MILLISECOND,
ISO_MICROSECOND,
ISO_NANOSECOND,
CALENDAR,
EPOCHNANOSECONDS,
CreateSlots,
GetSlot,
SetSlot
} from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { PlainTimeParams as Params, PlainTimeReturn as Return } from './internaltypes';
const ObjectAssign = Object.assign;
type TemporalTimeToStringOptions = {
unit: ReturnType<typeof ES.ToSecondsStringPrecisionRecord>['unit'];
increment: ReturnType<typeof ES.ToSecondsStringPrecisionRecord>['increment'];
roundingMode: Temporal.RoundingMode;
};
function TemporalTimeToString(
time: Temporal.PlainTime,
precision: ReturnType<typeof ES.ToSecondsStringPrecisionRecord>['precision'],
options: TemporalTimeToStringOptions | undefined = undefined
) {
let hour = GetSlot(time, ISO_HOUR);
let minute = GetSlot(time, ISO_MINUTE);
let second = GetSlot(time, ISO_SECOND);
let millisecond = GetSlot(time, ISO_MILLISECOND);
let microsecond = GetSlot(time, ISO_MICROSECOND);
let nanosecond = GetSlot(time, ISO_NANOSECOND);
if (options) {
const { unit, increment, roundingMode } = options;
({ hour, minute, second, millisecond, microsecond, nanosecond } = ES.RoundTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
increment,
unit,
roundingMode
));
}
const hourString = ES.ISODateTimePartString(hour);
const minuteString = ES.ISODateTimePartString(minute);
const seconds = ES.FormatSecondsStringPart(second, millisecond, microsecond, nanosecond, precision);
return `${hourString}:${minuteString}${seconds}`;
}
export class PlainTime implements Temporal.PlainTime {
constructor(
isoHourParam = 0,
isoMinuteParam = 0,
isoSecondParam = 0,
isoMillisecondParam = 0,
isoMicrosecondParam = 0,
isoNanosecondParam = 0
) {
const isoHour = isoHourParam === undefined ? 0 : ES.ToIntegerWithTruncation(isoHourParam);
const isoMinute = isoMinuteParam === undefined ? 0 : ES.ToIntegerWithTruncation(isoMinuteParam);
const isoSecond = isoSecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(isoSecondParam);
const isoMillisecond = isoMillisecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(isoMillisecondParam);
const isoMicrosecond = isoMicrosecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(isoMicrosecondParam);
const isoNanosecond = isoNanosecondParam === undefined ? 0 : ES.ToIntegerWithTruncation(isoNanosecondParam);
ES.RejectTime(isoHour, isoMinute, isoSecond, isoMillisecond, isoMicrosecond, isoNanosecond);
CreateSlots(this);
SetSlot(this, ISO_HOUR, isoHour);
SetSlot(this, ISO_MINUTE, isoMinute);
SetSlot(this, ISO_SECOND, isoSecond);
SetSlot(this, ISO_MILLISECOND, isoMillisecond);
SetSlot(this, ISO_MICROSECOND, isoMicrosecond);
SetSlot(this, ISO_NANOSECOND, isoNanosecond);
if (DEBUG) {
Object.defineProperty(this, '_repr_', {
value: `${this[Symbol.toStringTag]} <${TemporalTimeToString(this, 'auto')}>`,
writable: false,
enumerable: false,
configurable: false
});
}
}
get hour(): Return['hour'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_HOUR);
}
get minute(): Return['minute'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_MINUTE);
}
get second(): Return['second'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_SECOND);
}
get millisecond(): Return['millisecond'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_MILLISECOND);
}
get microsecond(): Return['microsecond'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_MICROSECOND);
}
get nanosecond(): Return['nanosecond'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return GetSlot(this, ISO_NANOSECOND);
}
with(temporalTimeLike: Params['with'][0], optionsParam: Params['with'][1] = undefined): Return['with'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(temporalTimeLike)) {
throw new TypeError('invalid argument');
}
ES.RejectTemporalLikeObject(temporalTimeLike);
const options = ES.GetOptionsObject(optionsParam);
const overflow = ES.ToTemporalOverflow(options);
const partialTime = ES.ToTemporalTimeRecord(temporalTimeLike, 'partial');
const fields = ES.ToTemporalTimeRecord(this);
let { hour, minute, second, millisecond, microsecond, nanosecond } = ObjectAssign(fields, partialTime);
({ hour, minute, second, millisecond, microsecond, nanosecond } = ES.RegulateTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
overflow
));
return new PlainTime(hour, minute, second, millisecond, microsecond, nanosecond);
}
add(temporalDurationLike: Params['add'][0]): Return['add'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromPlainTime('add', this, temporalDurationLike);
}
subtract(temporalDurationLike: Params['subtract'][0]): Return['subtract'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromPlainTime('subtract', this, temporalDurationLike);
}
until(other: Params['until'][0], options: Params['until'][1] = undefined): Return['until'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainTime('until', this, other, options);
}
since(other: Params['since'][0], options: Params['since'][1] = undefined): Return['since'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainTime('since', this, other, options);
}
round(roundToParam: Params['round'][0]): Return['round'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
if (roundToParam === undefined) throw new TypeError('options parameter is required');
const roundTo =
typeof roundToParam === 'string'
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
: ES.GetOptionsObject(roundToParam);
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED);
const MAX_INCREMENTS = {
hour: 24,
minute: 60,
second: 60,
millisecond: 1000,
microsecond: 1000,
nanosecond: 1000
};
ES.ValidateTemporalRoundingIncrement(roundingIncrement, MAX_INCREMENTS[smallestUnit], false);
let hour = GetSlot(this, ISO_HOUR);
let minute = GetSlot(this, ISO_MINUTE);
let second = GetSlot(this, ISO_SECOND);
let millisecond = GetSlot(this, ISO_MILLISECOND);
let microsecond = GetSlot(this, ISO_MICROSECOND);
let nanosecond = GetSlot(this, ISO_NANOSECOND);
({ hour, minute, second, millisecond, microsecond, nanosecond } = ES.RoundTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
roundingIncrement,
smallestUnit,
roundingMode
));
return new PlainTime(hour, minute, second, millisecond, microsecond, nanosecond);
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalTime(otherParam);
for (const slot of [ISO_HOUR, ISO_MINUTE, ISO_SECOND, ISO_MILLISECOND, ISO_MICROSECOND, ISO_NANOSECOND]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return true;
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const digits = ES.ToFractionalSecondDigits(options);
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
return TemporalTimeToString(this, precision, { unit, increment, roundingMode });
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return TemporalTimeToString(this, 'auto');
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return new DateTimeFormat(locales, options).format(this);
}
valueOf(): never {
throw new TypeError('use compare() or equals() to compare Temporal.PlainTime');
}
toPlainDateTime(temporalDateParam: Params['toPlainDateTime'][0]): Return['toPlainDateTime'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
const temporalDate = ES.ToTemporalDate(temporalDateParam);
const year = GetSlot(temporalDate, ISO_YEAR);
const month = GetSlot(temporalDate, ISO_MONTH);
const day = GetSlot(temporalDate, ISO_DAY);
const calendar = GetSlot(temporalDate, CALENDAR);
const hour = GetSlot(this, ISO_HOUR);
const minute = GetSlot(this, ISO_MINUTE);
const second = GetSlot(this, ISO_SECOND);
const millisecond = GetSlot(this, ISO_MILLISECOND);
const microsecond = GetSlot(this, ISO_MICROSECOND);
const nanosecond = GetSlot(this, ISO_NANOSECOND);
return ES.CreateTemporalDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
}
toZonedDateTime(item: Params['toZonedDateTime'][0]): Return['toZonedDateTime'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(item)) {
throw new TypeError('invalid argument');
}
const dateLike = item.plainDate;
if (dateLike === undefined) {
throw new TypeError('missing date property');
}
const temporalDate = ES.ToTemporalDate(dateLike);
const timeZoneLike = item.timeZone;
if (timeZoneLike === undefined) {
throw new TypeError('missing timeZone property');
}
const timeZone = ES.ToTemporalTimeZoneSlotValue(timeZoneLike);
const year = GetSlot(temporalDate, ISO_YEAR);
const month = GetSlot(temporalDate, ISO_MONTH);
const day = GetSlot(temporalDate, ISO_DAY);
const calendar = GetSlot(temporalDate, CALENDAR);
const hour = GetSlot(this, ISO_HOUR);
const minute = GetSlot(this, ISO_MINUTE);
const second = GetSlot(this, ISO_SECOND);
const millisecond = GetSlot(this, ISO_MILLISECOND);
const microsecond = GetSlot(this, ISO_MICROSECOND);
const nanosecond = GetSlot(this, ISO_NANOSECOND);
const PlainDateTime = GetIntrinsic('%Temporal.PlainDateTime%');
const dt = new PlainDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
const instant = ES.GetInstantFor(timeZone, dt, 'compatible');
return ES.CreateTemporalZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendar);
}
getISOFields(): Return['getISOFields'] {
if (!ES.IsTemporalTime(this)) throw new TypeError('invalid receiver');
return {
isoHour: GetSlot(this, ISO_HOUR),
isoMicrosecond: GetSlot(this, ISO_MICROSECOND),
isoMillisecond: GetSlot(this, ISO_MILLISECOND),
isoMinute: GetSlot(this, ISO_MINUTE),
isoNanosecond: GetSlot(this, ISO_NANOSECOND),
isoSecond: GetSlot(this, ISO_SECOND)
};
}
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
const options = ES.GetOptionsObject(optionsParam);
const overflow = ES.ToTemporalOverflow(options);
if (ES.IsTemporalTime(item)) {
return new PlainTime(
GetSlot(item, ISO_HOUR),
GetSlot(item, ISO_MINUTE),
GetSlot(item, ISO_SECOND),
GetSlot(item, ISO_MILLISECOND),
GetSlot(item, ISO_MICROSECOND),
GetSlot(item, ISO_NANOSECOND)
);
}
return ES.ToTemporalTime(item, overflow);
}
static compare(oneParam: Params['compare'][0], twoParam: Params['compare'][1]): Return['compare'] {
const one = ES.ToTemporalTime(oneParam);
const two = ES.ToTemporalTime(twoParam);
for (const slot of [ISO_HOUR, ISO_MINUTE, ISO_SECOND, ISO_MILLISECOND, ISO_MICROSECOND, ISO_NANOSECOND] as const) {
const val1 = GetSlot(one, slot);
const val2 = GetSlot(two, slot);
if (val1 !== val2) return ES.ComparisonResult(val1 - val2);
}
return 0;
}
[Symbol.toStringTag]!: 'Temporal.PlainTime';
}
MakeIntrinsicClass(PlainTime, 'Temporal.PlainTime');
@@ -0,0 +1,191 @@
import * as ES from './ecmascript';
import { MakeIntrinsicClass } from './intrinsicclass';
import { ISO_YEAR, ISO_MONTH, ISO_DAY, CALENDAR, GetSlot } from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { PlainYearMonthParams as Params, PlainYearMonthReturn as Return } from './internaltypes';
const ObjectCreate = Object.create;
export class PlainYearMonth implements Temporal.PlainYearMonth {
constructor(
isoYearParam: Params['constructor'][0],
isoMonthParam: Params['constructor'][1],
calendarParam: Params['constructor'][2] = 'iso8601',
referenceISODayParam: Params['constructor'][3] = 1
) {
const isoYear = ES.ToIntegerWithTruncation(isoYearParam);
const isoMonth = ES.ToIntegerWithTruncation(isoMonthParam);
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
const referenceISODay = ES.ToIntegerWithTruncation(referenceISODayParam);
ES.CreateTemporalYearMonthSlots(this, isoYear, isoMonth, calendar, referenceISODay);
}
get year(): Return['year'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarYear(GetSlot(this, CALENDAR), this);
}
get month(): Return['month'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonth(GetSlot(this, CALENDAR), this);
}
get monthCode(): Return['monthCode'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthCode(GetSlot(this, CALENDAR), this);
}
get calendarId(): Return['calendarId'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarIdentifier(GetSlot(this, CALENDAR));
}
get era(): Return['era'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarEra(GetSlot(this, CALENDAR), this);
}
get eraYear(): Return['eraYear'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarEraYear(GetSlot(this, CALENDAR), this);
}
get daysInMonth(): Return['daysInMonth'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInMonth(GetSlot(this, CALENDAR), this);
}
get daysInYear(): Return['daysInYear'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInYear(GetSlot(this, CALENDAR), this);
}
get monthsInYear(): Return['monthsInYear'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthsInYear(GetSlot(this, CALENDAR), this);
}
get inLeapYear(): Return['inLeapYear'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.CalendarInLeapYear(GetSlot(this, CALENDAR), this);
}
with(temporalYearMonthLike: Params['with'][0], optionsParam: Params['with'][1] = undefined): Return['with'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(temporalYearMonthLike)) {
throw new TypeError('invalid argument');
}
ES.RejectTemporalLikeObject(temporalYearMonthLike);
const options = ES.GetOptionsObject(optionsParam);
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['month', 'monthCode', 'year'] as const);
let fields = ES.PrepareTemporalFields(this, fieldNames, []);
const partialYearMonth = ES.PrepareTemporalFields(temporalYearMonthLike, fieldNames, 'partial');
fields = ES.CalendarMergeFields(calendar, fields, partialYearMonth);
fields = ES.PrepareTemporalFields(fields, fieldNames, []);
return ES.CalendarYearMonthFromFields(calendar, fields, options);
}
add(temporalDurationLike: Params['add'][0], options: Params['add'][1] = undefined): Return['add'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromPlainYearMonth('add', this, temporalDurationLike, options);
}
subtract(
temporalDurationLike: Params['subtract'][0],
options: Params['subtract'][1] = undefined
): Return['subtract'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromPlainYearMonth('subtract', this, temporalDurationLike, options);
}
until(other: Params['until'][0], options: Params['until'][1] = undefined): Return['until'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainYearMonth('until', this, other, options);
}
since(other: Params['since'][0], options: Params['since'][1] = undefined): Return['since'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalPlainYearMonth('since', this, other, options);
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalYearMonth(otherParam);
for (const slot of [ISO_YEAR, ISO_MONTH, ISO_DAY]) {
const val1 = GetSlot(this, slot);
const val2 = GetSlot(other, slot);
if (val1 !== val2) return false;
}
return ES.CalendarEquals(GetSlot(this, CALENDAR), GetSlot(other, CALENDAR));
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const showCalendar = ES.ToCalendarNameOption(options);
return ES.TemporalYearMonthToString(this, showCalendar);
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.TemporalYearMonthToString(this);
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
options: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return new DateTimeFormat(locales, options).format(this);
}
valueOf(): never {
throw new TypeError('use compare() or equals() to compare Temporal.PlainYearMonth');
}
toPlainDate(item: Params['toPlainDate'][0]): Return['toPlainDate'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(item)) throw new TypeError('argument should be an object');
const calendar = GetSlot(this, CALENDAR);
const receiverFieldNames = ES.CalendarFields(calendar, ['monthCode', 'year'] as const);
const fields = ES.PrepareTemporalFields(this, receiverFieldNames, []);
const inputFieldNames = ES.CalendarFields(calendar, ['day'] as const);
const inputFields = ES.PrepareTemporalFields(item, inputFieldNames, []);
let mergedFields = ES.CalendarMergeFields(calendar, fields, inputFields);
// TODO: Use MergeLists abstract operation.
const mergedFieldNames = [...new Set([...receiverFieldNames, ...inputFieldNames])];
mergedFields = ES.PrepareTemporalFields(mergedFields, mergedFieldNames, []);
const options = ObjectCreate(null);
options.overflow = 'reject';
return ES.CalendarDateFromFields(calendar, mergedFields, options);
}
getISOFields(): Return['getISOFields'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return {
calendar: GetSlot(this, CALENDAR),
isoDay: GetSlot(this, ISO_DAY),
isoMonth: GetSlot(this, ISO_MONTH),
isoYear: GetSlot(this, ISO_YEAR)
};
}
getCalendar(): Return['getCalendar'] {
if (!ES.IsTemporalYearMonth(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarObject(GetSlot(this, CALENDAR));
}
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
const options = ES.GetOptionsObject(optionsParam);
if (ES.IsTemporalYearMonth(item)) {
ES.ToTemporalOverflow(options); // validate and ignore
return ES.CreateTemporalYearMonth(
GetSlot(item, ISO_YEAR),
GetSlot(item, ISO_MONTH),
GetSlot(item, CALENDAR),
GetSlot(item, ISO_DAY)
);
}
return ES.ToTemporalYearMonth(item, options);
}
static compare(oneParam: Params['compare'][0], twoParam: Params['compare'][1]): Return['compare'] {
const one = ES.ToTemporalYearMonth(oneParam);
const two = ES.ToTemporalYearMonth(twoParam);
return ES.CompareISODate(
GetSlot(one, ISO_YEAR),
GetSlot(one, ISO_MONTH),
GetSlot(one, ISO_DAY),
GetSlot(two, ISO_YEAR),
GetSlot(two, ISO_MONTH),
GetSlot(two, ISO_DAY)
);
}
[Symbol.toStringTag]!: 'Temporal.PlainYearMonth';
}
MakeIntrinsicClass(PlainYearMonth, 'Temporal.PlainYearMonth');
+70
View File
@@ -0,0 +1,70 @@
const tzComponent = /\.[-A-Za-z_]|\.\.[-A-Za-z._]{1,12}|\.[-A-Za-z_][-A-Za-z._]{0,12}|[A-Za-z_][-A-Za-z._]{0,13}/;
const offsetNoCapture = /(?:[+\u2212-][0-2][0-9](?::?[0-5][0-9](?::?[0-5][0-9](?:[.,]\d{1,9})?)?)?)/;
export const timeZoneID = new RegExp(
'(?:' +
[
`(?:${tzComponent.source})(?:\\/(?:${tzComponent.source}))*`,
'Etc/GMT(?:0|[-+]\\d{1,2})',
'GMT[-+]?0',
'EST5EDT',
'CST6CDT',
'MST7MDT',
'PST8PDT',
offsetNoCapture.source
].join('|') +
')'
);
const yearpart = /(?:[+\u2212-]\d{6}|\d{4})/;
const monthpart = /(?:0[1-9]|1[0-2])/;
const daypart = /(?:0[1-9]|[12]\d|3[01])/;
const datesplit = new RegExp(
`(${yearpart.source})(?:-(${monthpart.source})-(${daypart.source})|(${monthpart.source})(${daypart.source}))`
);
const timesplit = /(\d{2})(?::(\d{2})(?::(\d{2})(?:[.,](\d{1,9}))?)?|(\d{2})(?:(\d{2})(?:[.,](\d{1,9}))?)?)?/;
export const offset = /([+\u2212-])([01][0-9]|2[0-3])(?::?([0-5][0-9])(?::?([0-5][0-9])(?:[.,](\d{1,9}))?)?)?/;
const offsetpart = new RegExp(`([zZ])|${offset.source}?`);
export const annotation = /\[(!)?([a-z_][a-z0-9_-]*)=([A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)\]/g;
export const zoneddatetime = new RegExp(
[
`^${datesplit.source}`,
`(?:(?:T|\\s+)${timesplit.source}(?:${offsetpart.source})?)?`,
`(?:\\[!?(${timeZoneID.source})\\])?`,
`((?:${annotation.source})*)$`
].join(''),
'i'
);
export const time = new RegExp(
[
`^T?${timesplit.source}`,
`(?:${offsetpart.source})?`,
`(?:\\[!?${timeZoneID.source}\\])?`,
`((?:${annotation.source})*)$`
].join(''),
'i'
);
// The short forms of YearMonth and MonthDay are only for the ISO calendar, but
// annotations are still allowed, and will throw if the calendar annotation is
// not ISO.
// Non-ISO calendar YearMonth and MonthDay have to parse as a Temporal.PlainDate,
// with the reference fields.
// YYYYMM forbidden by ISO 8601 because ambiguous with YYMMDD, but allowed by
// RFC 3339 and we don't allow 2-digit years, so we allow it.
// Not ambiguous with HHMMSS because that requires a 'T' prefix
// UTC offsets are not allowed, because they are not allowed with any date-only
// format; also, YYYY-MM-UU is ambiguous with YYYY-MM-DD
export const yearmonth = new RegExp(
`^(${yearpart.source})-?(${monthpart.source})(?:\\[!?${timeZoneID.source}\\])?((?:${annotation.source})*)$`
);
export const monthday = new RegExp(
`^(?:--)?(${monthpart.source})-?(${daypart.source})(?:\\[!?${timeZoneID.source}\\])?((?:${annotation.source})*)$`
);
const fraction = /(\d+)(?:[.,](\d{1,9}))?/;
const durationDate = /(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)W)?(?:(\d+)D)?/;
const durationTime = new RegExp(`(?:${fraction.source}H)?(?:${fraction.source}M)?(?:${fraction.source}S)?`);
export const duration = new RegExp(`^([+\u2212-])?P${durationDate.source}(?:T(?!$)${durationTime.source})?$`, 'i');
+315
View File
@@ -0,0 +1,315 @@
import type JSBI from 'jsbi';
import type { Temporal } from '..';
import type { BuiltinCalendarId, AnyTemporalType, CalendarSlot, TimeZoneSlot } from './internaltypes';
// Instant
export const EPOCHNANOSECONDS = 'slot-epochNanoSeconds';
// TimeZone
export const TIMEZONE_ID = 'slot-timezone-identifier';
// DateTime, Date, Time, YearMonth, MonthDay
export const ISO_YEAR = 'slot-year';
export const ISO_MONTH = 'slot-month';
export const ISO_DAY = 'slot-day';
export const ISO_HOUR = 'slot-hour';
export const ISO_MINUTE = 'slot-minute';
export const ISO_SECOND = 'slot-second';
export const ISO_MILLISECOND = 'slot-millisecond';
export const ISO_MICROSECOND = 'slot-microsecond';
export const ISO_NANOSECOND = 'slot-nanosecond';
export const CALENDAR = 'slot-calendar';
// Date, YearMonth, and MonthDay all have the same slots, disambiguation needed:
export const DATE_BRAND = 'slot-date-brand';
export const YEAR_MONTH_BRAND = 'slot-year-month-brand';
export const MONTH_DAY_BRAND = 'slot-month-day-brand';
// ZonedDateTime
export const INSTANT = 'slot-cached-instant';
export const TIME_ZONE = 'slot-time-zone';
// Duration
export const YEARS = 'slot-years';
export const MONTHS = 'slot-months';
export const WEEKS = 'slot-weeks';
export const DAYS = 'slot-days';
export const HOURS = 'slot-hours';
export const MINUTES = 'slot-minutes';
export const SECONDS = 'slot-seconds';
export const MILLISECONDS = 'slot-milliseconds';
export const MICROSECONDS = 'slot-microseconds';
export const NANOSECONDS = 'slot-nanoseconds';
// Calendar
export const CALENDAR_ID = 'slot-calendar-identifier';
interface SlotInfo<ValueType, UsedByType extends AnyTemporalType> {
value: ValueType;
usedBy: UsedByType;
}
interface SlotInfoRecord {
[k: string]: SlotInfo<unknown, AnyTemporalType>;
}
interface Slots extends SlotInfoRecord {
// Instant
[EPOCHNANOSECONDS]: SlotInfo<JSBI, Temporal.Instant | Temporal.ZonedDateTime>; // number? JSBI?
// TimeZone
[TIMEZONE_ID]: SlotInfo<string, Temporal.TimeZone>;
// DateTime, Date, Time, YearMonth, MonthDay
[ISO_YEAR]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MONTH]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_DAY]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_HOUR]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MINUTE]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_SECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MILLISECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_MICROSECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[ISO_NANOSECOND]: SlotInfo<number, TypesWithCalendarUnits>;
[CALENDAR]: SlotInfo<CalendarSlot, TypesWithCalendarUnits>;
// Date, YearMonth, MonthDay common slots
[DATE_BRAND]: SlotInfo<true, Temporal.PlainDate>;
[YEAR_MONTH_BRAND]: SlotInfo<true, Temporal.PlainYearMonth>;
[MONTH_DAY_BRAND]: SlotInfo<true, Temporal.PlainMonthDay>;
// ZonedDateTime
[INSTANT]: SlotInfo<Temporal.Instant, Temporal.ZonedDateTime>;
[TIME_ZONE]: SlotInfo<TimeZoneSlot, Temporal.ZonedDateTime>;
// Duration
[YEARS]: SlotInfo<number, Temporal.Duration>;
[MONTHS]: SlotInfo<number, Temporal.Duration>;
[WEEKS]: SlotInfo<number, Temporal.Duration>;
[DAYS]: SlotInfo<number, Temporal.Duration>;
[HOURS]: SlotInfo<number, Temporal.Duration>;
[MINUTES]: SlotInfo<number, Temporal.Duration>;
[SECONDS]: SlotInfo<number, Temporal.Duration>;
[MILLISECONDS]: SlotInfo<number, Temporal.Duration>;
[MICROSECONDS]: SlotInfo<number, Temporal.Duration>;
[NANOSECONDS]: SlotInfo<number, Temporal.Duration>;
// Calendar
[CALENDAR_ID]: SlotInfo<BuiltinCalendarId, Temporal.Calendar>;
}
type TypesWithCalendarUnits =
| Temporal.PlainDateTime
| Temporal.PlainDate
| Temporal.PlainTime
| Temporal.PlainYearMonth
| Temporal.PlainMonthDay
| Temporal.ZonedDateTime;
interface SlotsToTypes {
// Instant
[EPOCHNANOSECONDS]: Temporal.Instant;
// TimeZone
[TIMEZONE_ID]: Temporal.TimeZone;
// DateTime, Date, Time, YearMonth, MonthDay
[ISO_YEAR]: TypesWithCalendarUnits;
[ISO_MONTH]: TypesWithCalendarUnits;
[ISO_DAY]: TypesWithCalendarUnits;
[ISO_HOUR]: TypesWithCalendarUnits;
[ISO_MINUTE]: TypesWithCalendarUnits;
[ISO_SECOND]: TypesWithCalendarUnits;
[ISO_MILLISECOND]: TypesWithCalendarUnits;
[ISO_MICROSECOND]: TypesWithCalendarUnits;
[ISO_NANOSECOND]: TypesWithCalendarUnits;
[CALENDAR]: TypesWithCalendarUnits;
// Date, YearMonth, MonthDay common slots
[DATE_BRAND]: Temporal.PlainDate;
[YEAR_MONTH_BRAND]: Temporal.PlainYearMonth;
[MONTH_DAY_BRAND]: Temporal.PlainMonthDay;
// ZonedDateTime
[INSTANT]: Temporal.ZonedDateTime;
[TIME_ZONE]: Temporal.ZonedDateTime;
// Duration
[YEARS]: Temporal.Duration;
[MONTHS]: Temporal.Duration;
[WEEKS]: Temporal.Duration;
[DAYS]: Temporal.Duration;
[HOURS]: Temporal.Duration;
[MINUTES]: Temporal.Duration;
[SECONDS]: Temporal.Duration;
[MILLISECONDS]: Temporal.Duration;
[MICROSECONDS]: Temporal.Duration;
[NANOSECONDS]: Temporal.Duration;
// Calendar
[CALENDAR_ID]: Temporal.Calendar;
}
type SlotKey = keyof SlotsToTypes;
const globalSlots = new WeakMap<Slots[keyof Slots]['usedBy'], Record<keyof Slots, Slots[keyof Slots]['value']>>();
function _GetSlots(container: Slots[keyof Slots]['usedBy']) {
return globalSlots.get(container);
}
const GetSlotsSymbol = Symbol.for('@@Temporal__GetSlots');
// expose GetSlots to avoid dual package hazards
(globalThis as any)[GetSlotsSymbol] ||= _GetSlots;
const GetSlots = (globalThis as any)[GetSlotsSymbol] as typeof _GetSlots;
function _CreateSlots(container: Slots[keyof Slots]['usedBy']): void {
globalSlots.set(container, Object.create(null));
}
const CreateSlotsSymbol = Symbol.for('@@Temporal__CreateSlots');
// expose CreateSlots to avoid dual package hazards
(globalThis as any)[CreateSlotsSymbol] ||= _CreateSlots;
export const CreateSlots = (globalThis as any)[CreateSlotsSymbol] as typeof _CreateSlots;
// TODO: is there a better way than 9 overloads to make HasSlot into a type
// guard that takes a variable number of parameters?
export function HasSlot<ID1 extends SlotKey>(container: unknown, id1: ID1): container is Slots[ID1]['usedBy'];
export function HasSlot<ID1 extends SlotKey, ID2 extends SlotKey>(
container: unknown,
id1: ID1,
id2: ID2
): container is Slots[ID1]['usedBy'] | Slots[ID2]['usedBy'];
export function HasSlot<ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3
): container is Slots[ID1]['usedBy'] | Slots[ID2]['usedBy'] | Slots[ID3]['usedBy'];
export function HasSlot<ID1 extends SlotKey, ID2 extends SlotKey, ID3 extends SlotKey, ID4 extends SlotKey>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4
): container is Slots[ID1 | ID2 | ID3 | ID4]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey,
ID7 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6,
id7: ID7
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey,
ID7 extends SlotKey,
ID8 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6,
id7: ID7,
id8: ID8
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7 | ID8]['usedBy'];
export function HasSlot<
ID1 extends SlotKey,
ID2 extends SlotKey,
ID3 extends SlotKey,
ID4 extends SlotKey,
ID5 extends SlotKey,
ID6 extends SlotKey,
ID7 extends SlotKey,
ID8 extends SlotKey,
ID9 extends SlotKey
>(
container: unknown,
id1: ID1,
id2: ID2,
id3: ID3,
id4: ID4,
id5: ID5,
id6: ID6,
id7: ID7,
id8: ID8,
id9: ID9
): container is Slots[ID1 | ID2 | ID3 | ID4 | ID5 | ID6 | ID7 | ID8 | ID9]['usedBy'];
export function HasSlot(container: unknown, ...ids: (keyof Slots)[]): boolean {
if (!container || 'object' !== typeof container) return false;
const myslots = GetSlots(container as AnyTemporalType);
return !!myslots && ids.every((id) => id in myslots);
}
export function GetSlot<KeyT extends keyof Slots>(
container: Slots[typeof id]['usedBy'],
id: KeyT
): Slots[KeyT]['value'] {
const value = GetSlots(container)?.[id];
if (value === undefined) throw new TypeError(`Missing internal slot ${id}`);
return value;
}
export function SetSlot<KeyT extends SlotKey>(
container: Slots[KeyT]['usedBy'],
id: KeyT,
value: Slots[KeyT]['value']
): void {
const slots = GetSlots(container);
if (slots === undefined) throw new TypeError('Missing slots for the given container');
const existingSlot = slots[id];
if (existingSlot) throw new TypeError(`${id} already has set`);
slots[id] = value;
}
+11
View File
@@ -0,0 +1,11 @@
export { Instant } from './instant';
export { Calendar } from './calendar';
export { PlainDate } from './plaindate';
export { PlainDateTime } from './plaindatetime';
export { Duration } from './duration';
export { PlainMonthDay } from './plainmonthday';
export { Now } from './now';
export { PlainTime } from './plaintime';
export { TimeZone } from './timezone';
export { PlainYearMonth } from './plainyearmonth';
export { ZonedDateTime } from './zoneddatetime';
+168
View File
@@ -0,0 +1,168 @@
import { DEBUG } from './debug';
import * as ES from './ecmascript';
import { DefineIntrinsic, 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 GetSlot(this, TIMEZONE_ID);
}
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.IsTimeZoneOffsetString(id)) {
return ES.ParseTimeZoneOffsetString(id);
}
return ES.GetNamedTimeZoneOffsetNanoseconds(id, GetSlot(instant, EPOCHNANOSECONDS));
}
getOffsetStringFor(instantParam: Params['getOffsetStringFor'][0]): Return['getOffsetStringFor'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const instant = ES.ToTemporalInstant(instantParam);
return ES.GetOffsetStringFor(this, instant);
}
getPlainDateTimeFor(
instantParam: Params['getPlainDateTimeFor'][0],
calendarParam: Params['getPlainDateTimeFor'][1] = 'iso8601'
): Return['getPlainDateTimeFor'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
const instant = ES.ToTemporalInstant(instantParam);
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
return ES.GetPlainDateTimeFor(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.GetInstantFor(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.IsTimeZoneOffsetString(id)) {
const epochNs = ES.GetUTCEpochNanoseconds(
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.GetNamedTimeZoneEpochNanoseconds(
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.IsTimeZoneOffsetString(id) || id === 'UTC') {
return null;
}
let epochNanoseconds: JSBI | null = GetSlot(startingPoint, EPOCHNANOSECONDS);
const Instant = GetIntrinsic('%Temporal.Instant%');
epochNanoseconds = ES.GetNamedTimeZoneNextTransition(id, epochNanoseconds);
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.IsTimeZoneOffsetString(id) || id === 'UTC') {
return null;
}
let epochNanoseconds: JSBI | null = GetSlot(startingPoint, EPOCHNANOSECONDS);
const Instant = GetIntrinsic('%Temporal.Instant%');
epochNanoseconds = ES.GetNamedTimeZonePreviousTransition(id, epochNanoseconds);
return epochNanoseconds === null ? null : new Instant(epochNanoseconds);
}
toString(): string {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
return GetSlot(this, TIMEZONE_ID);
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalTimeZone(this)) throw new TypeError('invalid receiver');
return GetSlot(this, TIMEZONE_ID);
}
static from(item: Params['from'][0]): Return['from'] {
const timeZoneSlotValue = ES.ToTemporalTimeZoneSlotValue(item);
return ES.ToTemporalTimeZoneObject(timeZoneSlotValue);
}
[Symbol.toStringTag]!: 'Temporal.TimeZone';
}
MakeIntrinsicClass(TimeZone, 'Temporal.TimeZone');
DefineIntrinsic('Temporal.TimeZone.prototype.getOffsetNanosecondsFor', TimeZone.prototype.getOffsetNanosecondsFor);
DefineIntrinsic('Temporal.TimeZone.prototype.getPossibleInstantsFor', TimeZone.prototype.getPossibleInstantsFor);
+627
View File
@@ -0,0 +1,627 @@
import * as ES from './ecmascript';
import { GetIntrinsic, MakeIntrinsicClass } from './intrinsicclass';
import {
CALENDAR,
EPOCHNANOSECONDS,
ISO_HOUR,
INSTANT,
ISO_DAY,
ISO_MONTH,
ISO_YEAR,
ISO_MICROSECOND,
ISO_MILLISECOND,
ISO_MINUTE,
ISO_NANOSECOND,
ISO_SECOND,
TIME_ZONE,
GetSlot
} from './slots';
import type { Temporal } from '..';
import { DateTimeFormat } from './intl';
import type { ZonedDateTimeParams as Params, ZonedDateTimeReturn as Return } from './internaltypes';
import JSBI from 'jsbi';
import { BILLION, MILLION, THOUSAND, ZERO, HOUR_NANOS } from './ecmascript';
const customResolvedOptions = DateTimeFormat.prototype.resolvedOptions as Intl.DateTimeFormat['resolvedOptions'];
const ObjectCreate = Object.create;
export class ZonedDateTime implements Temporal.ZonedDateTime {
constructor(
epochNanosecondsParam: bigint | JSBI,
timeZoneParam: string | Temporal.TimeZoneProtocol,
calendarParam: string | Temporal.CalendarProtocol = 'iso8601'
) {
// Note: if the argument is not passed, ToBigInt(undefined) will throw. This check exists only
// to improve the error message.
// ToTemporalTimeZoneSlotValue(undefined) has a clear enough message.
if (arguments.length < 1) {
throw new TypeError('missing argument: epochNanoseconds is required');
}
const epochNanoseconds = ES.ToBigInt(epochNanosecondsParam);
const timeZone = ES.ToTemporalTimeZoneSlotValue(timeZoneParam);
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
ES.CreateTemporalZonedDateTimeSlots(this, epochNanoseconds, timeZone, calendar);
}
get calendarId(): Return['calendarId'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarIdentifier(GetSlot(this, CALENDAR));
}
get timeZoneId(): Return['timeZoneId'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalTimeZoneIdentifier(GetSlot(this, TIME_ZONE));
}
get year(): Return['year'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarYear(GetSlot(this, CALENDAR), dateTime(this));
}
get month(): Return['month'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonth(GetSlot(this, CALENDAR), dateTime(this));
}
get monthCode(): Return['monthCode'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthCode(GetSlot(this, CALENDAR), dateTime(this));
}
get day(): Return['day'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDay(GetSlot(this, CALENDAR), dateTime(this));
}
get hour(): Return['hour'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(dateTime(this), ISO_HOUR);
}
get minute(): Return['minute'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(dateTime(this), ISO_MINUTE);
}
get second(): Return['second'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(dateTime(this), ISO_SECOND);
}
get millisecond(): Return['millisecond'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(dateTime(this), ISO_MILLISECOND);
}
get microsecond(): Return['microsecond'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(dateTime(this), ISO_MICROSECOND);
}
get nanosecond(): Return['nanosecond'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return GetSlot(dateTime(this), ISO_NANOSECOND);
}
get era(): Return['era'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarEra(GetSlot(this, CALENDAR), dateTime(this));
}
get eraYear(): Return['eraYear'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarEraYear(GetSlot(this, CALENDAR), dateTime(this));
}
get epochSeconds(): Return['epochSeconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const value = GetSlot(this, EPOCHNANOSECONDS);
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(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(ES.BigIntFloorDiv(value, THOUSAND));
}
get epochNanoseconds(): Return['epochNanoseconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToBigIntExternal(GetSlot(this, EPOCHNANOSECONDS));
}
get dayOfWeek(): Return['dayOfWeek'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDayOfWeek(GetSlot(this, CALENDAR), dateTime(this));
}
get dayOfYear(): Return['dayOfYear'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDayOfYear(GetSlot(this, CALENDAR), dateTime(this));
}
get weekOfYear(): Return['weekOfYear'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarWeekOfYear(GetSlot(this, CALENDAR), dateTime(this));
}
get yearOfWeek(): Return['yearOfWeek'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarYearOfWeek(GetSlot(this, CALENDAR), dateTime(this));
}
get hoursInDay(): Return['hoursInDay'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const dt = dateTime(this);
const DateTime = GetIntrinsic('%Temporal.PlainDateTime%');
const year = GetSlot(dt, ISO_YEAR);
const month = GetSlot(dt, ISO_MONTH);
const day = GetSlot(dt, ISO_DAY);
const today = new DateTime(year, month, day, 0, 0, 0, 0, 0, 0);
const tomorrowFields = ES.AddISODate(year, month, day, 0, 0, 0, 1, 'reject');
const tomorrow = new DateTime(tomorrowFields.year, tomorrowFields.month, tomorrowFields.day, 0, 0, 0, 0, 0, 0);
const timeZone = GetSlot(this, TIME_ZONE);
const todayNs = GetSlot(ES.GetInstantFor(timeZone, today, 'compatible'), EPOCHNANOSECONDS);
const tomorrowNs = GetSlot(ES.GetInstantFor(timeZone, tomorrow, 'compatible'), EPOCHNANOSECONDS);
const diffNs = JSBI.subtract(tomorrowNs, todayNs);
return ES.BigIntDivideToNumber(diffNs, HOUR_NANOS);
}
get daysInWeek(): Return['daysInWeek'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInWeek(GetSlot(this, CALENDAR), dateTime(this));
}
get daysInMonth(): Return['daysInMonth'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInMonth(GetSlot(this, CALENDAR), dateTime(this));
}
get daysInYear(): Return['daysInYear'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarDaysInYear(GetSlot(this, CALENDAR), dateTime(this));
}
get monthsInYear(): Return['monthsInYear'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarMonthsInYear(GetSlot(this, CALENDAR), dateTime(this));
}
get inLeapYear(): Return['inLeapYear'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.CalendarInLeapYear(GetSlot(this, CALENDAR), dateTime(this));
}
get offset(): Return['offset'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.GetOffsetStringFor(GetSlot(this, TIME_ZONE), GetSlot(this, INSTANT));
}
get offsetNanoseconds(): Return['offsetNanoseconds'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.GetOffsetNanosecondsFor(GetSlot(this, TIME_ZONE), GetSlot(this, INSTANT));
}
with(temporalZonedDateTimeLike: Params['with'][0], optionsParam: Params['with'][1] = undefined): Return['with'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
if (!ES.IsObject(temporalZonedDateTimeLike)) {
throw new TypeError('invalid zoned-date-time-like');
}
ES.RejectTemporalLikeObject(temporalZonedDateTimeLike);
const options = ES.GetOptionsObject(optionsParam);
const calendar = GetSlot(this, CALENDAR);
let fieldNames: (keyof Temporal.ZonedDateTimeLike)[] = ES.CalendarFields(calendar, [
'day',
'hour',
'microsecond',
'millisecond',
'minute',
'month',
'monthCode',
'nanosecond',
'second',
'year'
] as const);
fieldNames.push('offset');
let fields = ES.PrepareTemporalFields(this, fieldNames, ['offset']);
const partialZonedDateTime = ES.PrepareTemporalFields(temporalZonedDateTimeLike, fieldNames, 'partial');
fields = ES.CalendarMergeFields(calendar, fields, partialZonedDateTime);
fields = ES.PrepareTemporalFields(fields, fieldNames, ['offset']);
const disambiguation = ES.ToTemporalDisambiguation(options);
const offset = ES.ToTemporalOffset(options, 'prefer');
let { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } =
ES.InterpretTemporalDateTimeFields(calendar, fields, options);
const offsetNs = ES.ParseTimeZoneOffsetString(fields.offset);
const timeZone = GetSlot(this, TIME_ZONE);
const epochNanoseconds = ES.InterpretISODateTimeOffset(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
'option',
offsetNs,
timeZone,
disambiguation,
offset,
/* matchMinute = */ false
);
return ES.CreateTemporalZonedDateTime(epochNanoseconds, timeZone, calendar);
}
withPlainDate(temporalDateParam: Params['withPlainDate'][0]): Return['withPlainDate'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const temporalDate = ES.ToTemporalDate(temporalDateParam);
const year = GetSlot(temporalDate, ISO_YEAR);
const month = GetSlot(temporalDate, ISO_MONTH);
const day = GetSlot(temporalDate, ISO_DAY);
let calendar = GetSlot(temporalDate, CALENDAR);
const thisDt = dateTime(this);
const hour = GetSlot(thisDt, ISO_HOUR);
const minute = GetSlot(thisDt, ISO_MINUTE);
const second = GetSlot(thisDt, ISO_SECOND);
const millisecond = GetSlot(thisDt, ISO_MILLISECOND);
const microsecond = GetSlot(thisDt, ISO_MICROSECOND);
const nanosecond = GetSlot(thisDt, ISO_NANOSECOND);
calendar = ES.ConsolidateCalendars(GetSlot(this, CALENDAR), calendar);
const timeZone = GetSlot(this, TIME_ZONE);
const PlainDateTime = GetIntrinsic('%Temporal.PlainDateTime%');
const dt = new PlainDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
const instant = ES.GetInstantFor(timeZone, dt, 'compatible');
return ES.CreateTemporalZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendar);
}
withPlainTime(temporalTimeParam: Params['withPlainTime'][0] = undefined): Return['withPlainTime'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const PlainTime = GetIntrinsic('%Temporal.PlainTime%');
const temporalTime = temporalTimeParam === undefined ? new PlainTime() : ES.ToTemporalTime(temporalTimeParam);
const thisDt = dateTime(this);
const year = GetSlot(thisDt, ISO_YEAR);
const month = GetSlot(thisDt, ISO_MONTH);
const day = GetSlot(thisDt, ISO_DAY);
const calendar = GetSlot(this, CALENDAR);
const hour = GetSlot(temporalTime, ISO_HOUR);
const minute = GetSlot(temporalTime, ISO_MINUTE);
const second = GetSlot(temporalTime, ISO_SECOND);
const millisecond = GetSlot(temporalTime, ISO_MILLISECOND);
const microsecond = GetSlot(temporalTime, ISO_MICROSECOND);
const nanosecond = GetSlot(temporalTime, ISO_NANOSECOND);
const timeZone = GetSlot(this, TIME_ZONE);
const PlainDateTime = GetIntrinsic('%Temporal.PlainDateTime%');
const dt = new PlainDateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
calendar
);
const instant = ES.GetInstantFor(timeZone, dt, 'compatible');
return ES.CreateTemporalZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendar);
}
withTimeZone(timeZoneParam: Params['withTimeZone'][0]): Return['withTimeZone'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const timeZone = ES.ToTemporalTimeZoneSlotValue(timeZoneParam);
return ES.CreateTemporalZonedDateTime(GetSlot(this, EPOCHNANOSECONDS), timeZone, GetSlot(this, CALENDAR));
}
withCalendar(calendarParam: Params['withCalendar'][0]): Return['withCalendar'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const calendar = ES.ToTemporalCalendarSlotValue(calendarParam);
return ES.CreateTemporalZonedDateTime(GetSlot(this, EPOCHNANOSECONDS), GetSlot(this, TIME_ZONE), calendar);
}
add(temporalDurationLike: Params['add'][0], options: Params['add'][1] = undefined): Return['add'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromZonedDateTime('add', this, temporalDurationLike, options);
}
subtract(
temporalDurationLike: Params['subtract'][0],
options: Params['subtract'][1] = undefined
): Return['subtract'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.AddDurationToOrSubtractDurationFromZonedDateTime('subtract', this, temporalDurationLike, options);
}
until(other: Params['until'][0], options: Params['until'][1] = undefined): Return['until'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalZonedDateTime('until', this, other, options);
}
since(other: Params['since'][0], options: Params['since'][1] = undefined): Return['since'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.DifferenceTemporalZonedDateTime('since', this, other, options);
}
round(roundToParam: Params['round'][0]): Return['round'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
if (roundToParam === undefined) throw new TypeError('options parameter is required');
const roundTo =
typeof roundToParam === 'string'
? (ES.CreateOnePropObject('smallestUnit', roundToParam) as Exclude<typeof roundToParam, string>)
: ES.GetOptionsObject(roundToParam);
const roundingIncrement = ES.ToTemporalRoundingIncrement(roundTo);
const roundingMode = ES.ToTemporalRoundingMode(roundTo, 'halfExpand');
const smallestUnit = ES.GetTemporalUnit(roundTo, 'smallestUnit', 'time', ES.REQUIRED, ['day']);
const maximumIncrements = {
day: 1,
hour: 24,
minute: 60,
second: 60,
millisecond: 1000,
microsecond: 1000,
nanosecond: 1000
};
const maximum = maximumIncrements[smallestUnit];
const inclusive = maximum === 1;
ES.ValidateTemporalRoundingIncrement(roundingIncrement, maximum, inclusive);
// first, round the underlying DateTime fields
const dt = dateTime(this);
let year = GetSlot(dt, ISO_YEAR);
let month = GetSlot(dt, ISO_MONTH);
let day = GetSlot(dt, ISO_DAY);
let hour = GetSlot(dt, ISO_HOUR);
let minute = GetSlot(dt, ISO_MINUTE);
let second = GetSlot(dt, ISO_SECOND);
let millisecond = GetSlot(dt, ISO_MILLISECOND);
let microsecond = GetSlot(dt, ISO_MICROSECOND);
let nanosecond = GetSlot(dt, ISO_NANOSECOND);
const DateTime = GetIntrinsic('%Temporal.PlainDateTime%');
const timeZone = GetSlot(this, TIME_ZONE);
const calendar = GetSlot(this, CALENDAR);
const dtStart = new DateTime(GetSlot(dt, ISO_YEAR), GetSlot(dt, ISO_MONTH), GetSlot(dt, ISO_DAY), 0, 0, 0, 0, 0, 0);
const instantStart = ES.GetInstantFor(timeZone, dtStart, 'compatible');
const endNs = ES.AddZonedDateTime(instantStart, timeZone, calendar, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0);
const dayLengthNs = JSBI.subtract(endNs, JSBI.BigInt(GetSlot(instantStart, EPOCHNANOSECONDS)));
if (JSBI.lessThanOrEqual(dayLengthNs, ZERO)) {
throw new RangeError('cannot round a ZonedDateTime in a calendar with zero or negative length days');
}
({ year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.RoundISODateTime(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
roundingIncrement,
smallestUnit,
roundingMode,
// Days are guaranteed to be shorter than Number.MAX_SAFE_INTEGER
// (which can hold up to 104 days in nanoseconds)
JSBI.toNumber(dayLengthNs)
));
// Now reset all DateTime fields but leave the TimeZone. The offset will
// also be retained if the new date/time values are still OK with the old
// offset. Otherwise the offset will be changed to be compatible with the
// new date/time values. If DST disambiguation is required, the `compatible`
// disambiguation algorithm will be used.
const offsetNs = ES.GetOffsetNanosecondsFor(timeZone, GetSlot(this, INSTANT));
const epochNanoseconds = ES.InterpretISODateTimeOffset(
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
'option',
offsetNs,
timeZone,
'compatible',
'prefer',
/* matchMinute = */ false
);
return ES.CreateTemporalZonedDateTime(epochNanoseconds, timeZone, GetSlot(this, CALENDAR));
}
equals(otherParam: Params['equals'][0]): Return['equals'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const other = ES.ToTemporalZonedDateTime(otherParam);
const one = GetSlot(this, EPOCHNANOSECONDS);
const two = GetSlot(other, EPOCHNANOSECONDS);
if (!JSBI.equal(JSBI.BigInt(one), JSBI.BigInt(two))) return false;
if (!ES.TimeZoneEquals(GetSlot(this, TIME_ZONE), GetSlot(other, TIME_ZONE))) return false;
return ES.CalendarEquals(GetSlot(this, CALENDAR), GetSlot(other, CALENDAR));
}
toString(optionsParam: Params['toString'][0] = undefined): string {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const showCalendar = ES.ToCalendarNameOption(options);
const digits = ES.ToFractionalSecondDigits(options);
const showOffset = ES.ToShowOffsetOption(options);
const roundingMode = ES.ToTemporalRoundingMode(options, 'trunc');
const smallestUnit = ES.GetTemporalUnit(options, 'smallestUnit', 'time', undefined);
if (smallestUnit === 'hour') throw new RangeError('smallestUnit must be a time unit other than "hour"');
const showTimeZone = ES.ToTimeZoneNameOption(options);
const { precision, unit, increment } = ES.ToSecondsStringPrecisionRecord(smallestUnit, digits);
return ES.TemporalZonedDateTimeToString(this, precision, showCalendar, showTimeZone, showOffset, {
unit,
increment,
roundingMode
});
}
toLocaleString(
locales: Params['toLocaleString'][0] = undefined,
optionsParam: Params['toLocaleString'][1] = undefined
): string {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const options = ES.GetOptionsObject(optionsParam);
const optionsCopy = ObjectCreate(null);
// This is not quite per specification, but this polyfill's DateTimeFormat
// already doesn't match the InitializeDateTimeFormat operation, and the
// access order might change anyway;
// see https://github.com/tc39/ecma402/issues/747
ES.CopyDataProperties(optionsCopy, options, ['timeZone']);
if (options.timeZone !== undefined) {
throw new TypeError('ZonedDateTime toLocaleString does not accept a timeZone option');
}
if (
optionsCopy.year === undefined &&
optionsCopy.month === undefined &&
optionsCopy.day === undefined &&
optionsCopy.weekday === undefined &&
optionsCopy.dateStyle === undefined &&
optionsCopy.hour === undefined &&
optionsCopy.minute === undefined &&
optionsCopy.second === undefined &&
optionsCopy.timeStyle === undefined &&
optionsCopy.dayPeriod === undefined &&
optionsCopy.timeZoneName === undefined
) {
optionsCopy.timeZoneName = 'short';
// The rest of the defaults will be filled in by formatting the Instant
}
let timeZone = ES.ToTemporalTimeZoneIdentifier(GetSlot(this, TIME_ZONE));
if (ES.IsTimeZoneOffsetString(timeZone)) {
// Note: https://github.com/tc39/ecma402/issues/683 will remove this
throw new RangeError('toLocaleString does not support offset string time zones');
}
timeZone = ES.GetCanonicalTimeZoneIdentifier(timeZone);
optionsCopy.timeZone = timeZone;
const formatter = new DateTimeFormat(locales, optionsCopy);
const localeCalendarIdentifier = ES.Call(customResolvedOptions, formatter, []).calendar;
const calendarIdentifier = ES.ToTemporalCalendarIdentifier(GetSlot(this, CALENDAR));
if (
calendarIdentifier !== 'iso8601' &&
localeCalendarIdentifier !== 'iso8601' &&
localeCalendarIdentifier !== calendarIdentifier
) {
throw new RangeError(
`cannot format ZonedDateTime with calendar ${calendarIdentifier}` +
` in locale with calendar ${localeCalendarIdentifier}`
);
}
return formatter.format(GetSlot(this, INSTANT));
}
toJSON(): Return['toJSON'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.TemporalZonedDateTimeToString(this, 'auto');
}
valueOf(): never {
throw new TypeError('use compare() or equals() to compare Temporal.ZonedDateTime');
}
startOfDay(): Return['startOfDay'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const dt = dateTime(this);
const DateTime = GetIntrinsic('%Temporal.PlainDateTime%');
const calendar = GetSlot(this, CALENDAR);
const dtStart = new DateTime(
GetSlot(dt, ISO_YEAR),
GetSlot(dt, ISO_MONTH),
GetSlot(dt, ISO_DAY),
0,
0,
0,
0,
0,
0,
calendar
);
const timeZone = GetSlot(this, TIME_ZONE);
const instant = ES.GetInstantFor(timeZone, dtStart, 'compatible');
return ES.CreateTemporalZonedDateTime(GetSlot(instant, EPOCHNANOSECONDS), timeZone, calendar);
}
toInstant(): Return['toInstant'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const TemporalInstant = GetIntrinsic('%Temporal.Instant%');
return new TemporalInstant(GetSlot(this, EPOCHNANOSECONDS));
}
toPlainDate(): Return['toPlainDate'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.TemporalDateTimeToDate(dateTime(this));
}
toPlainTime(): Return['toPlainTime'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.TemporalDateTimeToTime(dateTime(this));
}
toPlainDateTime(): Return['toPlainDateTime'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return dateTime(this);
}
toPlainYearMonth(): Return['toPlainYearMonth'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['monthCode', 'year'] as const);
const fields = ES.PrepareTemporalFields(this, fieldNames, []);
return ES.CalendarYearMonthFromFields(calendar, fields);
}
toPlainMonthDay(): Return['toPlainMonthDay'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const calendar = GetSlot(this, CALENDAR);
const fieldNames = ES.CalendarFields(calendar, ['day', 'monthCode'] as const);
const fields = ES.PrepareTemporalFields(this, fieldNames, []);
return ES.CalendarMonthDayFromFields(calendar, fields);
}
getISOFields(): Return['getISOFields'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
const dt = dateTime(this);
const tz = GetSlot(this, TIME_ZONE);
return {
calendar: GetSlot(this, CALENDAR),
isoDay: GetSlot(dt, ISO_DAY),
isoHour: GetSlot(dt, ISO_HOUR),
isoMicrosecond: GetSlot(dt, ISO_MICROSECOND),
isoMillisecond: GetSlot(dt, ISO_MILLISECOND),
isoMinute: GetSlot(dt, ISO_MINUTE),
isoMonth: GetSlot(dt, ISO_MONTH),
isoNanosecond: GetSlot(dt, ISO_NANOSECOND),
isoSecond: GetSlot(dt, ISO_SECOND),
isoYear: GetSlot(dt, ISO_YEAR),
offset: ES.GetOffsetStringFor(tz, GetSlot(this, INSTANT)),
timeZone: tz
};
}
getCalendar(): Return['getCalendar'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalCalendarObject(GetSlot(this, CALENDAR));
}
getTimeZone(): Return['getTimeZone'] {
if (!ES.IsTemporalZonedDateTime(this)) throw new TypeError('invalid receiver');
return ES.ToTemporalTimeZoneObject(GetSlot(this, TIME_ZONE));
}
static from(item: Params['from'][0], optionsParam: Params['from'][1] = undefined): Return['from'] {
const options = ES.GetOptionsObject(optionsParam);
if (ES.IsTemporalZonedDateTime(item)) {
ES.ToTemporalDisambiguation(options); // validate and ignore
ES.ToTemporalOffset(options, 'reject');
ES.ToTemporalOverflow(options);
return ES.CreateTemporalZonedDateTime(
GetSlot(item, EPOCHNANOSECONDS),
GetSlot(item, TIME_ZONE),
GetSlot(item, CALENDAR)
);
}
return ES.ToTemporalZonedDateTime(item, options);
}
static compare(oneParam: Params['compare'][0], twoParam: Params['compare'][1]): Return['compare'] {
const one = ES.ToTemporalZonedDateTime(oneParam);
const two = ES.ToTemporalZonedDateTime(twoParam);
const ns1 = GetSlot(one, EPOCHNANOSECONDS);
const ns2 = GetSlot(two, EPOCHNANOSECONDS);
if (JSBI.lessThan(JSBI.BigInt(ns1), JSBI.BigInt(ns2))) return -1;
if (JSBI.greaterThan(JSBI.BigInt(ns1), JSBI.BigInt(ns2))) return 1;
return 0;
}
[Symbol.toStringTag]!: 'Temporal.ZonedDateTime';
}
MakeIntrinsicClass(ZonedDateTime, 'Temporal.ZonedDateTime');
function dateTime(zdt: Temporal.ZonedDateTime) {
return ES.GetPlainDateTimeFor(GetSlot(zdt, TIME_ZONE), GetSlot(zdt, INSTANT), GetSlot(zdt, CALENDAR));
}