UNPKG

@js-temporal/polyfill

Version:

Polyfill for Tc39 Stage 3 proposal Temporal (https://github.com/tc39/proposal-temporal)

326 lines (307 loc) 13.4 kB
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] = ES.GetISO8601Calendar() ) { const isoYear = ES.ToIntegerThrowOnInfinity(isoYearParam); const isoMonth = ES.ToIntegerThrowOnInfinity(isoMonthParam); const isoDay = ES.ToIntegerThrowOnInfinity(isoDayParam); const calendar = ES.ToTemporalCalendar(calendarParam); // Note: if the arguments are not passed, // ToIntegerThrowOnInfinity(undefined) will have returned 0, which will // be rejected by RejectISODate in CreateTemporalDateSlots. This check // exists only to improve the error message. if (arguments.length < 3) { throw new RangeError('missing argument: isoYear, isoMonth and isoDay are required'); } ES.CreateTemporalDateSlots(this, isoYear, isoMonth, isoDay, calendar); } get calendar(): Return['calendar'] { if (!ES.IsTemporalDate(this)) throw new TypeError('invalid receiver'); return 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 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.RejectObjectWithCalendarOrTimeZone(temporalDateLike); const calendar = GetSlot(this, CALENDAR); const fieldNames = ES.CalendarFields(calendar, ['day', 'month', 'monthCode', 'year'] as const); const props = ES.PrepareTemporalFields(temporalDateLike, fieldNames, 'partial'); if (!props) { throw new TypeError('invalid date-like'); } let fields = ES.PrepareTemporalFields(this, fieldNames, []); fields = ES.CalendarMergeFields(calendar, fields, props); fields = ES.PrepareTemporalFields(fields, fieldNames, []); const options = ES.GetOptionsObject(optionsParam); 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.ToTemporalCalendar(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.ToShowCalendarOption(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'); let timeZone, temporalTime; if (ES.IsObject(item)) { const timeZoneLike = item.timeZone; if (timeZoneLike === undefined) { // The cast below is needed because it's possible here for // `timeZoneLike` here to be `{ plainTime: Temporal.PlainTimeLike }`, // not a TimeZoneProtocol. // TODO: should we check for that shape to improve on the (bad) error // message that the caller will get from ToTemporalTimeZone? timeZone = ES.ToTemporalTimeZone(item as Temporal.TimeZoneProtocol); } else { timeZone = ES.ToTemporalTimeZone(timeZoneLike); type TimeZoneAndPlainTimeProps = Exclude<typeof item, Temporal.TimeZoneProtocol>; temporalTime = (item as TimeZoneAndPlainTimeProps).plainTime; } } else { timeZone = ES.ToTemporalTimeZone(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); 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.BuiltinTimeZoneGetInstantFor(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) }; } 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');