| import { IllegalArgumentError } from "../Cause.ts" | |
| import * as Clock from "../Clock.ts" | |
| import type * as DateTime from "../DateTime.ts" | |
| import * as Duration from "../Duration.ts" | |
| import type * as Effect from "../Effect.ts" | |
| import * as Equal from "../Equal.ts" | |
| import * as Equ from "../Equivalence.ts" | |
| import type { LazyArg } from "../Function.ts" | |
| import { dual } from "../Function.ts" | |
| import * as Hash from "../Hash.ts" | |
| import * as Inspectable from "../Inspectable.ts" | |
| import * as Option from "../Option.ts" | |
| import * as order from "../Order.ts" | |
| import { pipeArguments } from "../Pipeable.ts" | |
| import * as Predicate from "../Predicate.ts" | |
| import type { Mutable } from "../Types.ts" | |
| import * as effect from "./effect.ts" | |
| /** @internal */ | |
| export const TypeId = "~effect/time/DateTime" | |
| /** @internal */ | |
| export const TimeZoneTypeId = "~effect/time/DateTime/TimeZone" | |
| const Proto = { | |
| [TypeId]: TypeId, | |
| pipe() { | |
| return pipeArguments(this, arguments) | |
| }, | |
| [Inspectable.NodeInspectSymbol](this: DateTime.DateTime) { | |
| return this.toString() | |
| }, | |
| toJSON(this: DateTime.DateTime) { | |
| return toDateUtc(this).toJSON() | |
| } | |
| } | |
| const ProtoUtc = { | |
| ...Proto, | |
| _tag: "Utc", | |
| [Hash.symbol](this: DateTime.Utc) { | |
| return Hash.number(this.epochMilliseconds) | |
| }, | |
| [Equal.symbol](this: DateTime.Utc, that: unknown) { | |
| return isDateTime(that) && that._tag === "Utc" && this.epochMilliseconds === that.epochMilliseconds | |
| }, | |
| toString(this: DateTime.Utc) { | |
| return `DateTime.Utc(${toDateUtc(this).toJSON()})` | |
| } | |
| } | |
| const ProtoZoned = { | |
| ...Proto, | |
| _tag: "Zoned", | |
| [Hash.symbol](this: DateTime.Zoned) { | |
| return Hash.combine(Hash.number(this.epochMilliseconds))(Hash.hash(this.zone)) | |
| }, | |
| [Equal.symbol](this: DateTime.Zoned, that: unknown) { | |
| return isDateTime(that) && that._tag === "Zoned" && this.epochMilliseconds === that.epochMilliseconds && | |
| Equal.equals(this.zone, that.zone) | |
| }, | |
| toString(this: DateTime.Zoned) { | |
| return `DateTime.Zoned(${formatIsoZoned(this)})` | |
| } | |
| } | |
| const ProtoTimeZone = { | |
| [TimeZoneTypeId]: TimeZoneTypeId, | |
| [Inspectable.NodeInspectSymbol](this: DateTime.TimeZone) { | |
| return this.toString() | |
| } | |
| } | |
| const ProtoTimeZoneNamed = { | |
| ...ProtoTimeZone, | |
| _tag: "Named", | |
| [Hash.symbol](this: DateTime.TimeZone.Named) { | |
| return Hash.string(`Named:${this.id}`) | |
| }, | |
| [Equal.symbol](this: DateTime.TimeZone.Named, that: unknown) { | |
| return isTimeZone(that) && that._tag === "Named" && this.id === that.id | |
| }, | |
| toString(this: DateTime.TimeZone.Named) { | |
| return `TimeZone.Named(${this.id})` | |
| }, | |
| toJSON(this: DateTime.TimeZone.Named) { | |
| return { | |
| _id: "TimeZone", | |
| _tag: "Named", | |
| id: this.id | |
| } | |
| } | |
| } | |
| const ProtoTimeZoneOffset = { | |
| ...ProtoTimeZone, | |
| _tag: "Offset", | |
| [Hash.symbol](this: DateTime.TimeZone.Offset) { | |
| return Hash.string(`Offset:${this.offset}`) | |
| }, | |
| [Equal.symbol](this: DateTime.TimeZone.Offset, that: unknown) { | |
| return isTimeZone(that) && that._tag === "Offset" && this.offset === that.offset | |
| }, | |
| toString(this: DateTime.TimeZone.Offset) { | |
| return `TimeZone.Offset(${offsetToString(this.offset)})` | |
| }, | |
| toJSON(this: DateTime.TimeZone.Offset) { | |
| return { | |
| _id: "TimeZone", | |
| _tag: "Offset", | |
| offset: this.offset | |
| } | |
| } | |
| } | |
| /** @internal */ | |
| export const makeZonedProto = ( | |
| epochMillis: number, | |
| zone: DateTime.TimeZone, | |
| partsUtc?: DateTime.DateTime.PartsWithWeekday | |
| ): DateTime.Zoned => { | |
| const self = Object.create(ProtoZoned) | |
| self.epochMilliseconds = epochMillis | |
| self.zone = zone | |
| Object.defineProperty(self, "partsUtc", { | |
| value: partsUtc, | |
| enumerable: false, | |
| writable: true | |
| }) | |
| Object.defineProperty(self, "adjustedEpochMillis", { | |
| value: undefined, | |
| enumerable: false, | |
| writable: true | |
| }) | |
| Object.defineProperty(self, "partsAdjusted", { | |
| value: undefined, | |
| enumerable: false, | |
| writable: true | |
| }) | |
| return self | |
| } | |
| // ============================================================================= | |
| // guards | |
| // ============================================================================= | |
| /** @internal */ | |
| export const isDateTime = (u: unknown): u is DateTime.DateTime => Predicate.hasProperty(u, TypeId) | |
| const isDateTimeArgs = (args: IArguments) => isDateTime(args[0]) | |
| /** @internal */ | |
| export const isTimeZone = (u: unknown): u is DateTime.TimeZone => Predicate.hasProperty(u, TimeZoneTypeId) | |
| /** @internal */ | |
| export const isTimeZoneOffset = (u: unknown): u is DateTime.TimeZone.Offset => isTimeZone(u) && u._tag === "Offset" | |
| /** @internal */ | |
| export const isTimeZoneNamed = (u: unknown): u is DateTime.TimeZone.Named => isTimeZone(u) && u._tag === "Named" | |
| /** @internal */ | |
| export const isUtc = (self: DateTime.DateTime): self is DateTime.Utc => self._tag === "Utc" | |
| /** @internal */ | |
| export const isZoned = (self: DateTime.DateTime): self is DateTime.Zoned => self._tag === "Zoned" | |
| // ============================================================================= | |
| // instances | |
| // ============================================================================= | |
| /** @internal */ | |
| export const Equivalence: Equ.Equivalence<DateTime.DateTime> = Equ.make((a, b) => | |
| a.epochMilliseconds === b.epochMilliseconds | |
| ) | |
| /** @internal */ | |
| export const Order: order.Order<DateTime.DateTime> = order.make((self, that) => | |
| self.epochMilliseconds < that.epochMilliseconds ? -1 : self.epochMilliseconds > that.epochMilliseconds ? 1 : 0 | |
| ) | |
| /** @internal */ | |
| export const clamp: { | |
| <Min extends DateTime.DateTime, Max extends DateTime.DateTime>( | |
| options: { readonly minimum: Min; readonly maximum: Max } | |
| ): <A extends DateTime.DateTime>(self: A) => A | Min | Max | |
| <A extends DateTime.DateTime, Min extends DateTime.DateTime, Max extends DateTime.DateTime>( | |
| self: A, | |
| options: { readonly minimum: Min; readonly maximum: Max } | |
| ): A | Min | Max | |
| } = order.clamp(Order) | |
| // ============================================================================= | |
| // constructors | |
| // ============================================================================= | |
| const makeUtc = (epochMillis: number): DateTime.Utc => { | |
| const self = Object.create(ProtoUtc) | |
| self.epochMilliseconds = epochMillis | |
| Object.defineProperty(self, "partsUtc", { | |
| value: undefined, | |
| enumerable: false, | |
| writable: true | |
| }) | |
| return self | |
| } | |
| /** @internal */ | |
| export const fromDateUnsafe = (date: Date): DateTime.Utc => { | |
| const epochMillis = date.getTime() | |
| if (Number.isNaN(epochMillis)) { | |
| throw new IllegalArgumentError("Invalid date") | |
| } | |
| return makeUtc(epochMillis) | |
| } | |
| /** @internal */ | |
| export const makeUnsafe = <A extends DateTime.DateTime.Input>(input: A): DateTime.DateTime.PreserveZone<A> => { | |
| if (isDateTime(input)) { | |
| return input as DateTime.DateTime.PreserveZone<A> | |
| } else if (input instanceof Date) { | |
| return fromDateUnsafe(input) as DateTime.DateTime.PreserveZone<A> | |
| } else if (typeof input === "object") { | |
| if ("epochMilliseconds" in input) { | |
| return makeUtc(input.epochMilliseconds) as DateTime.DateTime.PreserveZone<A> | |
| } | |
| const date = new Date(0) | |
| setPartsDate(date, input) | |
| return fromDateUnsafe(date) as DateTime.DateTime.PreserveZone<A> | |
| } else if (typeof input === "string" && !hasZone(input)) { | |
| return fromDateUnsafe(new Date(input + "Z")) as DateTime.DateTime.PreserveZone<A> | |
| } | |
| return fromDateUnsafe(new Date(input)) as DateTime.DateTime.PreserveZone<A> | |
| } | |
| /** | |
| * Detects whether a date string already contains timezone info. | |
| * Without a zone, `new Date("2024-01-01T12:00:00")` is parsed as local time, | |
| * so `makeUnsafe` appends "Z" to force UTC interpretation. | |
| * This check prevents appending "Z" to strings that already have a zone | |
| * (e.g. "2024-01-01T12:00:00Z", "...+05:30", "...GMT"), which would produce invalid dates. | |
| */ | |
| const hasZone = (input: string): boolean => /Z|GMT|[+-]\d{2}$|[+-]\d{2}:?\d{2}$|\]$/.test(input) | |
| const minEpochMillis = -8640000000000000 + (12 * 60 * 60 * 1000) | |
| const maxEpochMillis = 8640000000000000 - (14 * 60 * 60 * 1000) | |
| /** @internal */ | |
| export const makeZonedUnsafe = (input: DateTime.DateTime.Input, options?: { | |
| readonly timeZone?: number | string | DateTime.TimeZone | undefined | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned => { | |
| let timeZoneOption = options?.timeZone | |
| if (timeZoneOption === undefined && isDateTime(input) && isZoned(input)) { | |
| return input | |
| } | |
| const self = makeUnsafe(input) | |
| if (self.epochMilliseconds < minEpochMillis || self.epochMilliseconds > maxEpochMillis) { | |
| throw new RangeError(`Epoch millis out of range: ${self.epochMilliseconds}`) | |
| } | |
| if (timeZoneOption === undefined && typeof input === "object" && "timeZoneId" in input) { | |
| timeZoneOption = input.timeZoneId | |
| } | |
| let zone: DateTime.TimeZone | |
| if (timeZoneOption === undefined) { | |
| const offset = new Date(self.epochMilliseconds).getTimezoneOffset() * -60 * 1000 | |
| zone = zoneMakeOffset(offset) | |
| } else if (isTimeZone(timeZoneOption)) { | |
| zone = timeZoneOption | |
| } else if (typeof timeZoneOption === "number") { | |
| zone = zoneMakeOffset(timeZoneOption) | |
| } else { | |
| const parsedZone = zoneFromString(timeZoneOption) | |
| if (Option.isNone(parsedZone)) { | |
| throw new IllegalArgumentError(`Invalid time zone: ${timeZoneOption}`) | |
| } | |
| zone = parsedZone.value | |
| } | |
| if (options?.adjustForTimeZone !== true) { | |
| return makeZonedProto(self.epochMilliseconds, zone, self.partsUtc) | |
| } | |
| return makeZonedFromAdjusted(self.epochMilliseconds, zone, options?.disambiguation ?? "compatible") | |
| } | |
| /** @internal */ | |
| export const makeZoned: ( | |
| input: DateTime.DateTime.Input, | |
| options?: { | |
| readonly timeZone?: number | string | DateTime.TimeZone | undefined | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| } | |
| ) => Option.Option<DateTime.Zoned> = Option.liftThrowable(makeZonedUnsafe) | |
| /** @internal */ | |
| export const make: <A extends DateTime.DateTime.Input>(input: A) => Option.Option<DateTime.DateTime.PreserveZone<A>> = | |
| Option.liftThrowable(makeUnsafe) | |
| const zonedStringRegExp = /^(.{17,35})\[(.+)\]$/ | |
| /** @internal */ | |
| export const makeZonedFromString = (input: string): Option.Option<DateTime.Zoned> => { | |
| const match = zonedStringRegExp.exec(input) | |
| if (match === null) { | |
| const offset = parseOffset(input) | |
| return offset !== null ? makeZoned(input, { timeZone: offset }) : Option.none() | |
| } | |
| const [, isoString, timeZone] = match | |
| return makeZoned(isoString, { timeZone }) | |
| } | |
| /** @internal */ | |
| export const now: Effect.Effect<DateTime.Utc> = effect.map(Clock.currentTimeMillis, makeUtc) | |
| /** @internal */ | |
| export const nowAsDate: Effect.Effect<Date> = effect.map(Clock.currentTimeMillis, (millis) => new Date(millis)) | |
| /** @internal */ | |
| export const nowUnsafe: LazyArg<DateTime.Utc> = () => makeUtc(Date.now()) | |
| // ============================================================================= | |
| // time zones | |
| // ============================================================================= | |
| /** @internal */ | |
| export const toUtc = (self: DateTime.DateTime): DateTime.Utc => makeUtc(self.epochMilliseconds) | |
| /** @internal */ | |
| export const setZone: { | |
| (zone: DateTime.TimeZone, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): (self: DateTime.DateTime) => DateTime.Zoned | |
| (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned => | |
| options?.adjustForTimeZone === true | |
| ? makeZonedFromAdjusted(self.epochMilliseconds, zone, options?.disambiguation ?? "compatible") | |
| : makeZonedProto(self.epochMilliseconds, zone, self.partsUtc)) | |
| /** @internal */ | |
| export const setZoneOffset: { | |
| (offset: number, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): (self: DateTime.DateTime) => DateTime.Zoned | |
| (self: DateTime.DateTime, offset: number, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, offset: number, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned => setZone(self, zoneMakeOffset(offset), options)) | |
| const validZoneCache = new Map<string, DateTime.TimeZone.Named>() | |
| const formatOptions: Intl.DateTimeFormatOptions = { | |
| day: "numeric", | |
| month: "numeric", | |
| year: "numeric", | |
| hour: "numeric", | |
| minute: "numeric", | |
| second: "numeric", | |
| timeZoneName: "longOffset", | |
| fractionalSecondDigits: 3, | |
| hourCycle: "h23" | |
| } | |
| const zoneMakeIntl = (format: Intl.DateTimeFormat): DateTime.TimeZone.Named => { | |
| const zoneId = format.resolvedOptions().timeZone | |
| if (validZoneCache.has(zoneId)) { | |
| return validZoneCache.get(zoneId)! | |
| } | |
| const zone = Object.create(ProtoTimeZoneNamed) | |
| zone.id = zoneId | |
| zone.format = format | |
| validZoneCache.set(zoneId, zone) | |
| return zone | |
| } | |
| /** @internal */ | |
| export const zoneMakeNamedUnsafe = (zoneId: string): DateTime.TimeZone.Named => { | |
| if (validZoneCache.has(zoneId)) { | |
| return validZoneCache.get(zoneId)! | |
| } | |
| try { | |
| return zoneMakeIntl( | |
| new Intl.DateTimeFormat("en-US", { | |
| ...formatOptions, | |
| timeZone: zoneId | |
| }) | |
| ) | |
| } catch { | |
| throw new IllegalArgumentError(`Invalid time zone: ${zoneId}`) | |
| } | |
| } | |
| /** @internal */ | |
| export const zoneMakeOffset = (offset: number): DateTime.TimeZone.Offset => { | |
| const zone = Object.create(ProtoTimeZoneOffset) | |
| zone.offset = offset | |
| return zone | |
| } | |
| /** @internal */ | |
| export const zoneMakeNamed: (zoneId: string) => Option.Option<DateTime.TimeZone.Named> = Option.liftThrowable( | |
| zoneMakeNamedUnsafe | |
| ) | |
| /** @internal */ | |
| export const zoneMakeNamedEffect = (zoneId: string): Effect.Effect<DateTime.TimeZone.Named, IllegalArgumentError> => | |
| effect.try({ | |
| try: () => zoneMakeNamedUnsafe(zoneId), | |
| catch: (e) => e as IllegalArgumentError | |
| }) | |
| /** @internal */ | |
| export const zoneMakeLocal = (): DateTime.TimeZone.Named => | |
| zoneMakeIntl(new Intl.DateTimeFormat("en-US", formatOptions)) | |
| const offsetZoneRegExp = /^(?:GMT|[+-])/ | |
| /** @internal */ | |
| export const zoneFromString = (zone: string): Option.Option<DateTime.TimeZone> => { | |
| if (offsetZoneRegExp.test(zone)) { | |
| const offset = parseOffset(zone) | |
| return offset === null ? Option.none() : Option.some(zoneMakeOffset(offset)) | |
| } | |
| return zoneMakeNamed(zone) | |
| } | |
| /** @internal */ | |
| export const zoneToString = (self: DateTime.TimeZone): string => { | |
| if (self._tag === "Offset") { | |
| return offsetToString(self.offset) | |
| } | |
| return self.id | |
| } | |
| /** @internal */ | |
| export const setZoneNamed: { | |
| (zoneId: string, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): (self: DateTime.DateTime) => Option.Option<DateTime.Zoned> | |
| (self: DateTime.DateTime, zoneId: string, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): Option.Option<DateTime.Zoned> | |
| } = dual( | |
| isDateTimeArgs, | |
| (self: DateTime.DateTime, zoneId: string, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): Option.Option<DateTime.Zoned> => Option.map(zoneMakeNamed(zoneId), (zone) => setZone(self, zone, options)) | |
| ) | |
| /** @internal */ | |
| export const setZoneNamedUnsafe: { | |
| (zoneId: string, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): (self: DateTime.DateTime) => DateTime.Zoned | |
| (self: DateTime.DateTime, zoneId: string, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, zoneId: string, options?: { | |
| readonly adjustForTimeZone?: boolean | undefined | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.Zoned => setZone(self, zoneMakeNamedUnsafe(zoneId), options)) | |
| // ============================================================================= | |
| // comparisons | |
| // ============================================================================= | |
| /** @internal */ | |
| export const distance: { | |
| (other: DateTime.DateTime): (self: DateTime.DateTime) => Duration.Duration | |
| (self: DateTime.DateTime, other: DateTime.DateTime): Duration.Duration | |
| } = dual( | |
| 2, | |
| (self: DateTime.DateTime, other: DateTime.DateTime): Duration.Duration => | |
| Duration.millis(toEpochMillis(other) - toEpochMillis(self)) | |
| ) | |
| /** @internal */ | |
| export const min: { | |
| <That extends DateTime.DateTime>(that: That): <Self extends DateTime.DateTime>(self: Self) => Self | That | |
| <Self extends DateTime.DateTime, That extends DateTime.DateTime>(self: Self, that: That): Self | That | |
| } = order.min(Order) | |
| /** @internal */ | |
| export const max: { | |
| <That extends DateTime.DateTime>(that: That): <Self extends DateTime.DateTime>(self: Self) => Self | That | |
| <Self extends DateTime.DateTime, That extends DateTime.DateTime>(self: Self, that: That): Self | That | |
| } = order.max(Order) | |
| /** @internal */ | |
| export const isGreaterThan: { | |
| (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean | |
| (self: DateTime.DateTime, that: DateTime.DateTime): boolean | |
| } = order.isGreaterThan(Order) | |
| /** @internal */ | |
| export const isGreaterThanOrEqualTo: { | |
| (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean | |
| (self: DateTime.DateTime, that: DateTime.DateTime): boolean | |
| } = order.isGreaterThanOrEqualTo(Order) | |
| /** @internal */ | |
| export const isLessThan: { | |
| (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean | |
| (self: DateTime.DateTime, that: DateTime.DateTime): boolean | |
| } = order.isLessThan(Order) | |
| /** @internal */ | |
| export const isLessThanOrEqualTo: { | |
| (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean | |
| (self: DateTime.DateTime, that: DateTime.DateTime): boolean | |
| } = order.isLessThanOrEqualTo(Order) | |
| /** @internal */ | |
| export const between: { | |
| (options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): (self: DateTime.DateTime) => boolean | |
| (self: DateTime.DateTime, options: { minimum: DateTime.DateTime; maximum: DateTime.DateTime }): boolean | |
| } = order.isBetween(Order) | |
| /** @internal */ | |
| export const isFuture = (self: DateTime.DateTime): Effect.Effect<boolean> => effect.map(now, isLessThan(self)) | |
| /** @internal */ | |
| export const isFutureUnsafe = (self: DateTime.DateTime): boolean => isLessThan(nowUnsafe(), self) | |
| /** @internal */ | |
| export const isPast = (self: DateTime.DateTime): Effect.Effect<boolean> => effect.map(now, isGreaterThan(self)) | |
| /** @internal */ | |
| export const isPastUnsafe = (self: DateTime.DateTime): boolean => isGreaterThan(nowUnsafe(), self) | |
| // ============================================================================= | |
| // conversions | |
| // ============================================================================= | |
| /** @internal */ | |
| export const toDateUtc = (self: DateTime.DateTime): Date => new Date(self.epochMilliseconds) | |
| /** @internal */ | |
| export const toDate = (self: DateTime.DateTime): Date => { | |
| if (self._tag === "Utc") { | |
| return new Date(self.epochMilliseconds) | |
| } else if (self.zone._tag === "Offset") { | |
| return new Date(self.epochMilliseconds + self.zone.offset) | |
| } else if (self.adjustedEpochMilliseconds !== undefined) { | |
| return new Date(self.adjustedEpochMilliseconds) | |
| } | |
| const parts = self.zone.format.formatToParts(self.epochMilliseconds).filter((_) => _.type !== "literal") | |
| const date = new Date(0) | |
| date.setUTCFullYear( | |
| Number(parts[2].value), | |
| Number(parts[0].value) - 1, | |
| Number(parts[1].value) | |
| ) | |
| date.setUTCHours( | |
| Number(parts[3].value), | |
| Number(parts[4].value), | |
| Number(parts[5].value), | |
| Number(parts[6].value) | |
| ) | |
| self.adjustedEpochMilliseconds = date.getTime() | |
| return date | |
| } | |
| /** @internal */ | |
| export const zonedOffset = (self: DateTime.Zoned): number => { | |
| const date = toDate(self) | |
| return date.getTime() - toEpochMillis(self) | |
| } | |
| const offsetToString = (offset: number): string => { | |
| const abs = Math.abs(offset) | |
| let hours = Math.floor(abs / (60 * 60 * 1000)) | |
| let minutes = Math.round((abs % (60 * 60 * 1000)) / (60 * 1000)) | |
| if (minutes === 60) { | |
| hours += 1 | |
| minutes = 0 | |
| } | |
| return `${offset < 0 ? "-" : "+"}${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}` | |
| } | |
| /** @internal */ | |
| export const zonedOffsetIso = (self: DateTime.Zoned): string => offsetToString(zonedOffset(self)) | |
| /** @internal */ | |
| export const toEpochMillis = (self: DateTime.DateTime): number => self.epochMilliseconds | |
| /** @internal */ | |
| export const removeTime = (self: DateTime.DateTime): DateTime.Utc => | |
| withDate(self, (date) => { | |
| date.setUTCHours(0, 0, 0, 0) | |
| return makeUtc(date.getTime()) | |
| }) | |
| // ============================================================================= | |
| // parts | |
| // ============================================================================= | |
| const dateToParts = (date: Date): DateTime.DateTime.PartsWithWeekday => ({ | |
| millisecond: date.getUTCMilliseconds(), | |
| second: date.getUTCSeconds(), | |
| minute: date.getUTCMinutes(), | |
| hour: date.getUTCHours(), | |
| day: date.getUTCDate(), | |
| weekDay: date.getUTCDay(), | |
| month: date.getUTCMonth() + 1, | |
| year: date.getUTCFullYear() | |
| }) | |
| /** @internal */ | |
| export const toParts = (self: DateTime.DateTime): DateTime.DateTime.PartsWithWeekday => { | |
| if (self._tag === "Utc") { | |
| return toPartsUtc(self) | |
| } else if (self.partsAdjusted !== undefined) { | |
| return self.partsAdjusted | |
| } | |
| self.partsAdjusted = withDate(self, dateToParts) | |
| return self.partsAdjusted | |
| } | |
| /** @internal */ | |
| export const toPartsUtc = (self: DateTime.DateTime): DateTime.DateTime.PartsWithWeekday => { | |
| if (self.partsUtc !== undefined) { | |
| return self.partsUtc | |
| } | |
| self.partsUtc = withDateUtc(self, dateToParts) | |
| return self.partsUtc | |
| } | |
| /** @internal */ | |
| export const getPartUtc: { | |
| (part: keyof DateTime.DateTime.PartsWithWeekday): (self: DateTime.DateTime) => number | |
| (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number | |
| } = dual(2, (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number => toPartsUtc(self)[part]) | |
| /** @internal */ | |
| export const getPart: { | |
| (part: keyof DateTime.DateTime.PartsWithWeekday): (self: DateTime.DateTime) => number | |
| (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number | |
| } = dual(2, (self: DateTime.DateTime, part: keyof DateTime.DateTime.PartsWithWeekday): number => toParts(self)[part]) | |
| const setPartsDate = (date: Date, parts: Partial<DateTime.DateTime.PartsWithWeekday>): void => { | |
| if (parts.year !== undefined) { | |
| date.setUTCFullYear(parts.year) | |
| } | |
| if (parts.month !== undefined) { | |
| date.setUTCMonth(parts.month - 1) | |
| } | |
| if (parts.day !== undefined) { | |
| date.setUTCDate(parts.day) | |
| } | |
| if (parts.weekDay !== undefined) { | |
| const diff = parts.weekDay - date.getUTCDay() | |
| date.setUTCDate(date.getUTCDate() + diff) | |
| } | |
| if (parts.hour !== undefined) { | |
| date.setUTCHours(parts.hour) | |
| } | |
| if (parts.minute !== undefined) { | |
| date.setUTCMinutes(parts.minute) | |
| } | |
| if (parts.second !== undefined) { | |
| date.setUTCSeconds(parts.second) | |
| } | |
| if (parts.millisecond !== undefined) { | |
| date.setUTCMilliseconds(parts.millisecond) | |
| } | |
| } | |
| /** @internal */ | |
| export const setParts: { | |
| ( | |
| parts: Partial<DateTime.DateTime.PartsWithWeekday> | |
| ): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>( | |
| self: A, | |
| parts: Partial<DateTime.DateTime.PartsWithWeekday> | |
| ): A | |
| } = dual( | |
| 2, | |
| (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsWithWeekday>): DateTime.DateTime => | |
| mutate(self, (date) => setPartsDate(date, parts)) | |
| ) | |
| /** @internal */ | |
| export const setPartsUtc: { | |
| ( | |
| parts: Partial<DateTime.DateTime.PartsWithWeekday> | |
| ): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>( | |
| self: A, | |
| parts: Partial<DateTime.DateTime.PartsWithWeekday> | |
| ): A | |
| } = dual( | |
| 2, | |
| (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsWithWeekday>): DateTime.DateTime => | |
| mutateUtc(self, (date) => setPartsDate(date, parts)) | |
| ) | |
| // ============================================================================= | |
| // mapping | |
| // ============================================================================= | |
| const constDayMillis = 24 * 60 * 60 * 1000 | |
| const makeZonedFromAdjusted = ( | |
| adjustedMillis: number, | |
| zone: DateTime.TimeZone, | |
| disambiguation: DateTime.Disambiguation | |
| ): DateTime.Zoned => { | |
| if (zone._tag === "Offset") { | |
| return makeZonedProto(adjustedMillis - zone.offset, zone) | |
| } | |
| const beforeOffset = calculateNamedOffset( | |
| adjustedMillis - constDayMillis, | |
| adjustedMillis, | |
| zone | |
| ) | |
| const afterOffset = calculateNamedOffset( | |
| adjustedMillis + constDayMillis, | |
| adjustedMillis, | |
| zone | |
| ) | |
| // If there is no transition, we can return early | |
| if (beforeOffset === afterOffset) { | |
| return makeZonedProto(adjustedMillis - beforeOffset, zone) | |
| } | |
| const isForwards = beforeOffset < afterOffset | |
| const transitionMillis = beforeOffset - afterOffset | |
| // If the transition is forwards, we only need to check if we should move the | |
| // local wall clock time forward if it is inside the gap | |
| if (isForwards) { | |
| const currentAfterOffset = calculateNamedOffset( | |
| adjustedMillis - afterOffset, | |
| adjustedMillis, | |
| zone | |
| ) | |
| if (currentAfterOffset === afterOffset) { | |
| return makeZonedProto(adjustedMillis - afterOffset, zone) | |
| } | |
| const before = makeZonedProto(adjustedMillis - beforeOffset, zone) | |
| const beforeAdjustedMillis = toDate(before).getTime() | |
| // If the wall clock time has changed, we are inside the gap | |
| if (adjustedMillis !== beforeAdjustedMillis) { | |
| switch (disambiguation) { | |
| case "reject": { | |
| const formatted = new Date(adjustedMillis).toISOString() | |
| throw new RangeError(`Gap time: ${formatted} does not exist in time zone ${zone.id}`) | |
| } | |
| case "earlier": | |
| return makeZonedProto(adjustedMillis - afterOffset, zone) | |
| case "compatible": | |
| case "later": | |
| return before | |
| } | |
| } | |
| // The wall clock time is in the earlier offset, so we use that | |
| return before | |
| } | |
| const currentBeforeOffset = calculateNamedOffset( | |
| adjustedMillis - beforeOffset, | |
| adjustedMillis, | |
| zone | |
| ) | |
| // The wall clock time is in the earlier offset, so we use that | |
| if (currentBeforeOffset === beforeOffset) { | |
| if (disambiguation === "earlier" || disambiguation === "compatible") { | |
| return makeZonedProto(adjustedMillis - beforeOffset, zone) | |
| } | |
| const laterOffset = calculateNamedOffset( | |
| adjustedMillis - beforeOffset + transitionMillis, | |
| adjustedMillis + transitionMillis, | |
| zone | |
| ) | |
| if (laterOffset === beforeOffset) { | |
| return makeZonedProto(adjustedMillis - beforeOffset, zone) | |
| } | |
| // If the offset changed in this period, then we are inside the period where | |
| // the wall clock time occurs twice, once in the earlier offset and once in | |
| // the later offset. | |
| if (disambiguation === "reject") { | |
| const formatted = new Date(adjustedMillis).toISOString() | |
| throw new RangeError(`Ambiguous time: ${formatted} occurs twice in time zone ${zone.id}`) | |
| } | |
| // If the disambiguation is "later", we return the later offset below | |
| } | |
| return makeZonedProto(adjustedMillis - afterOffset, zone) | |
| } | |
| const offsetRegExp = /([+-])(\d{2}):(\d{2})$/ | |
| const parseOffset = (offset: string): number | null => { | |
| const match = offsetRegExp.exec(offset) | |
| if (match === null) { | |
| return null | |
| } | |
| const [, sign, hours, minutes] = match | |
| return (sign === "+" ? 1 : -1) * (Number(hours) * 60 + Number(minutes)) * 60 * 1000 | |
| } | |
| const calculateNamedOffset = ( | |
| utcMillis: number, | |
| adjustedMillis: number, | |
| zone: DateTime.TimeZone.Named | |
| ): number => { | |
| const offset = zone.format.formatToParts(utcMillis).find((_) => _.type === "timeZoneName")?.value ?? "" | |
| if (offset === "GMT") { | |
| return 0 | |
| } | |
| const result = parseOffset(offset) | |
| if (result === null) { | |
| // fallback to using the adjusted date | |
| return zonedOffset(makeZonedProto(adjustedMillis, zone)) | |
| } | |
| return result | |
| } | |
| /** @internal */ | |
| export const mutate: { | |
| (f: (date: Date) => void, options?: { | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, f: (date: Date) => void, options?: { | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): A | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, f: (date: Date) => void, options?: { | |
| readonly disambiguation?: DateTime.Disambiguation | undefined | |
| }): DateTime.DateTime => { | |
| if (self._tag === "Utc") { | |
| const date = toDateUtc(self) | |
| f(date) | |
| return makeUtc(date.getTime()) | |
| } | |
| const adjustedDate = toDate(self) | |
| const newAdjustedDate = new Date(adjustedDate.getTime()) | |
| f(newAdjustedDate) | |
| return makeZonedFromAdjusted(newAdjustedDate.getTime(), self.zone, options?.disambiguation ?? "compatible") | |
| }) | |
| /** @internal */ | |
| export const mutateUtc: { | |
| (f: (date: Date) => void): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, f: (date: Date) => void): A | |
| } = dual(2, (self: DateTime.DateTime, f: (date: Date) => void): DateTime.DateTime => | |
| mapEpochMillis(self, (millis) => { | |
| const date = new Date(millis) | |
| f(date) | |
| return date.getTime() | |
| })) | |
| /** @internal */ | |
| export const mapEpochMillis: { | |
| (f: (millis: number) => number): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, f: (millis: number) => number): A | |
| } = dual(2, (self: DateTime.DateTime, f: (millis: number) => number): DateTime.DateTime => { | |
| const millis = f(toEpochMillis(self)) | |
| return self._tag === "Utc" ? makeUtc(millis) : makeZonedProto(millis, self.zone) | |
| }) | |
| /** @internal */ | |
| export const withDate: { | |
| <A>(f: (date: Date) => A): (self: DateTime.DateTime) => A | |
| <A>(self: DateTime.DateTime, f: (date: Date) => A): A | |
| } = dual(2, <A>(self: DateTime.DateTime, f: (date: Date) => A): A => f(toDate(self))) | |
| /** @internal */ | |
| export const withDateUtc: { | |
| <A>(f: (date: Date) => A): (self: DateTime.DateTime) => A | |
| <A>(self: DateTime.DateTime, f: (date: Date) => A): A | |
| } = dual(2, <A>(self: DateTime.DateTime, f: (date: Date) => A): A => f(toDateUtc(self))) | |
| /** @internal */ | |
| export const match: { | |
| <A, B>(options: { | |
| readonly onUtc: (_: DateTime.Utc) => A | |
| readonly onZoned: (_: DateTime.Zoned) => B | |
| }): (self: DateTime.DateTime) => A | B | |
| <A, B>(self: DateTime.DateTime, options: { | |
| readonly onUtc: (_: DateTime.Utc) => A | |
| readonly onZoned: (_: DateTime.Zoned) => B | |
| }): A | B | |
| } = dual(2, <A, B>(self: DateTime.DateTime, options: { | |
| readonly onUtc: (_: DateTime.Utc) => A | |
| readonly onZoned: (_: DateTime.Zoned) => B | |
| }): A | B => self._tag === "Utc" ? options.onUtc(self) : options.onZoned(self)) | |
| // ============================================================================= | |
| // math | |
| // ============================================================================= | |
| /** @internal */ | |
| export const addDuration: { | |
| (duration: Duration.Input): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, duration: Duration.Input): A | |
| } = dual( | |
| 2, | |
| (self: DateTime.DateTime, duration: Duration.Input): DateTime.DateTime => | |
| mapEpochMillis(self, (millis) => millis + Duration.toMillis(Duration.fromInputUnsafe(duration))) | |
| ) | |
| /** @internal */ | |
| export const subtractDuration: { | |
| (duration: Duration.Input): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, duration: Duration.Input): A | |
| } = dual( | |
| 2, | |
| (self: DateTime.DateTime, duration: Duration.Input): DateTime.DateTime => | |
| mapEpochMillis(self, (millis) => millis - Duration.toMillis(Duration.fromInputUnsafe(duration))) | |
| ) | |
| const addMillis = (date: Date, amount: number): void => { | |
| date.setTime(date.getTime() + amount) | |
| } | |
| /** @internal */ | |
| export const add: { | |
| ( | |
| parts: Partial<DateTime.DateTime.PartsForMath> | |
| ): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>( | |
| self: A, | |
| parts: Partial<DateTime.DateTime.PartsForMath> | |
| ): A | |
| } = dual( | |
| 2, | |
| (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsForMath>): DateTime.DateTime => | |
| mutate(self, (date) => { | |
| if (parts.milliseconds) { | |
| addMillis(date, parts.milliseconds) | |
| } | |
| if (parts.seconds) { | |
| addMillis(date, parts.seconds * 1000) | |
| } | |
| if (parts.minutes) { | |
| addMillis(date, parts.minutes * 60 * 1000) | |
| } | |
| if (parts.hours) { | |
| addMillis(date, parts.hours * 60 * 60 * 1000) | |
| } | |
| if (parts.days) { | |
| date.setUTCDate(date.getUTCDate() + parts.days) | |
| } | |
| if (parts.weeks) { | |
| date.setUTCDate(date.getUTCDate() + parts.weeks * 7) | |
| } | |
| if (parts.months) { | |
| const day = date.getUTCDate() | |
| date.setUTCMonth(date.getUTCMonth() + parts.months + 1, 0) | |
| if (day < date.getUTCDate()) { | |
| date.setUTCDate(day) | |
| } | |
| } | |
| if (parts.years) { | |
| const day = date.getUTCDate() | |
| const month = date.getUTCMonth() | |
| date.setUTCFullYear( | |
| date.getUTCFullYear() + parts.years, | |
| month + 1, | |
| 0 | |
| ) | |
| if (day < date.getUTCDate()) { | |
| date.setUTCDate(day) | |
| } | |
| } | |
| }) | |
| ) | |
| /** @internal */ | |
| export const subtract: { | |
| ( | |
| parts: Partial<DateTime.DateTime.PartsForMath> | |
| ): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>( | |
| self: A, | |
| parts: Partial<DateTime.DateTime.PartsForMath> | |
| ): A | |
| } = dual(2, (self: DateTime.DateTime, parts: Partial<DateTime.DateTime.PartsForMath>): DateTime.DateTime => { | |
| const newParts = {} as Partial<Mutable<DateTime.DateTime.PartsForMath>> | |
| for (const key in parts) { | |
| newParts[key as keyof DateTime.DateTime.PartsForMath] = -1 * parts[key as keyof DateTime.DateTime.PartsForMath]! | |
| } | |
| return add(self, newParts) | |
| }) | |
| const startOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }) => { | |
| switch (part) { | |
| case "second": { | |
| date.setUTCMilliseconds(0) | |
| break | |
| } | |
| case "minute": { | |
| date.setUTCSeconds(0, 0) | |
| break | |
| } | |
| case "hour": { | |
| date.setUTCMinutes(0, 0, 0) | |
| break | |
| } | |
| case "day": { | |
| date.setUTCHours(0, 0, 0, 0) | |
| break | |
| } | |
| case "week": { | |
| const weekStartsOn = options?.weekStartsOn ?? 0 | |
| const day = date.getUTCDay() | |
| const diff = (day - weekStartsOn + 7) % 7 | |
| date.setUTCDate(date.getUTCDate() - diff) | |
| date.setUTCHours(0, 0, 0, 0) | |
| break | |
| } | |
| case "month": { | |
| date.setUTCDate(1) | |
| date.setUTCHours(0, 0, 0, 0) | |
| break | |
| } | |
| case "year": { | |
| date.setUTCMonth(0, 1) | |
| date.setUTCHours(0, 0, 0, 0) | |
| break | |
| } | |
| } | |
| } | |
| /** @internal */ | |
| export const startOf: { | |
| (part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): A | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): DateTime.DateTime => mutate(self, (date) => startOfDate(date, part, options))) | |
| const endOfDate = (date: Date, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }) => { | |
| switch (part) { | |
| case "second": { | |
| date.setUTCMilliseconds(999) | |
| break | |
| } | |
| case "minute": { | |
| date.setUTCSeconds(59, 999) | |
| break | |
| } | |
| case "hour": { | |
| date.setUTCMinutes(59, 59, 999) | |
| break | |
| } | |
| case "day": { | |
| date.setUTCHours(23, 59, 59, 999) | |
| break | |
| } | |
| case "week": { | |
| const weekStartsOn = options?.weekStartsOn ?? 0 | |
| const day = date.getUTCDay() | |
| const diff = (day - weekStartsOn + 7) % 7 | |
| date.setUTCDate(date.getUTCDate() - diff + 6) | |
| date.setUTCHours(23, 59, 59, 999) | |
| break | |
| } | |
| case "month": { | |
| date.setUTCMonth(date.getUTCMonth() + 1, 0) | |
| date.setUTCHours(23, 59, 59, 999) | |
| break | |
| } | |
| case "year": { | |
| date.setUTCMonth(11, 31) | |
| date.setUTCHours(23, 59, 59, 999) | |
| break | |
| } | |
| } | |
| } | |
| /** @internal */ | |
| export const endOf: { | |
| (part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): A | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): DateTime.DateTime => mutate(self, (date) => endOfDate(date, part, options))) | |
| /** @internal */ | |
| export const nearest: { | |
| (part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): <A extends DateTime.DateTime>(self: A) => A | |
| <A extends DateTime.DateTime>(self: A, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): A | |
| } = dual(isDateTimeArgs, (self: DateTime.DateTime, part: DateTime.DateTime.UnitSingular, options?: { | |
| readonly weekStartsOn?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | undefined | |
| }): DateTime.DateTime => | |
| mutate(self, (date) => { | |
| if (part === "millisecond") return | |
| const millis = date.getTime() | |
| const start = new Date(millis) | |
| startOfDate(start, part, options) | |
| const startMillis = start.getTime() | |
| const end = new Date(millis) | |
| endOfDate(end, part, options) | |
| const endMillis = end.getTime() + 1 | |
| const diffStart = millis - startMillis | |
| const diffEnd = endMillis - millis | |
| if (diffStart < diffEnd) { | |
| date.setTime(startMillis) | |
| } else { | |
| date.setTime(endMillis) | |
| } | |
| })) | |
| // ============================================================================= | |
| // formatting | |
| // ============================================================================= | |
| const intlTimeZone = (self: DateTime.TimeZone): string => { | |
| if (self._tag === "Named") { | |
| return self.id | |
| } | |
| return offsetToString(self.offset) | |
| } | |
| /** @internal */ | |
| export const format: { | |
| ( | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): (self: DateTime.DateTime) => string | |
| ( | |
| self: DateTime.DateTime, | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): string | |
| } = dual(isDateTimeArgs, ( | |
| self: DateTime.DateTime, | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): string => { | |
| try { | |
| return new Intl.DateTimeFormat(options?.locale, { | |
| timeZone: self._tag === "Utc" ? "UTC" : intlTimeZone(self.zone), | |
| ...options | |
| }).format(self.epochMilliseconds) | |
| } catch { | |
| return new Intl.DateTimeFormat(options?.locale, { | |
| timeZone: "UTC", | |
| ...options | |
| }).format(toDate(self)) | |
| } | |
| }) | |
| /** @internal */ | |
| export const formatLocal: { | |
| ( | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): (self: DateTime.DateTime) => string | |
| ( | |
| self: DateTime.DateTime, | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): string | |
| } = dual(isDateTimeArgs, ( | |
| self: DateTime.DateTime, | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): string => new Intl.DateTimeFormat(options?.locale, options).format(self.epochMilliseconds)) | |
| /** @internal */ | |
| export const formatUtc: { | |
| ( | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): (self: DateTime.DateTime) => string | |
| ( | |
| self: DateTime.DateTime, | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): string | |
| } = dual(isDateTimeArgs, ( | |
| self: DateTime.DateTime, | |
| options?: | |
| | Intl.DateTimeFormatOptions & { | |
| readonly locale?: Intl.LocalesArgument | |
| } | |
| | undefined | |
| ): string => | |
| new Intl.DateTimeFormat(options?.locale, { | |
| ...options, | |
| timeZone: "UTC" | |
| }).format(self.epochMilliseconds)) | |
| /** @internal */ | |
| export const formatIntl: { | |
| (format: Intl.DateTimeFormat): (self: DateTime.DateTime) => string | |
| (self: DateTime.DateTime, format: Intl.DateTimeFormat): string | |
| } = dual(2, (self: DateTime.DateTime, format: Intl.DateTimeFormat): string => format.format(self.epochMilliseconds)) | |
| /** @internal */ | |
| export const formatIso = (self: DateTime.DateTime): string => toDateUtc(self).toISOString() | |
| /** @internal */ | |
| export const formatIsoDate = (self: DateTime.DateTime): string => toDate(self).toISOString().slice(0, 10) | |
| /** @internal */ | |
| export const formatIsoDateUtc = (self: DateTime.DateTime): string => toDateUtc(self).toISOString().slice(0, 10) | |
| /** @internal */ | |
| export const formatIsoOffset = (self: DateTime.DateTime): string => { | |
| const date = toDate(self) | |
| return self._tag === "Utc" ? date.toISOString() : `${date.toISOString().slice(0, -1)}${zonedOffsetIso(self)}` | |
| } | |
| /** @internal */ | |
| export const formatIsoZoned = (self: DateTime.Zoned): string => | |
| self.zone._tag === "Offset" ? formatIsoOffset(self) : `${formatIsoOffset(self)}[${self.zone.id}]` | |
Xet Storage Details
- Size:
- 42.5 kB
- Xet hash:
- 85f07ee676674b4c2ef2633b3b7918d43a4c271455763b7eec6218866d161203
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.