Skip to main content
Glama
ssv445

Lorem Ipsum MCP Server

by ssv445
dateTime.ts38.7 kB
import { IllegalArgumentException } from "../Cause.js" import * as Clock from "../Clock.js" import type * as DateTime from "../DateTime.js" import * as Duration from "../Duration.js" import type * as Effect from "../Effect.js" import * as Either from "../Either.js" import * as Equal from "../Equal.js" import * as equivalence from "../Equivalence.js" import type { LazyArg } from "../Function.js" import { dual, pipe } from "../Function.js" import { globalValue } from "../GlobalValue.js" import * as Hash from "../Hash.js" import * as Inspectable from "../Inspectable.js" import * as Option from "../Option.js" import * as order from "../Order.js" import { pipeArguments } from "../Pipeable.js" import * as Predicate from "../Predicate.js" import type { Mutable } from "../Types.js" import * as internalEffect from "./core-effect.js" import * as core from "./core.js" /** @internal */ export const TypeId: DateTime.TypeId = Symbol.for("effect/DateTime") as DateTime.TypeId /** @internal */ export const TimeZoneTypeId: DateTime.TimeZoneTypeId = Symbol.for("effect/DateTime/TimeZone") as DateTime.TimeZoneTypeId 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.cached(this, Hash.number(this.epochMillis)) }, [Equal.symbol](this: DateTime.Utc, that: unknown) { return isDateTime(that) && that._tag === "Utc" && this.epochMillis === that.epochMillis }, toString(this: DateTime.Utc) { return `DateTime.Utc(${toDateUtc(this).toJSON()})` } } const ProtoZoned = { ...Proto, _tag: "Zoned", [Hash.symbol](this: DateTime.Zoned) { return pipe( Hash.number(this.epochMillis), Hash.combine(Hash.hash(this.zone)), Hash.cached(this) ) }, [Equal.symbol](this: DateTime.Zoned, that: unknown) { return isDateTime(that) && that._tag === "Zoned" && this.epochMillis === that.epochMillis && 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.cached(this, 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.cached(this, 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.epochMillis = 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: equivalence.Equivalence<DateTime.DateTime> = equivalence.make((a, b) => a.epochMillis === b.epochMillis ) /** @internal */ export const Order: order.Order<DateTime.DateTime> = order.make((self, that) => self.epochMillis < that.epochMillis ? -1 : self.epochMillis > that.epochMillis ? 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.epochMillis = epochMillis Object.defineProperty(self, "partsUtc", { value: undefined, enumerable: false, writable: true }) return self } /** @internal */ export const unsafeFromDate = (date: Date): DateTime.Utc => { const epochMillis = date.getTime() if (Number.isNaN(epochMillis)) { throw new IllegalArgumentException("Invalid date") } return makeUtc(epochMillis) } /** @internal */ export const unsafeMake = <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 unsafeFromDate(input) as DateTime.DateTime.PreserveZone<A> } else if (typeof input === "object") { const date = new Date(0) setPartsDate(date, input) return unsafeFromDate(date) as DateTime.DateTime.PreserveZone<A> } else if (typeof input === "string" && !hasZone(input)) { return unsafeFromDate(new Date(input + "Z")) as DateTime.DateTime.PreserveZone<A> } return unsafeFromDate(new Date(input)) as DateTime.DateTime.PreserveZone<A> } const hasZone = (input: string): boolean => /Z|[+-]\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 unsafeMakeZoned = (input: DateTime.DateTime.Input, options?: { readonly timeZone?: number | string | DateTime.TimeZone | undefined readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned => { if (options?.timeZone === undefined && isDateTime(input) && isZoned(input)) { return input } const self = unsafeMake(input) if (self.epochMillis < minEpochMillis || self.epochMillis > maxEpochMillis) { throw new IllegalArgumentException(`Epoch millis out of range: ${self.epochMillis}`) } let zone: DateTime.TimeZone if (options?.timeZone === undefined) { const offset = new Date(self.epochMillis).getTimezoneOffset() * -60 * 1000 zone = zoneMakeOffset(offset) } else if (isTimeZone(options?.timeZone)) { zone = options.timeZone } else if (typeof options?.timeZone === "number") { zone = zoneMakeOffset(options.timeZone) } else { const parsedZone = zoneFromString(options.timeZone) if (Option.isNone(parsedZone)) { throw new IllegalArgumentException(`Invalid time zone: ${options.timeZone}`) } zone = parsedZone.value } if (options?.adjustForTimeZone !== true) { return makeZonedProto(self.epochMillis, zone, self.partsUtc) } return makeZonedFromAdjusted(self.epochMillis, zone) } /** @internal */ export const makeZoned: ( input: DateTime.DateTime.Input, options?: { readonly timeZone?: number | string | DateTime.TimeZone | undefined readonly adjustForTimeZone?: boolean | undefined } ) => Option.Option<DateTime.Zoned> = Option.liftThrowable(unsafeMakeZoned) /** @internal */ export const make: <A extends DateTime.DateTime.Input>(input: A) => Option.Option<DateTime.DateTime.PreserveZone<A>> = Option.liftThrowable(unsafeMake) const zonedStringRegex = /^(.{17,35})\[(.+)\]$/ /** @internal */ export const makeZonedFromString = (input: string): Option.Option<DateTime.Zoned> => { const match = zonedStringRegex.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> = core.map(Clock.currentTimeMillis, makeUtc) /** @internal */ export const nowAsDate: Effect.Effect<Date> = core.map(Clock.currentTimeMillis, (millis) => new Date(millis)) /** @internal */ export const unsafeNow: LazyArg<DateTime.Utc> = () => makeUtc(Date.now()) // ============================================================================= // time zones // ============================================================================= /** @internal */ export const toUtc = (self: DateTime.DateTime): DateTime.Utc => makeUtc(self.epochMillis) /** @internal */ export const setZone: { (zone: DateTime.TimeZone, options?: { readonly adjustForTimeZone?: boolean | undefined }): (self: DateTime.DateTime) => DateTime.Zoned (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: { readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned } = dual(isDateTimeArgs, (self: DateTime.DateTime, zone: DateTime.TimeZone, options?: { readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned => options?.adjustForTimeZone === true ? makeZonedFromAdjusted(self.epochMillis, zone) : makeZonedProto(self.epochMillis, zone, self.partsUtc)) /** @internal */ export const setZoneOffset: { (offset: number, options?: { readonly adjustForTimeZone?: boolean | undefined }): (self: DateTime.DateTime) => DateTime.Zoned (self: DateTime.DateTime, offset: number, options?: { readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned } = dual(isDateTimeArgs, (self: DateTime.DateTime, offset: number, options?: { readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned => setZone(self, zoneMakeOffset(offset), options)) const validZoneCache = globalValue("effect/DateTime/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 zoneUnsafeMakeNamed = (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 IllegalArgumentException(`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( zoneUnsafeMakeNamed ) /** @internal */ export const zoneMakeNamedEffect = (zoneId: string): Effect.Effect<DateTime.TimeZone.Named, IllegalArgumentException> => internalEffect.try_({ try: () => zoneUnsafeMakeNamed(zoneId), catch: (e) => e as IllegalArgumentException }) /** @internal */ export const zoneMakeLocal = (): DateTime.TimeZone.Named => zoneMakeIntl(new Intl.DateTimeFormat("en-US", formatOptions)) const offsetZoneRegex = /^(?:GMT|[+-])/ /** @internal */ export const zoneFromString = (zone: string): Option.Option<DateTime.TimeZone> => { if (offsetZoneRegex.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 }): (self: DateTime.DateTime) => Option.Option<DateTime.Zoned> (self: DateTime.DateTime, zoneId: string, options?: { readonly adjustForTimeZone?: boolean | undefined }): Option.Option<DateTime.Zoned> } = dual( isDateTimeArgs, (self: DateTime.DateTime, zoneId: string, options?: { readonly adjustForTimeZone?: boolean | undefined }): Option.Option<DateTime.Zoned> => Option.map(zoneMakeNamed(zoneId), (zone) => setZone(self, zone, options)) ) /** @internal */ export const unsafeSetZoneNamed: { (zoneId: string, options?: { readonly adjustForTimeZone?: boolean | undefined }): (self: DateTime.DateTime) => DateTime.Zoned (self: DateTime.DateTime, zoneId: string, options?: { readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned } = dual(isDateTimeArgs, (self: DateTime.DateTime, zoneId: string, options?: { readonly adjustForTimeZone?: boolean | undefined }): DateTime.Zoned => setZone(self, zoneUnsafeMakeNamed(zoneId), options)) // ============================================================================= // comparisons // ============================================================================= /** @internal */ export const distance: { (other: DateTime.DateTime): (self: DateTime.DateTime) => number (self: DateTime.DateTime, other: DateTime.DateTime): number } = dual(2, (self: DateTime.DateTime, other: DateTime.DateTime): number => toEpochMillis(other) - toEpochMillis(self)) /** @internal */ export const distanceDurationEither: { (other: DateTime.DateTime): (self: DateTime.DateTime) => Either.Either<Duration.Duration, Duration.Duration> (self: DateTime.DateTime, other: DateTime.DateTime): Either.Either<Duration.Duration, Duration.Duration> } = dual( 2, (self: DateTime.DateTime, other: DateTime.DateTime): Either.Either<Duration.Duration, Duration.Duration> => { const diffMillis = distance(self, other) return diffMillis > 0 ? Either.right(Duration.millis(diffMillis)) : Either.left(Duration.millis(-diffMillis)) } ) /** @internal */ export const distanceDuration: { (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(Math.abs(distance(self, other))) ) /** @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 greaterThan: { (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean (self: DateTime.DateTime, that: DateTime.DateTime): boolean } = order.greaterThan(Order) /** @internal */ export const greaterThanOrEqualTo: { (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean (self: DateTime.DateTime, that: DateTime.DateTime): boolean } = order.greaterThanOrEqualTo(Order) /** @internal */ export const lessThan: { (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean (self: DateTime.DateTime, that: DateTime.DateTime): boolean } = order.lessThan(Order) /** @internal */ export const lessThanOrEqualTo: { (that: DateTime.DateTime): (self: DateTime.DateTime) => boolean (self: DateTime.DateTime, that: DateTime.DateTime): boolean } = order.lessThanOrEqualTo(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.between(Order) /** @internal */ export const isFuture = (self: DateTime.DateTime): Effect.Effect<boolean> => core.map(now, lessThan(self)) /** @internal */ export const unsafeIsFuture = (self: DateTime.DateTime): boolean => lessThan(unsafeNow(), self) /** @internal */ export const isPast = (self: DateTime.DateTime): Effect.Effect<boolean> => core.map(now, greaterThan(self)) /** @internal */ export const unsafeIsPast = (self: DateTime.DateTime): boolean => greaterThan(unsafeNow(), self) // ============================================================================= // conversions // ============================================================================= /** @internal */ export const toDateUtc = (self: DateTime.DateTime): Date => new Date(self.epochMillis) /** @internal */ export const toDate = (self: DateTime.DateTime): Date => { if (self._tag === "Utc") { return new Date(self.epochMillis) } else if (self.zone._tag === "Offset") { return new Date(self.epochMillis + self.zone.offset) } else if (self.adjustedEpochMillis !== undefined) { return new Date(self.adjustedEpochMillis) } const parts = self.zone.format.formatToParts(self.epochMillis).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.adjustedEpochMillis = 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.epochMillis /** @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 => ({ millis: date.getUTCMilliseconds(), seconds: date.getUTCSeconds(), minutes: date.getUTCMinutes(), hours: 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.hours !== undefined) { date.setUTCHours(parts.hours) } if (parts.minutes !== undefined) { date.setUTCMinutes(parts.minutes) } if (parts.seconds !== undefined) { date.setUTCSeconds(parts.seconds) } if (parts.millis !== undefined) { date.setUTCMilliseconds(parts.millis) } } /** @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 makeZonedFromAdjusted = (adjustedMillis: number, zone: DateTime.TimeZone): DateTime.Zoned => { const offset = zone._tag === "Offset" ? zone.offset : calculateNamedOffset(adjustedMillis, zone) return makeZonedProto(adjustedMillis - offset, zone) } const offsetRegex = /([+-])(\d{2}):(\d{2})$/ const parseOffset = (offset: string): number | null => { const match = offsetRegex.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 = (adjustedMillis: number, zone: DateTime.TimeZone.Named): number => { const offset = zone.format.formatToParts(adjustedMillis).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): <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 => { 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) }) /** @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.DurationInput): <A extends DateTime.DateTime>(self: A) => A <A extends DateTime.DateTime>(self: A, duration: Duration.DurationInput): A } = dual( 2, (self: DateTime.DateTime, duration: Duration.DurationInput): DateTime.DateTime => mapEpochMillis(self, (millis) => millis + Duration.toMillis(duration)) ) /** @internal */ export const subtractDuration: { (duration: Duration.DurationInput): <A extends DateTime.DateTime>(self: A) => A <A extends DateTime.DateTime>(self: A, duration: Duration.DurationInput): A } = dual( 2, (self: DateTime.DateTime, duration: Duration.DurationInput): DateTime.DateTime => mapEpochMillis(self, (millis) => millis - Duration.toMillis(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.millis) { addMillis(date, parts.millis) } 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 === "milli") 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.epochMillis) } 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.epochMillis)) /** @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.epochMillis)) /** @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.epochMillis)) /** @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}]`

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ssv445/lorem-ipsum-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server