UNPKG

@thisisagile/easy

Version:

Straightforward library for building domain-driven microservice architectures

163 lines (129 loc) 5.05 kB
import { DateTime as LuxonDateTime, DateTimeUnit as LuxonDateTimeUnit, DurationUnit as LuxonDurationUnit, Settings } from 'luxon'; import { Value } from '../types/Value'; import { Optional } from '../types/Types'; import { choose } from '../types/Case'; import { isDefined, isNumber, isString } from '../types/Is'; import { isDate } from '../types/IsDate'; import { isA } from '../types/IsA'; import { ifDefined } from '../utils/If'; import { JsonValue } from '../types/Json'; import { seconds } from '../utils/Seconds'; import { tryTo } from '../types/Try'; import { use } from '../types/Constructor'; Settings.defaultZone = 'utc'; export type DateTimeUnit = LuxonDateTimeUnit; export type DurationUnit = LuxonDurationUnit; export type Duration = Partial<Record<DurationUnit, number>>; export type DiffOptions = { rounding: 'floor' | 'ceil' | 'round'; }; type DatetimeInput = string | number | Date | DateTime | null; export class DateTime extends Value<Optional<string>> { protected readonly luxon: LuxonDateTime; constructor(value?: DatetimeInput); constructor(value?: string, format?: string); constructor(value?: DatetimeInput, format?: string) { const luxon = choose(value) .type(isString, v => (format ? LuxonDateTime.fromFormat(v, format, { setZone: true }) : LuxonDateTime.fromISO(v, { setZone: true }))) .type(isNumber, v => LuxonDateTime.fromMillis(v)) .type(isDate, v => LuxonDateTime.fromJSDate(v)) .type(isDateTime, v => v.luxon) // Allow constructing with LuxonDateTime without exposing types .else(value instanceof LuxonDateTime ? value : LuxonDateTime.fromISO(undefined as any)); super(luxon.toISO() ?? undefined); this.luxon = luxon; } static get now(): DateTime { return new DateTime(LuxonDateTime.utc() as any); } get isValid(): boolean { return isDefined(this.value) && this.utc.isValid; } /** * @deprecated Deprecated in favor for DateTime.from as that also accepts locales and another DateTime */ get fromNow(): string { return this.from(); } protected get utc(): LuxonDateTime { return this.luxon.setZone('utc'); } from(locale?: string): string; from(date?: DateTime, locale?: string): string; from(dateOrLocale?: string | DateTime, maybeLocale?: string): string { return ( use((isString(dateOrLocale) ? dateOrLocale : maybeLocale) ?? 'en', locale => ifDefined( isA<DateTime>(dateOrLocale) ? dateOrLocale : undefined, d => this.utc.setLocale(locale).toRelative({ base: d.utc }), () => this.utc.setLocale(locale).toRelative() ) ) ?? '' ); } isAfter(dt: DateTime): boolean { return this.utc > dt.utc; } isBefore(dt: DateTime): boolean { return this.utc < dt.utc; } equals(dt: DateTime): boolean { return this.utc.hasSame(dt.utc, 'millisecond'); } add(n: number, unit?: DurationUnit): DateTime; add(duration: Duration): DateTime; add(n: number | Duration, unit: DurationUnit = 'day'): DateTime { return new DateTime(this.luxon.plus(isNumber(n) ? { [unit]: n } : n) as any); } subtract(n: number, unit?: DurationUnit): DateTime; subtract(duration: Duration): DateTime; subtract(n: number | Duration, unit: DurationUnit = 'day'): DateTime { return new DateTime(this.luxon.minus(isNumber(n) ? { [unit]: n } : n) as any); } diff(other: DateTime, unit: DateTimeUnit = 'day', opts?: DiffOptions): number { return Math[opts?.rounding ?? 'floor'](this.utc.diff(other.utc).as(unit)); } startOf(unit: DateTimeUnit = 'day'): DateTime { return new DateTime(this.luxon.startOf(unit) as any); } endOf(unit: DateTimeUnit = 'day'): DateTime { return new DateTime(this.luxon.endOf(unit) as any); } isWeekend(): boolean { return this.luxon.isWeekend; } withZone(zone: string): DateTime { return new DateTime(this.luxon.setZone(zone) as any); } toString(): string { return this.value ?? ''; } toJSON(): JsonValue { return this.utc.toISO(); } toFormat(format: string): string { return this.luxon.toFormat(format); } toLocale(locale = 'nl-NL', format = 'D'): string { return this.luxon.setLocale(locale).toFormat(format); } toFull(locale?: string): string { return this.toLocale(locale, 'DDD'); } toDate(): Optional<Date> { return this.isValid ? this.utc.toJSDate() : undefined; } toEpoch(): number { return this.luxon.toMillis(); } ago(end: DateTime = DateTime.now): string { return seconds.toText(end.diff(this, 'second')); } withClock(clock: DateTime): DateTime { return tryTo(() => [this.toDate() as Date, clock.toDate() as Date]) .map(([td, cd]) => new Date(td.getFullYear(), td.getMonth(), td.getDate(), cd.getHours(), cd.getMinutes(), cd.getSeconds())) .map(d => new DateTime(d)).value; } } export const isDateTime = (dt?: unknown): dt is DateTime => isDefined(dt) && dt instanceof DateTime; export const dt = (dt?: DatetimeInput): DateTime => new DateTime(dt);