/* eslint-disable func-style */ import PowerduckState from '../../app/powerduck-state'; export const toDisplayString = Symbol('toDisplayString'); function toDisplayStringImpl( this: Temporal.PlainDateTime | Temporal.PlainDate, options?: { showTime?: boolean; showSeconds?: boolean; monthLong?: boolean; dayLong?: boolean; hideDay?: boolean; hideMonth?: boolean; hideYear?: boolean; language?: string; } | boolean, showTime?: boolean, showSeconds?: boolean, _monthLong?: boolean, ): string { let opts: any = {}; if (typeof options === 'object') { opts = options ?? {}; } else { opts = { showTime: options as any, showSeconds: showTime as any, monthLong: showSeconds, }; } const fmt: Intl.DateTimeFormatOptions = { year: !opts.hideYear ? 'numeric' : undefined, month: !opts.hideMonth ? (opts.monthLong ? 'long' : 'numeric') : undefined, day: !opts.hideDay && !opts.dayLong ? 'numeric' : undefined, weekday: opts.dayLong ? 'long' : undefined, }; if (showTime || opts.showTime) { fmt.hour = 'numeric'; fmt.minute = '2-digit'; } if (showSeconds || opts.showSeconds) { fmt.second = '2-digit'; } try { const locale = opts.language || PowerduckState.getCurrentLanguage?.() || undefined; return this.toLocaleString(locale, fmt); } catch { return this.toString(); } } export const fromDate = Symbol('fromDate'); // eslint-disable-next-line unused-imports/no-unused-vars -- referenced via `typeof fromDateImpl` in the module-augmentation declaration below function fromDateImpl(this: typeof Temporal.PlainDateTime, date: Date): Temporal.PlainDateTime { return Temporal.PlainDateTime.from({ year: date.getFullYear(), month: date.getMonth() + 1, // JS Date months are 0-based day: date.getDate(), hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(), }); } export const utcEpochMilliseconds = Symbol('utcEpochMilliseconds'); function utcEpochMillisecondsImpl(this: Temporal.PlainDateTime): number { if (this == null) { return null; } let y = this.year; let m = this.month; if (m <= 2) { y -= 1; m += 12; } // Algorithm from Howard Hinnant's civil date conversion const era = Math.floor(y / 400); const yoe = y - era * 400; const doy = Math.floor((153 * (m - 3) + 2) / 5) + this.day - 1; const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy; const daysSinceEpoch = era * 146097 + doe - 719468; const msOfDay = this.hour * 3_600_000 + this.minute * 60_000 + this.second * 1_000 + (this.millisecond ?? 0); return daysSinceEpoch * 86_400_000 + msOfDay; } export const toJsDate = Symbol('toJsDate'); function toJsDateImpl(this: Temporal.PlainDateTime): Date { // eslint-disable-next-line no-restricted-syntax return new Date(this.toString()); } export const toWire = Symbol('toWire'); function toWireImp( this: Temporal.PlainDateTime, includeTime?: boolean, includeMs?: boolean, ): string { if (includeTime) { if (includeMs) { return this.toString(); } else { return this.toString().split('.')[0]; } } else { return this.toPlainDate().toString(); } } declare global { // eslint-disable-next-line ts/no-namespace namespace Temporal { interface PlainDateConstructor { [fromDate]: typeof fromDateImpl; } interface PlainDate { [toDisplayString]: typeof toDisplayStringImpl; } interface PlainDateTime { [toJsDate]: typeof toJsDateImpl; [toWire]: typeof toWireImp; [toDisplayString]: typeof toDisplayStringImpl; [utcEpochMilliseconds]: typeof utcEpochMillisecondsImpl; } } } export const applyTemporalExtensions = (): void => { if (typeof Temporal === 'undefined') { return; } (Temporal.PlainDateTime.prototype as any)[toJsDate] = toJsDateImpl; (Temporal.PlainDateTime.prototype as any)[toWire] = toWireImp; (Temporal.PlainDateTime.prototype as any)[toDisplayString] = toDisplayStringImpl; (Temporal.PlainDate.prototype as any)[toDisplayString] = toDisplayStringImpl; (Temporal.PlainDateTime.prototype as any)[utcEpochMilliseconds] = utcEpochMillisecondsImpl; }; applyTemporalExtensions();