@jauntywunderkind/tc39-temporal
Version:
TC39 Proposal: Temporal
975 lines (930 loc) • 31.2 kB
JavaScript
import GetIntrinsic from 'es-abstract/GetIntrinsic.js';
import ES2019 from 'es-abstract/es2019.js';
import { assign as ObjectAssign, unique } from './compat.mjs';
const IntlDateTimeFormat = Intl.DateTimeFormat;
const _TemporalDateTime = import('./datetime.mjs')
const _TemporalDate = import('./date.mjs')
const _TemporalYearMonth = import('./yearmonth.mjs')
const _TemporalMonthDay = import('./monthday.mjs')
const _TemporalTime = import('./time.mjs')
const _TemporalAbsolute = import('./absolute.mjs')
const _TemporalTimeZone = import('./timezone.mjs')
const _TemporalDuration = import('./duration.mjs')
let TemporalDateTime
let TemporalDate
let TemporalYearMonth
let TemporalMonthDay
let TemporalTime
let TemporalAbsolute
let TemporalTimeZone
let TemporalDuration
const INTRINSICS = {}
export const _temporalLoaded= Promise.all([
_TemporalDateTime,
_TemporalDate,
_TemporalYearMonth,
_TemporalMonthDay,
_TemporalTime,
_TemporalAbsolute,
_TemporalTimeZone,
_TemporalDuration
]).then( function( temps){
TemporalDateTime= temps[ 0]
TemporalDate= temps[ 1]
TemporalYearMonth= temps[ 2]
TemporalMonthDay= temps[ 3]
TemporalTime= temps[ 4]
TemporalAbsolute= temps[ 5]
TemporalTimeZone= temps[ 6]
TemporalDuration= temps[ 7]
INTRINSICS['%Temporal.DateTime%']= TemporalDateTime
INTRINSICS['%Temporal.Date%']= TemporalDate
INTRINSICS['%Temporal.YearMonth%']= TemporalYearMonth
INTRINSICS['%Temporal.MonthDay%']= TemporalMonthDay
INTRINSICS['%Temporal.Time%']= TemporalTime
INTRINSICS['%Temporal.TimeZone%']= TemporalTimeZone
INTRINSICS['%Temporal.Absolute%']= TemporalAbsolute
INTRINSICS['%Temporal.Duration%']= TemporalDuration
})
import bigInt from 'big-integer';
import {
HasSlot,
EPOCHNANOSECONDS,
IDENTIFIER,
YEAR,
MONTH,
DAY,
HOUR,
MINUTE,
SECOND,
MILLISECOND,
MICROSECOND,
NANOSECOND,
YEARS,
MONTHS,
DAYS,
HOURS,
MINUTES,
SECONDS,
MILLISECONDS,
MICROSECONDS,
NANOSECONDS
} from './slots.mjs';
const DAYMILLIS = 86400000;
import * as PARSE from './regex.mjs';
export const ES = ObjectAssign(ObjectAssign({}, ES2019), {
IsAbsolute: (item) => HasSlot(item, EPOCHNANOSECONDS),
IsTimeZone: (item) => HasSlot(item, IDENTIFIER),
IsDuration: (item) =>
HasSlot(item, YEARS, MONTHS, DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS),
IsDate: (item) =>
HasSlot(item, YEAR, MONTH, DAY) && !HasSlot(item, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND),
IsTime: (item) =>
HasSlot(item, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND) && !HasSlot(item, YEAR, MONTH, DAY),
IsDateTime: (item) => HasSlot(item, YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, MILLISECOND, MICROSECOND, NANOSECOND),
IsYearMonth: (item) => HasSlot(item, YEAR, MONTH) && !HasSlot(item, DAY),
IsMonthDay: (item) => HasSlot(item, MONTH, DAY) && !HasSlot(item, YEAR),
ToTimeZone: (item) => {
if (ES.IsTimeZone(item)) return item;
const isoString = ES.ToString(item);
const match = PARSE.timezone.exec(isoString);
if (!match) return new TemporalTimeZone(isoString);
if (!match) throw new RangeError(`invalid absolute: ${isoString}`);
const zone = match[1] ? 'UTC' : match[3] || match[2];
return new TemporalTimeZone(zone);
},
ToAbsolute: (item) => {
if (ES.IsAbsolute(item)) return item;
if ('bigint' === typeof item || bigInt.isInstance(item)) return new TemporalAbsolute(item);
const isoString = ES.ToString(item);
const match = PARSE.absolute.exec(isoString);
if (!match) throw new RangeError(`invalid absolute: ${isoString}`);
const year = ES.ToInteger(match[1]);
const month = ES.ToInteger(match[2]);
const day = ES.ToInteger(match[3]);
const hour = ES.ToInteger(match[4]);
const minute = ES.ToInteger(match[5]);
const second = ES.ToInteger(match[6]);
const millisecond = ES.ToInteger(match[7]);
const microsecond = ES.ToInteger(match[8]);
const nanosecond = ES.ToInteger(match[9]);
const zone = match[11] || match[10] || 'UTC';
const datetime = ES.ToDateTime({
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
});
return datetime.inTimeZone(zone, match[11] ? match[10] : 'earlier');
},
ToDateTime: (item) => {
if (ES.IsDateTime(item)) return item;
const props = ES.ValidPropertyBag(item, [
'year',
'month',
'day',
'hour',
'minute',
'second',
'millisecond',
'microsecond',
'nanosecond'
]);
if (props) {
const {
year = 0,
month = 1,
day = 1,
hour = 0,
minute = 0,
second = 0,
millisecond = 0,
microsecond = 0,
nanosecond = 0
} = props;
return new TemporalDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, 'reject');
}
const isoString = ES.ToString(item);
const match = PARSE.datetime.exec(isoString);
if (!match) throw new RangeError(`invalid datetime: ${isoString}`);
const year = ES.ToInteger(match[1]);
const month = ES.ToInteger(match[2]);
const day = ES.ToInteger(match[3]);
const hour = ES.ToInteger(match[4]);
const minute = ES.ToInteger(match[5]);
const second = ES.ToInteger(match[6]);
const millisecond = ES.ToInteger(match[7]);
const microsecond = ES.ToInteger(match[8]);
const nanosecond = ES.ToInteger(match[9]);
return new TemporalDateTime(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond, 'reject');
},
ToDate: (item) => {
if (ES.IsDate(item)) return item;
const props = ES.ValidPropertyBag(item, [
'year',
'month',
'day'
]);
if (props) {
const {
year = 0,
month = 1,
day = 1
} = props;
return new TemporalDate(year, month, day, 'reject');
}
const isoString = ES.ToString(item);
const match = PARSE.date.exec(isoString);
if (!match) throw new RangeError(`invalid date: ${isoString}`);
const year = ES.ToInteger(match[1]);
const month = ES.ToInteger(match[2]);
const day = ES.ToInteger(match[3]);
return new TemporalDate(year, month, day, 'reject');
},
ToTime: (item) => {
if (ES.IsTime(item)) return item;
const props = ES.ValidPropertyBag(item, [
'hour',
'minute',
'second',
'millisecond',
'microsecond',
'nanosecond'
]);
if (props) {
const {
hour = 0,
minute = 0,
second = 0,
millisecond = 0,
microsecond = 0,
nanosecond = 0
} = props;
return new TemporalTime(hour, minute, second, millisecond, microsecond, nanosecond, 'reject');
}
const isoString = ES.ToString(item);
const match = PARSE.time.exec(isoString);
if (!match) throw new RangeError(`invalid date: ${isoString}`);
const hour = ES.ToInteger(match[1]);
const minute = ES.ToInteger(match[2]);
const second = ES.ToInteger(match[3]);
const millisecond = ES.ToInteger(match[4]);
const microsecond = ES.ToInteger(match[5]);
const nanosecond = ES.ToInteger(match[6]);
return new TemporalTime(hour, minute, second, millisecond, microsecond, nanosecond, 'reject');
},
ToYearMonth: (item) => {
if (ES.IsYearMonth(item)) return item;
const props = ES.ValidPropertyBag(item, [
'year',
'month'
]);
if (props) {
const {
year = 0,
month = 1
} = props;
return new TemporalYearMonth(year, month, 'reject');
}
const isoString = ES.ToString(item);
const match = PARSE.yearmonth.exec(isoString);
if (!match) throw new RangeError(`invalid yearmonth: ${isoString}`);
const year = ES.ToInteger(match[1]);
const month = ES.ToInteger(match[2]);
return new TemporalYearMonth(year, month, 'reject');
},
ToMonthDay: (item) => {
if (ES.IsMonthDay(item)) return item;
const props = ES.ValidPropertyBag(item, [
'month',
'day'
]);
if (props) {
const {
month = 1,
day = 1
} = props;
return new TemporalMonthDay(month, day, 'reject');
}
const isoString = ES.ToString(item);
const match = PARSE.monthday.exec(isoString);
if (!match) throw new RangeError(`invalid yearmonth: ${isoString}`);
const month = ES.ToInteger(match[1] || match[3]);
const day = ES.ToInteger(match[2] || match[4]);
return new TemporalMonthDay(month, day, 'reject');
},
ToDuration: (item) => {
if (ES.IsDuration(item)) return item;
const props = ES.ValidPropertyBag(item, [
'years',
'months',
'days',
'hours',
'minutes',
'seconds',
'milliseconds',
'microseconds',
'nanoseconds'
]);
if (props) {
const {
years = 0,
months = 0,
days = 0,
hours = 0,
minutes = 0,
seconds = 0,
milliseconds = 0,
microseconds = 0,
nanoseconds = 0
} = props;
return new TemporalDuration(years, months, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, 'reject');
}
const isoString = ES.ToString(item);
const match = PARSE.duration.exec(isoString);
if (!match) throw new RangeError(`invalid duration: ${isoString}`);
const years = ES.ToInteger(match[1]);
const months = ES.ToInteger(match[2]);
const days = ES.ToInteger(match[3]);
const hours = ES.ToInteger(match[4]);
const minutes = ES.ToInteger(match[5]);
const seconds = ES.ToInteger(match[6]);
const milliseconds = ES.ToInteger(match[7]);
const microseconds = ES.ToInteger(match[8]);
const nanoseconds = ES.ToInteger(match[9]);
return new TemporalDuration(
years,
months,
days,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds,
'reject'
);
},
GetIntrinsic: (intrinsic) => {
return intrinsic in INTRINSICS ? INTRINSICS[intrinsic] : GetIntrinsic(intrinsic);
},
ValidPropertyBag: (bag, anyof = []) => {
if (!bag || 'object' !== typeof bag) return false;
let any;
for (let prop of anyof) {
if (prop in bag) {
const value = ES.ToNumber(bag[prop]);
if (Number.isFinite(value)) {
any = any || {};
any[prop] = value;
}
}
}
return any ? any : false;
},
ValidDuration: (durationbag, invalid = []) => {
for (let prop of invalid) {
if (prop in durationbag) {
const value = ES.ToNumber(durationbag[prop]);
if (Number.isFinite(value) && !!value) {
return false;
}
}
}
return true;
},
ToTimeZone: (tz) => {
const TimeZone = ES.GetIntrinsic('%Temporal.TimeZone%');
return tz instanceof TimeZone ? tz : new TimeZone(`${tz}`);
},
ISOTimeZoneString: (timeZone, absolute) => {
let offset = timeZone.getOffsetFor(absolute);
let timeZoneString;
switch (true) {
case 'UTC' === timeZone.name:
timeZoneString = 'Z';
break;
case timeZone.name === offset:
timeZoneString = offset;
break;
default:
timeZoneString = `${offset}[${timeZone.name}]`;
break;
}
return timeZoneString;
},
ISOYearString: (year) => {
let yearString;
if (year < 1000 || year > 9999) {
let sign = year < 0 ? '-' : '+';
let yearNumber = Math.abs(year);
yearString = sign + `000000${yearNumber}`.slice(-6);
} else {
yearString = `${year}`;
}
return yearString;
},
ISODateTimePartString: (part) => `00${part}`.slice(-2),
ISOSecondsString: (seconds, millis, micros, nanos) => {
if (!seconds && !millis && !micros && !nanos) return '';
let parts = [];
if (nanos) parts.unshift(`000${nanos || 0}`.slice(-3));
if (micros || parts.length) parts.unshift(`000${micros || 0}`.slice(-3));
if (millis || parts.length) parts.unshift(`000${millis || 0}`.slice(-3));
let secs = `00${seconds}`.slice(-2);
let post = parts.length ? `.${parts.join('')}` : '';
return `${secs}${post}`;
},
GetCanonicalTimeZoneIdentifier: (timeZoneIdentifier) => {
const offset = parseOffsetString(timeZoneIdentifier);
if (offset !== null) return makeOffsetString(offset);
const formatter = new IntlDateTimeFormat('en-us', {
timeZone: String(timeZoneIdentifier),
hour12: false,
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
});
formatter.toString = tzIdent;
return formatter;
},
GetTimeZoneOffsetNanoseconds: (epochNanoseconds, timeZone) => {
const offset = parseOffsetString(`${timeZone}`);
if (offset !== null) return bigInt(offset).multiply(1e6);
const { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond } = ES.GetTimeZoneDateTimeParts(
epochNanoseconds,
timeZone
);
const utc = ES.GetEpochFromParts(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
return utc.minus(epochNanoseconds);
},
GetTimeZoneOffsetString: (epochNanoseconds, timeZone) => {
const offsetNanos = bigInt(ES.GetTimeZoneOffsetNanoseconds(epochNanoseconds, timeZone));
const offsetString = makeOffsetString(offsetNanos.divide(1e6));
return offsetString;
},
GetEpochFromParts: (year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) => {
const ms = Date.UTC(year, month - 1, day, hour, minute, second, millisecond);
let ns = bigInt(ms).multiply(1e6);
ns = ns.plus(bigInt(microsecond).multiply(1e3));
ns = ns.plus(bigInt(nanosecond));
return ns;
},
GetTimeZoneDateTimeParts: (epochNanoseconds, timeZone) => {
const offset = parseOffsetString(timeZone);
let nanos = bigInt(epochNanoseconds).mod(1e9);
let epochMilliseconds = bigInt(epochNanoseconds).divide(1e9).multiply(1e3).plus(Math.floor(nanos / 1e6));
nanos = +((epochNanoseconds < 0 ? 1e9 : 0) + nanos);
let millisecond = Math.floor(nanos / 1e6) % 1e3;
let microsecond = Math.floor(nanos / 1e3) % 1e3;
let nanosecond = Math.floor(nanos / 1e0) % 1e3;
if (offset !== null) {
let zonedEpochMilliseconds = epochMilliseconds + offset;
let item = new Date(zonedEpochMilliseconds);
let year = item.getUTCFullYear();
let month = item.getUTCMonth();
let day = item.getUTCDate();
let hour = item.getUTCHours();
let minute = item.getUTCMinutes();
let second = item.getUTCSeconds();
let days = 0;
({ days, hour, minute, second, millisecond, microsecond, nanosecond } = ES.BalanceTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
));
day += days;
({ year, month, day } = ES.BalanceDate(year, month, day));
return {
year,
month,
day,
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
};
}
let year, month, day, hour, minute, second;
({ year, month, day, hour, minute, second } = ES.GetFormatterParts(timeZone, epochMilliseconds).reduce(
reduceParts,
{}
));
let deltaDays = 0;
({ deltaDays, hour, minute, second, millisecond, microsecond, nanosecond } = ES.BalanceTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
));
day += deltaDays;
({ year, month, day } = ES.BalanceDate(year, month, day));
return { year, month, day, hour, minute, second, millisecond, microsecond, nanosecond };
},
GetTimeZoneNextTransition: (epochNanoseconds, timeZone) => {
const offset = parseOffsetString(timeZone);
if (offset !== null) {
return null;
}
let leftNanos = epochNanoseconds;
let leftOffset = ES.GetTimeZoneOffsetString(leftNanos, timeZone);
let rightNanos = leftNanos;
let rightOffset = leftOffset;
let weeks = 0;
while ((leftOffset === rightOffset) && (weeks < 104)) {
rightNanos = bigInt(leftNanos).plus(7 * 24 * DAYMILLIS * 1e6);
rightOffset = ES.GetTimeZoneOffsetString(rightNanos, timeZone);
if (leftOffset === rightOffset) {
leftNanos = rightNanos;
}
weeks++;
}
if (leftOffset === rightOffset) return null;
const result = bisect(
(epochNS) => ES.GetTimeZoneOffsetString(epochNS, timeZone),
leftNanos,
rightNanos,
leftOffset,
rightOffset
);
return result;
},
GetFormatterParts: (fmt, v) => {
const datetime = fmt.format(v);
const [date, time] = datetime.split(/,\s+/);
const [month, day, year] = date.split('/');
const [hour, minute, second] = time.split(':');
return [
{ type: 'year', value: +year },
{ type: 'month', value: +month },
{ type: 'day', value: +day },
{ type: 'hour', value: +hour },
{ type: 'minute', value: +minute },
{ type: 'second', value: +second }
];
},
GetTimeZoneEpochValue: (timeZone, year, month, day, hour, minute, second, millisecond, microsecond, nanosecond) => {
const offset = parseOffsetString(timeZone);
let ns = ES.GetEpochFromParts(year, month, day, hour, minute, second, millisecond, microsecond, nanosecond);
if (offset !== null) {
ns = ns.plus(bigInt(offset).multiply(1e6));
return [ns];
}
const earliest = ES.GetTimeZoneOffsetNanoseconds(bigInt(ns).minus(bigInt(DAYMILLIS).multiply(1e6)), timeZone);
const latest = ES.GetTimeZoneOffsetNanoseconds(bigInt(ns).plus(bigInt(DAYMILLIS).multiply(1e6)), timeZone);
const found = unique([earliest, latest])
.map((offsetNanoseconds) => {
const epochNanoseconds = bigInt(ns).minus(offsetNanoseconds);
const parts = ES.GetTimeZoneDateTimeParts(epochNanoseconds, timeZone);
if (
year !== parts.year ||
month !== parts.month ||
day !== parts.day ||
hour !== parts.hour ||
minute !== parts.minute ||
second !== parts.second ||
millisecond !== parts.millisecond ||
microsecond !== parts.microsecond ||
nanosecond !== parts.nanosecond
) {
return undefined;
}
return epochNanoseconds;
})
.filter((x) => x !== undefined);
return found;
},
LeapYear: (year) => {
if (undefined === year) return false;
const isDiv4 = year % 4 === 0;
const isDiv100 = year % 100 === 0;
const isDiv400 = year % 400 === 0;
return isDiv4 && (!isDiv100 || isDiv400);
},
DaysInMonth: (year, month) => {
const DoM = {
standard: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
leapyear: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
};
return DoM[ES.LeapYear(year) ? 'leapyear' : 'standard'][month - 1];
},
DayOfWeek: (year, month, day) => {
const m = month + (month < 3 ? 10 : -2);
const Y = year - (month < 3 ? 1 : 0);
const c = Math.floor(Y / 100);
const y = Y - c * 100;
const d = day;
const pD = d;
const pM = Math.floor(2.6 * m - 0.2);
const pY = y + Math.floor(y / 4);
const pC = Math.floor(c / 4) - 2 * c;
const dow = (pD + pM + pY + pC) % 7;
return dow + (dow <= 0 ? 7 : 0);
},
DayOfYear: (year, month, day) => {
let days = day;
for (let m = month - 1; m > 0; m--) {
days += ES.DaysInMonth(year, m);
}
return days;
},
WeekOfYear: (year, month, day) => {
let doy = ES.DayOfYear(year, month, day);
let dow = ES.DayOfWeek(year, month, day) || 7;
let doj = ES.DayOfWeek(year, 1, 1);
const week = Math.floor((doy - dow + 10) / 7);
if (week < 1) {
if (doj === (ES.LeapYear(year) ? 5 : 6)) {
return 53;
} else {
return 52;
}
}
if (week === 53) {
if ((ES.LeapYear(year) ? 366 : 365) - doy < 4 - dow) {
return 1;
}
}
return week;
},
BalanceYearMonth: (year, month) => {
month -= 1;
year += Math.floor(month / 12);
if (month < 0) {
month = 12 + (month % 12);
} else if (month > 11) {
month = month % 12;
}
month += 1;
return { year, month };
},
BalanceDate: (year, month, day) => {
({ year, month } = ES.BalanceYearMonth(year, month));
let daysInYear = 0;
while (((daysInYear = ES.LeapYear(month > 2 ? year : year - 1) ? -366 : -365), day < daysInYear)) {
year -= 1;
day -= daysInYear;
}
while (((daysInYear = ES.LeapYear(month > 2 ? year + 1 : year) ? 366 : 365), day > daysInYear)) {
year += 1;
day -= daysInYear;
}
while (day < 1) {
month -= 1;
if (month === 0) {
month = 12;
year -= 1;
}
day = ES.DaysInMonth(year, month) + day;
}
while (day > ES.DaysInMonth(year, month)) {
day = day - ES.DaysInMonth(year, month);
month += 1;
if (month > 12) {
month = 1;
year += 1;
}
}
return { year, month, day };
},
BalanceTime: (hour, minute, second, millisecond, microsecond, nanosecond) => {
microsecond += Math.floor(nanosecond / 1000);
nanosecond = nanosecond % 1000;
nanosecond = nanosecond < 0 ? 1000 + nanosecond : nanosecond;
millisecond += Math.floor(microsecond / 1000);
microsecond = microsecond % 1000;
microsecond = microsecond < 0 ? 1000 + microsecond : microsecond;
second += Math.floor(millisecond / 1000);
millisecond = millisecond % 1000;
millisecond = millisecond < 0 ? 1000 + millisecond : millisecond;
minute += Math.floor(second / 60);
second = second % 60;
second = second < 0 ? 60 + second : second;
hour += Math.floor(minute / 60);
minute = minute % 60;
minute = minute < 0 ? 60 + minute : minute;
let deltaDays = Math.floor(hour / 24);
hour = hour % 24;
hour = hour < 0 ? 24 + hour : hour;
return { deltaDays, hour, minute, second, millisecond, microsecond, nanosecond };
},
ConstrainToRange: (value, min, max) => Math.min(max, Math.max(min, value)),
ConstrainDate: (year, month, day) => {
year = ES.ConstrainToRange(year, -999999, 999999);
month = ES.ConstrainToRange(month, 1, 12);
day = ES.ConstrainToRange(day, 1, ES.DaysInMonth(year, month));
return { year, month, day };
},
ConstrainTime: (hour, minute, second, millisecond, microsecond, nanosecond) => {
hour = ES.ConstrainToRange(hour, 0, 23);
minute = ES.ConstrainToRange(minute, 0, 59);
second = ES.ConstrainToRange(second, 0, 59);
millisecond = ES.ConstrainToRange(millisecond, 0, 999);
microsecond = ES.ConstrainToRange(microsecond, 0, 999);
nanosecond = ES.ConstrainToRange(nanosecond, 0, 999);
return { hour, minute, second, millisecond, microsecond, nanosecond };
},
RejectToRange: (value, min, max) => {
if (value < min || value > max) throw new RangeError(`value out of range: ${min} <= ${value} <= ${max}`);
return value;
},
RejectDate: (year, month, day) => {
year = ES.RejectToRange(year, -999999, 999999);
month = ES.RejectToRange(month, 1, 12);
day = ES.RejectToRange(day, 1, ES.DaysInMonth(year, month));
return { year, month, day };
},
RejectTime: (hour, minute, second, millisecond, microsecond, nanosecond) => {
hour = ES.RejectToRange(hour, 0, 23);
minute = ES.RejectToRange(minute, 0, 59);
second = ES.RejectToRange(second, 0, 60);
millisecond = ES.RejectToRange(millisecond, 0, 999);
microsecond = ES.RejectToRange(microsecond, 0, 999);
nanosecond = ES.RejectToRange(nanosecond, 0, 999);
return { hour, minute, second, millisecond, microsecond, nanosecond };
},
DifferenceDate: (smaller, larger) => {
let years = larger.year - smaller.year;
let months = larger.month - smaller.month;
if (months < 0) {
years -= 1;
months += 12;
}
let { year, month } = ES.BalanceYearMonth(smaller.year + years, smaller.month + months);
while (smaller.day > ES.DaysInMonth(year, month)) {
months -= 1;
if (months < 0) {
years -= 1;
months += 12;
}
({ year, month } = ES.BalanceYearMonth(smaller.year + years, smaller.month + months));
}
let days = ES.DayOfYear(larger.year, larger.month, larger.day) - ES.DayOfYear(year, month, smaller.day);
if (days < 0) {
months -= 1;
if (months < 0) {
years -= 1;
months += 12;
}
({ year, month } = ES.BalanceYearMonth(smaller.year + years, smaller.month + months));
while (smaller.day > ES.DaysInMonth(year, month)) {
months -= 1;
if (months < 0) {
years -= 1;
months += 12;
}
({ year, month } = ES.BalanceYearMonth(smaller.year + years, smaller.month + months));
}
if (larger.year > year) {
const din = ES.LeapYear(year) ? 366 : 365;
days = ES.DayOfYear(larger.year, larger.month, larger.day) + (din - ES.DayOfYear(year, month, smaller.day));
} else {
days = ES.DayOfYear(larger.year, larger.month, larger.day) - ES.DayOfYear(year, month, smaller.day);
}
}
return { years, months, days };
},
DifferenceTime(earlier, later) {
let hours = later.hour - earlier.hour;
let minutes = later.minute - earlier.minute;
let seconds = later.second - earlier.second;
let milliseconds = later.millisecond - earlier.millisecond;
let microseconds = later.microsecond - earlier.microsecond;
let nanoseconds = later.nanosecond - earlier.nanosecond;
let deltaDays = 0;
({
deltaDays,
hour: hours,
minute: minutes,
second: seconds,
millisecond: milliseconds,
microsecond: microseconds,
nanosecond: nanoseconds
} = ES.BalanceTime(hours, minutes, seconds, milliseconds, microseconds, nanoseconds));
return { deltaDays, hours, minutes, seconds, milliseconds, microseconds, nanoseconds };
},
AddDate: (year, month, day, years, months, days, disambiguation = 'constrain') => {
year += years;
month += months;
({ year, month } = ES.BalanceYearMonth(year, month));
switch (disambiguation) {
case 'reject':
({ year, month, day } = ES.RejectDate(year, month, day));
break;
case 'constrain':
({ year, month, day } = ES.ConstrainDate(year, month, day));
break;
case 'balance':
({ year, month, day } = ES.BalanceDate(year, month, day));
break;
default:
throw new TypeError('disambiguation should be either reject, constrain or balance');
}
day += days;
({ year, month, day } = ES.BalanceDate(year, month, day));
return { year, month, day };
},
AddTime: (
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds
) => {
hour += hours;
minute += minutes;
second += seconds;
millisecond += milliseconds;
microsecond += microseconds;
nanosecond += nanoseconds;
let deltaDays = 0;
({ deltaDays, hour, minute, second, millisecond, microsecond, nanosecond } = ES.BalanceTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
));
return { deltaDays, hour, minute, second, millisecond, microsecond, nanosecond };
},
SubtractDate: (year, month, day, years, months, days, disambiguation = 'constrain') => {
day -= days;
({ year, month, day } = ES.BalanceDate(year, month, day));
month -= months;
year -= years;
({ year, month } = ES.BalanceYearMonth(year, month));
switch (disambiguation) {
case 'reject':
({ year, month, day } = ES.RejectDate(year, month, day));
break;
case 'constrain':
({ year, month, day } = ES.ConstrainDate(year, month, day));
break;
case 'balance':
({ year, month, day } = ES.BalanceDate(year, month, day));
break;
default:
throw new TypeError('disambiguation should be either reject, constrain or balance');
}
return { year, month, day };
},
SubtractTime: (
hour,
minute,
second,
millisecond,
microsecond,
nanosecond,
hours,
minutes,
seconds,
milliseconds,
microseconds,
nanoseconds
) => {
hour -= hours;
minute -= minutes;
second -= seconds;
millisecond -= milliseconds;
microsecond -= microseconds;
nanosecond -= nanoseconds;
let deltaDays = 0;
({ deltaDays, hour, minute, second, millisecond, microsecond, nanosecond } = ES.BalanceTime(
hour,
minute,
second,
millisecond,
microsecond,
nanosecond
));
return { deltaDays, hour, minute, second, millisecond, microsecond, nanosecond };
},
AssertPositiveInteger: (num) => {
if (!Number.isFinite(num) || Math.abs(num) !== num) throw new RangeError(`invalid positive integer: ${num}`);
return num;
},
SystemUTCEpochNanoSeconds: (() => {
let ns = Date.now() % 1e6;
return () => {
const ms = Date.now();
const result = bigInt(ms).multiply(1e6).plus(ns);
ns = ms % 1e6;
return result;
};
})(),
SystemTimeZone: () => {
const fmt = new IntlDateTimeFormat('en-us');
return ES.ToTimeZone(fmt.resolvedOptions().timeZone);
},
ComparisonResult: (value) => (value < 0 ? -1 : value > 0 ? 1 : value)
});
import * as REGEX from './regex.mjs';
const OFFSET = new RegExp(`^${REGEX.offset.source}$`);
function parseOffsetString(string) {
const match = OFFSET.exec(String(string));
if (!match) return null;
const sign = match[1] === '-' ? -1 : +1;
const hours = +match[2];
const minutes = +match[3];
return sign * (hours * 60 + minutes) * 60 * 1000;
}
function makeOffsetString(offsetMilliSeconds) {
let offsetSeconds = Math.round(offsetMilliSeconds / 1000);
const sign = (offsetSeconds < 0) ? '-' : '+';
offsetSeconds = Math.abs(offsetSeconds);
const offsetMinutes = Math.floor(offsetSeconds / 60) % 60;
const offsetHours = Math.floor(offsetSeconds / 3600);
const offsetMinuteString = `00${offsetMinutes}`.slice(-2);
const offsetHourString = `00${offsetHours}`.slice(-2);
return `${sign}${offsetHourString}:${offsetMinuteString}`;
}
function reduceParts(res, item) {
if (item.type === 'literal') return res;
if (item.type === 'timeZoneName') return res;
res[item.type] = parseInt(item.value, 10);
return res;
}
function bisect(getState, left, right, lstate = getState(left), rstate = getState(right)) {
left = bigInt(left);
right = bigInt(right);
while (right.minus(left).greater(1)) {
let middle = left.plus(right).divide(2);
const mstate = getState(middle);
if (mstate === lstate) {
left = middle;
lstate = mstate;
} else if(mstate === rstate) {
right = middle;
rstate = mstate;
} else {
throw new Error(`invalid state in bisection ${lstate} - ${mstate} - ${rstate}`);
}
}
return right;
}
function tzIdent() {
return this.resolvedOptions().timeZone;
}