diff --git a/.gitignore b/.gitignore index becb51289..61034c7eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,10 @@ -node_modules -.tern-port -build +node_modules/ +build/ +transpiled/ #* .#* -coverage/ +.tern-port .DS_Store .external-ecmascript.js .idea -.vscode \ No newline at end of file +.vscode diff --git a/package-lock.json b/package-lock.json index c7437f2e0..18dac2707 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4536,16 +4536,22 @@ } }, "fs-extra": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", - "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, "requires": { - "graceful-fs": "^4.1.2", + "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" }, "dependencies": { + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", @@ -10001,9 +10007,9 @@ "dev": true }, "typescript": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", - "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.2.tgz", + "integrity": "sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==", "dev": true }, "uglify-js": { diff --git a/package.json b/package.json index e11d7090f..3e1d5f67c 100644 --- a/package.json +++ b/package.json @@ -41,9 +41,9 @@ "@babel/plugin-external-helpers": "latest", "@babel/plugin-proposal-optional-chaining": "latest", "@babel/preset-env": "latest", - "@types/jest": "^24.0.18", - "@typescript-eslint/eslint-plugin": "^2.1.0", - "@typescript-eslint/parser": "^2.1.0", + "@types/jest": "latest", + "@typescript-eslint/eslint-plugin": "latest", + "@typescript-eslint/parser": "latest", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "11.0.0-beta.0", "babel-jest": "latest", @@ -63,22 +63,22 @@ "eslint-plugin-promise": "latest", "eslint-plugin-react": "latest", "eslint-plugin-standard": "latest", - "fs-extra": "^6.0.1", + "fs-extra": "latest", "full-icu": "latest", "husky": "latest", "jest": "latest", "lint-staged": "latest", "prettier": "1.18.2", - "rimraf": "^3.0.0", + "rimraf": "latest", "rollup": "latest", "rollup-plugin-babel": "latest", "rollup-plugin-babel-minify": "latest", "rollup-plugin-commonjs": "latest", "rollup-plugin-node-resolve": "latest", "rollup-plugin-typescript": "latest", - "ts-jest": "^24.0.2", + "ts-jest": "latest", "tslib": "latest", - "typescript": "^3.5.3", + "typescript": "latest", "uglify-js": "latest" }, "main": "build/node/luxon.js", diff --git a/src/datetime.ts b/src/datetime.ts index 81cf1403b..34bd2563c 100644 --- a/src/datetime.ts +++ b/src/datetime.ts @@ -65,7 +65,7 @@ import { DateTimeWithZoneOptions } from "./types/datetime"; import { DurationUnit } from "./types/duration"; -import { LocaleOptions } from "./types/locale"; +import { LocaleOptions, NumberingSystem, CalendarSystem } from "./types/locale"; import { ThrowOnInvalid } from "./types/common"; const MAX_DATE = 8.64e15; @@ -1142,11 +1142,13 @@ export default class DateTime { * @return {Object} */ resolvedLocaleOpts(options: LocaleOptions & DateTimeFormatOptions = {}) { - const { locale, numberingSystem, calendar } = Formatter.create( + const { locale, numberingSystem: ns, calendar } = Formatter.create( this.loc.clone(options), options ).resolvedOptions(this); - return { locale, numberingSystem, outputCalendar: calendar }; + const numberingSystem = ns as NumberingSystem; + const outputCalendar = calendar as CalendarSystem; + return { locale, numberingSystem, outputCalendar }; } // TRANSFORM @@ -1214,7 +1216,7 @@ export default class DateTime { /** * "Set" the locale, numberingSystem, or outputCalendar. Returns a newly-constructed DateTime. - * @param {Object} options - the options to set + * @param {Object} [options] - the options to set * @param {string} [options.locale] - ; * @param {CalendarSystem} [options.outputCalendar] - ; * @param {NumberingSystem} [options.numberingSystem] - ; @@ -1618,7 +1620,7 @@ export default class DateTime { /** * Return the difference between two DateTimes as a Duration. - * @param {DateTime} otherDateTime - the DateTime to compare this one to + * @param {DateTime} other - the DateTime to compare this one to * @param {string|string[]} [unit=['milliseconds']] - the unit or array of units (such as 'hours' or 'days') to include in the duration. * @param {Object} options - options that affect the creation of the Duration * @param {string} [options.conversionAccuracy='casual'] - the conversion system to use @@ -1632,7 +1634,7 @@ export default class DateTime { * @return {Duration} */ diff( - otherDateTime: DateTime, + other: DateTime, unit: DurationUnit | DurationUnit[] = "milliseconds", options: DiffOptions = {} ) { @@ -1646,9 +1648,9 @@ export default class DateTime { // GILLES added this test, for an invariant used in diff() if (units.length === 0) throw new InvalidArgumentError("At least one unit must be specified"); - const otherIsLater = otherDateTime.valueOf() > this.valueOf(), - earlier = otherIsLater ? this : otherDateTime, - later = otherIsLater ? otherDateTime : this, + const otherIsLater = other.valueOf() > this.valueOf(), + earlier = otherIsLater ? this : other, + later = otherIsLater ? other : this, diffed = diff(earlier, later, units, durOpts); return otherIsLater ? diffed.negate() : diffed; @@ -1669,26 +1671,26 @@ export default class DateTime { /** * Return an Interval spanning between this DateTime and another DateTime - * @param {DateTime} otherDateTime - the other end point of the Interval + * @param {DateTime} other - the other end point of the Interval * @return {Interval} */ - until(otherDateTime: DateTime) { - return Interval.fromDateTimes(this, otherDateTime); + until(other: DateTime) { + return Interval.fromDateTimes(this, other); } /** * Return whether this DateTime is in the same unit of time as another DateTime - * @param {DateTime} otherDateTime - the other DateTime + * @param {DateTime} other - the other DateTime * @param {string} unit - the unit of time to check sameness on * @example DateTime.local().hasSame(otherDT, 'day'); //~> true if both have the same calendar day * @return {boolean} */ - hasSame(otherDateTime: DateTime, unit: DurationUnit) { + hasSame(other: DateTime, unit: DurationUnit) { if (Duration.normalizeUnit(unit) === "milliseconds") { // GILLES added normalizeUnit - return this.valueOf() === otherDateTime.valueOf(); + return this.valueOf() === other.valueOf(); } else { - const inputMs = otherDateTime.valueOf(); + const inputMs = other.valueOf(); return this.startOf(unit).valueOf() <= inputMs && inputMs <= this.endOf(unit).valueOf(); } } diff --git a/src/duration.ts b/src/duration.ts index 87192a096..371d56ba2 100644 --- a/src/duration.ts +++ b/src/duration.ts @@ -196,7 +196,7 @@ export default class Duration { /** * @private */ - constructor(config: Config) { + private constructor(config: Config) { const accurate = config.conversionAccuracy === "longterm" || false; /** * @access private diff --git a/src/impl/util.ts b/src/impl/util.ts index 26f533f8a..3a1b4044e 100644 --- a/src/impl/util.ts +++ b/src/impl/util.ts @@ -186,7 +186,12 @@ export function untruncateYear(year: number) { // PARSING -export function parseZoneInfo(ts: number, offsetFormat: string, locale: string, timeZone?: string) { +export function parseZoneInfo( + ts: number, + offsetFormat?: string, + locale?: string, + timeZone?: string +) { const date = new Date(ts), intlOpts = { hour12: false, diff --git a/src/interval.ts b/src/interval.ts index 51299ec52..7216b0f55 100644 --- a/src/interval.ts +++ b/src/interval.ts @@ -33,7 +33,7 @@ interface Config { * * **Interrogation** To analyze the Interval, use {@link count}, {@link length}, {@link hasSame}, {@link contains}, {@link isAfter}, or {@link isBefore}. * * **Transformation** To create other Intervals out of this one, use {@link set}, {@link splitAt}, {@link splitBy}, {@link divideEqually}, {@link merge}, {@link xor}, {@link union}, {@link intersection}, or {@link difference}. * * **Comparison** To compare this Interval to another one, use {@link equals}, {@link overlaps}, {@link abutsStart}, {@link abutsEnd}, {@link engulfs} - * * **Output*** To convert the Interval into other representations, see {@link toString}, {@link toISO}, {@link toFormat}, and {@link toDuration}. + * * **Output** To convert the Interval into other representations, see {@link toString}, {@link toISO}, {@link toFormat}, and {@link toDuration}. */ export default class Interval { // Private readonly fields @@ -44,7 +44,7 @@ export default class Interval { /** * @private */ - constructor(config: Config) { + private constructor(config: Config) { validateStartEnd(config.start, config.end); /** @@ -242,7 +242,7 @@ export default class Interval { * @param {DateTime} values.end - the ending DateTime * @return {Interval} */ - set({ start, end }: IntervalObject = {}) { + set({ start, end }: IntervalObject) { return Interval.fromDateTimes(start || this.s, end || this.e); } diff --git a/src/luxon.d.ts b/src/luxon.d.ts new file mode 100644 index 000000000..9e82ef801 --- /dev/null +++ b/src/luxon.d.ts @@ -0,0 +1,507 @@ +// Type definitions for luxon 2.0 +// TypeScript Version: 3.6 + +export type DateTimeFormatOptions = Intl.DateTimeFormatOptions; + +export interface ZoneOptions { + keepLocalTime?: boolean; +} + +export type ToRelativeUnit = Exclude; +export interface ToRelativeOptions { + /** The DateTime to use as the basis to which this time is compared. Defaults to now. */ + base?: DateTime; + locale?: string; + style?: StringUnitLength; + /** If omitted, the method will pick the unit. */ + unit?: ToRelativeUnit; + /** Defaults to `true`. */ + round?: boolean; + /** + * Padding in milliseconds. This allows you to round up the result if it fits inside the threshold. + * Don't use in combination with {round: false} because the decimal output will include the padding. + * Defaults to 0. + */ + padding?: number; + /** The Intl system may choose not to honor this */ + numberingSystem?: NumberingSystem; +} + +export type ToRelativeNumeric = "auto" | "always"; +export type ToRelativeCalendarUnit = "years" | "quarters" | "months" | "weeks" | "days"; +export interface ToRelativeCalendarOptions { + /** The DateTime to use as the basis to which this time is compared. Defaults to now. */ + base?: DateTime; + locale?: string; + /** If omitted, the method will pick the unit. */ + unit?: ToRelativeCalendarUnit; + /** The Intl system may choose not to honor this. */ + numberingSystem?: NumberingSystem; +} + +export interface ToSQLOptions { + includeOffset?: boolean; + includeZone?: boolean; +} + +export interface ToISOTimeOptions { + suppressMilliseconds?: boolean; + suppressSeconds?: boolean; + includeOffset?: boolean; +} + +export interface LocaleOptions { + locale?: string; + outputCalendar?: CalendarSystem; + numberingSystem?: NumberingSystem; +} + +export interface DateTimeOptions extends LocaleOptions { + zone?: string | Zone; + nullOnInvalid?: boolean; +} +export interface DateTimeWithZoneOptions extends DateTimeOptions { + setZone?: boolean; +} +export interface TimeObject { + hour: number; + minute: number; + second: number; + millisecond: number; +} +export interface GregorianDateTime extends TimeObject { + year: number; + month: number; + day: number; +} +export interface WeekDateTime extends TimeObject { + weekYear: number; + weekNumber: number; + weekday: number; +} + +export interface OrdinalDateTime extends TimeObject { + year: number; + ordinal: number; +} + +export type GenericDateTime = Partial; + +export interface DiffOptions { + conversionAccuracy?: ConversionAccuracy; +} + +export interface ExplainedFormat { + input: string; + tokens: { + literal: boolean; + val: string; + }[]; + regex?: RegExp; + rawMatches?: RegExpMatchArray | null; + matches?: Record; + result?: { + [k: string]: any; + } | null; + zone?: Zone | null; + invalidReason?: string; +} + +export interface ThrowOnInvalid { + nullOnInvalid?: false; +} + +export class DateTime { + static readonly DATETIME_FULL: DateTimeFormatOptions; + static readonly DATETIME_FULL_WITH_SECONDS: DateTimeFormatOptions; + static readonly DATETIME_HUGE: DateTimeFormatOptions; + static readonly DATETIME_HUGE_WITH_SECONDS: DateTimeFormatOptions; + static readonly DATETIME_MED: DateTimeFormatOptions; + static readonly DATETIME_MED_WITH_SECONDS: DateTimeFormatOptions; + static readonly DATETIME_SHORT: DateTimeFormatOptions; + static readonly DATETIME_SHORT_WITH_SECONDS: DateTimeFormatOptions; + static readonly DATE_FULL: DateTimeFormatOptions; + static readonly DATE_HUGE: DateTimeFormatOptions; + static readonly DATE_MED: DateTimeFormatOptions; + static readonly DATETIME_MED_WITH_WEEKDAY: DateTimeFormatOptions; + static readonly DATE_SHORT: DateTimeFormatOptions; + static readonly TIME_24_SIMPLE: DateTimeFormatOptions; + static readonly TIME_24_WITH_LONG_OFFSET: DateTimeFormatOptions; + static readonly TIME_24_WITH_SECONDS: DateTimeFormatOptions; + static readonly TIME_24_WITH_SHORT_OFFSET: DateTimeFormatOptions; + static readonly TIME_SIMPLE: DateTimeFormatOptions; + static readonly TIME_WITH_LONG_OFFSET: DateTimeFormatOptions; + static readonly TIME_WITH_SECONDS: DateTimeFormatOptions; + static readonly TIME_WITH_SHORT_OFFSET: DateTimeFormatOptions; + static fromHTTP(text: string, options: DateTimeWithZoneOptions & ThrowOnInvalid): DateTime; + static fromHTTP(text: string, options: DateTimeWithZoneOptions): DateTime | null; + static fromHTTP(text: string): DateTime; + static fromISO(text: string, options: DateTimeWithZoneOptions & ThrowOnInvalid): DateTime; + static fromISO(text: string, options: DateTimeWithZoneOptions): DateTime | null; + static fromISO(text: string): DateTime; + static fromJSDate(date: Date, options: DateTimeOptions & ThrowOnInvalid): DateTime; + static fromJSDate(date: Date, options: DateTimeOptions): DateTime | null; + static fromJSDate(date: Date): DateTime; + static fromMillis(milliseconds: number, options: DateTimeOptions & ThrowOnInvalid): DateTime; + static fromMillis(milliseconds: number, options: DateTimeOptions): DateTime | null; + static fromMillis(milliseconds: number): DateTime; + static fromObject(object: GenericDateTime, options: DateTimeOptions & ThrowOnInvalid): DateTime; + static fromObject(object: GenericDateTime, options: DateTimeOptions): DateTime | null; + static fromObject(object: GenericDateTime): DateTime; + static fromRFC2822(text: string, options: DateTimeWithZoneOptions & ThrowOnInvalid): DateTime; + static fromRFC2822(text: string, options: DateTimeWithZoneOptions): DateTime | null; + static fromRFC2822(text: string): DateTime; + static fromSeconds(seconds: number, options: DateTimeOptions & ThrowOnInvalid): DateTime; + static fromSeconds(seconds: number, options: DateTimeOptions): DateTime | null; + static fromSeconds(seconds: number): DateTime; + static fromSQL(text: string, options: DateTimeWithZoneOptions & ThrowOnInvalid): DateTime; + static fromSQL(text: string, options: DateTimeWithZoneOptions): DateTime | null; + static fromSQL(text: string): DateTime; + static fromFormat( + text: string, + format: string, + options: DateTimeWithZoneOptions + ): DateTime | null; + static fromFormat(text: string, format: string): DateTime; + static fromFormat( + text: string, + format: string, + options: DateTimeWithZoneOptions & ThrowOnInvalid + ): DateTime; + static fromFormatExplain( + text: string, + format: string, + options?: DateTimeOptions + ): ExplainedFormat; + static isDateTime(o: unknown): o is DateTime; + static local(...args: (number | (DateTimeOptions & ThrowOnInvalid))[]): DateTime; + static local(...args: (number | DateTimeOptions)[]): DateTime | null; + static local(...args: number[]): DateTime; + static max(): undefined; + static max(...dateTimes: DateTime[]): DateTime; + static min(): undefined; + static min(...dateTimes: DateTime[]): DateTime; + static utc(...args: (number | (DateTimeOptions & ThrowOnInvalid))[]): DateTime; + static utc(...args: (number | DateTimeOptions)[]): DateTime | null; + static utc(...args: number[]): DateTime; + readonly day: number; + readonly daysInMonth: 28 | 29 | 30 | 31; + readonly daysInYear: 366 | 365; + readonly hour: number; + readonly isInDST: boolean; + readonly isInLeapYear: boolean; + readonly isOffsetFixed: boolean; + readonly locale: string; + readonly millisecond: number; + readonly minute: number; + readonly month: number; + readonly monthLong: string; + readonly monthShort: string; + readonly numberingSystem: NumberingSystem | undefined; + readonly offset: number; + readonly offsetNameLong: string | null; + readonly offsetNameShort: string | null; + readonly ordinal: number; + readonly outputCalendar: CalendarSystem | undefined; + readonly quarter: number; + readonly second: number; + readonly weekday: number; + readonly weekdayLong: string; + readonly weekdayShort: string; + readonly weekNumber: number; + readonly weeksInWeekYear: 53 | 52; + readonly weekYear: number; + readonly year: number; + readonly zone: Readonly; + readonly zoneName: string; + diff(other: DateTime, unit?: DurationUnit | DurationUnit[], options?: DiffOptions): Duration; + diffNow(unit?: DurationUnit | DurationUnit[], options?: DiffOptions): Duration; + endOf(unit: DurationUnit): DateTime; + equals(other: DateTime): boolean; + get(unit: keyof GenericDateTime): number; + hasSame(other: DateTime, unit: DurationUnit): boolean; + minus(duration: DurationLike): DateTime; + plus(duration: DurationLike): DateTime; + reconfigure(options?: LocaleOptions): DateTime; + resolvedLocaleOpts( + options?: LocaleOptions & DateTimeFormatOptions + ): { locale: string; numberingSystem: NumberingSystem; outputCalendar: CalendarSystem }; + set(values: GenericDateTime): DateTime; + setLocale(locale: string): DateTime; + setZone(zone: string | Zone, { keepLocalTime }?: ZoneOptions): DateTime; + startOf(unit: DurationUnit): DateTime; + toBSON(): Date; + toDefaultZone(): DateTime; + toFormat(format: string, options?: LocaleOptions): string; + toHTTP(): string; + toISO(options?: ToISOTimeOptions): string; + toISODate(): string; + toISOTime(options?: ToISOTimeOptions): string; + toISOWeekDate(): string; + toJSDate(): Date; + toJSON(): string; + toLocaleParts(options?: LocaleOptions & DateTimeFormatOptions): Intl.DateTimeFormatPart[]; + toLocaleString(options?: LocaleOptions & DateTimeFormatOptions): string; + toMillis(): number; + toObject(): GregorianDateTime; + toRelative(options?: ToRelativeOptions): string; + toRelativeCalendar(options?: ToRelativeCalendarOptions): string; + toRFC2822(): string; + toSeconds(): number; + toSQL(options?: ToSQLOptions): string; + toSQLDate(): string; + toSQLTime(options?: ToSQLOptions): string; + toString(): string; + toSystemZone(): DateTime; + toUTC(offset?: number, options?: ZoneOptions): DateTime; + until(other: DateTime): Interval; + valueOf(): number; +} + +export type DateTimeLike = DateTime | Date | GenericDateTime; + +export interface DurationOptions { + locale?: string; + numberingSystem?: NumberingSystem; + conversionAccuracy?: ConversionAccuracy; +} + +export interface DurationFromISOOptions extends DurationOptions { + nullOnInvalid?: boolean; +} + +export interface DurationObject { + years?: number; + year?: number; + quarters?: number; + quarter?: number; + months?: number; + month?: number; + weeks?: number; + week?: number; + days?: number; + day?: number; + hours?: number; + hour?: number; + minutes?: number; + minute?: number; + seconds?: number; + second?: number; + milliseconds?: number; + millisecond?: number; +} + +export type DurationUnit = keyof DurationObject; + +export interface DurationToFormatOptions extends DateTimeFormatOptions { + floor?: boolean; + round?: boolean; +} +export class Duration { + static fromISO(text: string): Duration; + static fromISO(text: string, options: DurationFromISOOptions & ThrowOnInvalid): Duration; + static fromISO(text: string, options: DurationFromISOOptions): Duration | null; + static fromMillis(count: number, options?: DurationOptions): Duration; + static fromObject(obj: DurationObject, options?: DurationOptions): Duration; + static isDuration(o: unknown): o is Duration; + readonly days: number; + readonly hours: number; + readonly locale: string; + readonly milliseconds: number; + readonly minutes: number; + readonly months: number; + readonly numberingSystem: NumberingSystem | undefined; + readonly quarters: number; + readonly seconds: number; + readonly weeks: number; + readonly years: number; + as(unit: DurationUnit): number; + equals(other: Duration): boolean; + get(unit: DurationUnit): number; + minus(duration: DurationLike): Duration; + negate(): Duration; + normalize(): Duration; + plus(duration: DurationLike): Duration; + reconfigure(options?: DurationOptions): Duration; + set(values: DurationObject): Duration; + shiftTo(...units: DurationUnit[]): Duration; + toFormat(format: string, options?: DurationToFormatOptions): string; + toISO(): string; + toJSON(): string; + toObject(): DurationObject; + toString(): string; + valueOf(): number; +} +export type DurationLike = Duration | DurationObject | number; + +export type NumberingSystem = + | "arab" + | "arabext" + | "bali" + | "beng" + | "deva" + | "fullwide" + | "gujr" + | "hanidec" + | "khmr" + | "knda" + | "laoo" + | "latn" + | "limb" + | "mlym" + | "mong" + | "mymr" + | "orya" + | "tamldec" + | "telu" + | "thai" + | "tibt"; + +export type CalendarSystem = + | "buddhist" + | "chinese" + | "coptic" + | "ethioaa" + | "ethiopic" + | "gregory" + | "hebrew" + | "indian" + | "islamic" + | "islamicc" + | "iso8601" + | "japanese" + | "persian" + | "roc"; + +export type ConversionAccuracy = "casual" | "longterm"; +export type StringUnitLength = "narrow" | "short" | "long"; +export type NumberUnitLength = "numeric" | "2-digit"; +export type UnitLength = StringUnitLength | NumberUnitLength; + +// Info +export interface InfoOptions { + locale?: string; +} + +export interface InfoUnitOptions extends InfoOptions { + numberingSystem?: NumberingSystem; +} +export interface InfoCalendarOptions extends InfoUnitOptions { + outputCalendar?: CalendarSystem; +} + +export interface Features { + intl: boolean; + intlTokens: boolean; + zones: boolean; + relative: boolean; +} + +export class Info { + static eras(length?: StringUnitLength, { locale }?: InfoOptions): string[]; + static features(): Features; + static hasDST(zone?: ZoneLike): boolean; + static isValidIANAZone(zone: string): boolean; + static meridiems(options?: InfoOptions): string[]; + static months(length?: UnitLength, options?: InfoCalendarOptions): string[]; + static monthsFormat(length?: UnitLength, options?: InfoCalendarOptions): string[]; + static normalizeZone(input?: ZoneLike): Zone; + static weekdays(length?: StringUnitLength, options?: InfoUnitOptions): string[]; + static weekdaysFormat(length?: StringUnitLength, options?: InfoUnitOptions): string[]; +} + +//Interval +export interface IntervalObject { + start?: DateTime; + end?: DateTime; +} + +export class Interval { + static after(start: DateTimeLike, duration: DurationLike): Interval; + static before(end: DateTimeLike, duration: DurationLike): Interval; + static fromDateTimes(start: DateTimeLike, end: DateTimeLike): Interval; + static fromISO(text: string, options?: DateTimeWithZoneOptions): Interval; + static isInterval(o: unknown): o is Interval; + static merge(intervals: Interval[]): Interval[]; + static xor(intervals: Interval[]): Interval[]; + readonly start: DateTime; + readonly end: DateTime; + abutsEnd(other: Interval): boolean; + abutsStart(other: Interval): boolean; + contains(dateTime: DateTime): boolean; + count(unit?: DurationUnit): number; + difference(...intervals: Interval[]): Interval[]; + divideEqually(numberOfParts: number): Interval[]; + engulfs(other: Interval): boolean; + equals(other: Interval): boolean; + hasSame(unit: DurationUnit): boolean; + intersection(other: Interval): Interval | null; + isAfter(dateTime: DateTime): boolean; + isBefore(dateTime: DateTime): boolean; + isEmpty(): boolean; + length(unit?: DurationUnit): number; + mapEndpoints(mapFn: (dt: DateTime) => DateTime): Interval; + overlaps(other: Interval): boolean; + set(interval: IntervalObject): Interval; + splitAt(...dateTimes: (DateTimeLike)[]): Interval[]; + splitBy(duration: DurationLike): Interval[]; + toDuration(unit?: DurationUnit | DurationUnit[], options?: DiffOptions): Duration; + toFormat(dateFormat: string, options?: { separator: string }): string; + toISO(options?: ToISOTimeOptions): string; + toString(): string; + union(other: Interval): Interval; +} + +// Settings +export class Settings { + static defaultLocale: string | undefined; + static defaultNumberingSystem: NumberingSystem | undefined; + static defaultOutputCalendar: CalendarSystem | undefined; + static now: () => number; + static readonly defaultZone: Zone; + static resetCaches(): void; + static setDefaultZone(zone?: ZoneLike): void; +} + +// Zone +export interface ZoneOffsetOptions { + format?: "short" | "long"; + locale?: string; +} + +export type ZoneOffsetFormat = "narrow" | "short" | "techie"; + +export type ZoneLike = Zone | number | string | null | undefined; + +export abstract class Zone { + readonly isValid: boolean; + readonly name: string; + readonly type: string; + readonly universal: boolean; + equals(other: Zone): boolean; + offset(ts: number): number; + offsetName(ts?: number, options?: ZoneOffsetOptions): string | null; + formatOffset(ts: number, format: ZoneOffsetFormat): string; +} + +export class IANAZone extends Zone { + static create(name: string): IANAZone; + static isValidSpecifier(s: string): boolean; + static isValidZone(zone: string): boolean; + static resetCache(): void; +} + +export class FixedOffsetZone extends Zone { + constructor(offset: number); + static readonly utcInstance: FixedOffsetZone; + static instance(offset: number): FixedOffsetZone; + static parseSpecifier(s: string): FixedOffsetZone | null; +} + +export class InvalidZone extends Zone { + constructor(zoneName: string); +} + +export class SystemZone extends Zone { + static readonly instance: SystemZone; +} diff --git a/src/settings.ts b/src/settings.ts index 4d6ddf139..92d377571 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -37,8 +37,13 @@ export default class Settings { /** * Set the default time zone to create DateTimes in. Does not affect existing instances. - * Use the value "system" to reset this value to the system's time zone. - * @type {Zone | string | number | undefined} + * + * Use the value "system" (default) to reset this value to the system's time zone. + * + * zone can be any IANA zone supported by the host environment, or a fixed-offset name of the form 'UTC+3'. + * + * You may also supply an instance of a {@link Zone} class, or a number which will be interpreted as a UTC offset in minutes. + * @param {Zone | string | number} [zone='system'] - the zone value */ static setDefaultZone(zone?: ZoneLike) { // GILLES change API !!! diff --git a/src/types/zone.ts b/src/types/zone.ts index a741cf8ae..80a21e446 100644 --- a/src/types/zone.ts +++ b/src/types/zone.ts @@ -1,8 +1,8 @@ import Zone from "../zone"; export interface ZoneOffsetOptions { - format: "short" | "long"; - locale: string; + format?: "short" | "long"; + locale?: string; } export type ZoneOffsetFormat = "narrow" | "short" | "techie"; diff --git a/src/zone.ts b/src/zone.ts index ff19d97c6..e5bddb073 100644 --- a/src/zone.ts +++ b/src/zone.ts @@ -7,8 +7,8 @@ function silenceUnusedWarning(...args: unknown[]) { } /** - * @interface * An abstract Zone class + * @interface */ export default abstract class Zone { /** @@ -41,13 +41,13 @@ export default abstract class Zone { /** * Returns the offset's common name (such as EST) at the specified timestamp * @abstract - * @param {number} ts - Epoch milliseconds for which to get the name - * @param {Object} options - Options to affect the format - * @param {string} options.format - What style of offset to return. Accepts 'long' or 'short'. - * @param {string} options.locale - What locale to return the offset name in. + * @param {number} [ts] - Epoch milliseconds for which to get the name + * @param {Object} [options] - Options to affect the format + * @param {string} [options.format] - What style of offset to return. Accepts 'long' or 'short'. + * @param {string} [options.locale] - What locale to return the offset name in. * @return {string | null} */ - offsetName(ts: number, options: ZoneOffsetOptions): string | null { + offsetName(ts?: number, options?: ZoneOffsetOptions): string | null { silenceUnusedWarning(ts, options); throw new ZoneIsAbstractError(); } @@ -79,11 +79,11 @@ export default abstract class Zone { /** * Return whether this Zone is equal to another zone * @abstract - * @param {Zone} otherZone - the zone to compare + * @param {Zone} other - the zone to compare * @return {boolean} */ - equals(otherZone: Zone): boolean { - silenceUnusedWarning(otherZone); + equals(other: Zone): boolean { + silenceUnusedWarning(other); throw new ZoneIsAbstractError(); } diff --git a/src/zones/IANAZone.ts b/src/zones/IANAZone.ts index c1bf423d0..d02765bab 100644 --- a/src/zones/IANAZone.ts +++ b/src/zones/IANAZone.ts @@ -134,7 +134,8 @@ export default class IANAZone extends Zone { return null; } - constructor(name: string) { + private constructor(name: string) { + // GILLES made private, use create instead super(); /** @private **/ this.zoneName = name; @@ -158,7 +159,7 @@ export default class IANAZone extends Zone { } /** @override **/ - offsetName(ts: number, { format, locale }: ZoneOffsetOptions) { + offsetName(ts: number, { format, locale }: ZoneOffsetOptions = {}) { return parseZoneInfo(ts, format, locale, this.name); } @@ -181,8 +182,8 @@ export default class IANAZone extends Zone { } /** @override **/ - equals(otherZone: Zone) { - return otherZone.type === "iana" && otherZone.name === this.name; + equals(other: Zone) { + return other.type === "iana" && other.name === this.name; } /** @override **/ diff --git a/src/zones/fixedOffsetZone.ts b/src/zones/fixedOffsetZone.ts index 70c1592cd..c2c181e7b 100644 --- a/src/zones/fixedOffsetZone.ts +++ b/src/zones/fixedOffsetZone.ts @@ -87,8 +87,8 @@ export default class FixedOffsetZone extends Zone { } /** @override **/ - equals(otherZone: Zone): boolean { - return otherZone.type === "fixed" && (otherZone as FixedOffsetZone).fixed === this.fixed; + equals(other: Zone): boolean { + return other.type === "fixed" && (other as FixedOffsetZone).fixed === this.fixed; } /** @override **/ diff --git a/src/zones/systemZone.ts b/src/zones/systemZone.ts index 8852d8853..69aaa93fe 100644 --- a/src/zones/systemZone.ts +++ b/src/zones/systemZone.ts @@ -38,7 +38,7 @@ export default class SystemZone extends Zone { } /** @override **/ - offsetName(ts: number, { format, locale }: ZoneOffsetOptions) { + offsetName(ts: number, { format, locale }: ZoneOffsetOptions = {}) { return parseZoneInfo(ts, format, locale); } @@ -53,8 +53,8 @@ export default class SystemZone extends Zone { } /** @override **/ - equals(otherZone: Zone) { - return otherZone.type === "system"; + equals(other: Zone) { + return other.type === "system"; } /** @override **/ diff --git a/test/helpers.ts b/test/helpers.ts index 278f152c3..3f9fcb5ac 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -13,6 +13,7 @@ const withoutIntl = function(name: string, f: Function) { Settings.resetCaches(); f(); } finally { + Settings.resetCaches(); // @ts-ignore Intl = intl; } @@ -28,6 +29,7 @@ const withoutFTP = function(name: string, f: Function) { Settings.resetCaches(); f(); } finally { + Settings.resetCaches(); Intl.DateTimeFormat.prototype.formatToParts = formatToParts; } }); @@ -43,6 +45,7 @@ const withoutRTF = function(name: string, f: Function) { Settings.resetCaches(); f(); } finally { + Settings.resetCaches(); Intl.RelativeTimeFormat = rtf; } }); @@ -66,6 +69,7 @@ const withoutZones = function(name: string, f: Function) { Settings.resetCaches(); f(); } finally { + Settings.resetCaches(); Intl.DateTimeFormat = DateTimeFormat; } }); diff --git a/test/info/zones.test.ts b/test/info/zones.test.ts index f56b12cc6..3919d5e71 100644 --- a/test/info/zones.test.ts +++ b/test/info/zones.test.ts @@ -75,7 +75,7 @@ test("Info.normalizeZone returns Zone objects unchanged", () => { const fixedOffsetZone = FixedOffsetZone.instance(5); expect(Info.normalizeZone(fixedOffsetZone)).toBe(fixedOffsetZone); - const ianaZone = new IANAZone("Europe/Paris"); + const ianaZone = IANAZone.create("Europe/Paris"); expect(Info.normalizeZone(ianaZone)).toBe(ianaZone); const invalidZone = new InvalidZone("bumblebee"); @@ -91,7 +91,7 @@ test.each<[string | number, Zone]>([ ["UTC", FixedOffsetZone.utcInstance], ["Etc/GMT+5", FixedOffsetZone.instance(-5 * 60)], ["Etc/GMT-10", FixedOffsetZone.instance(+10 * 60)], - ["Europe/Paris", new IANAZone("Europe/Paris")], + ["Europe/Paris", IANAZone.create("Europe/Paris")], [0, FixedOffsetZone.utcInstance], [3, FixedOffsetZone.instance(3)], [-11, FixedOffsetZone.instance(-11)] diff --git a/test/zones/IANA.test.ts b/test/zones/IANA.test.ts index 86343e042..4dfa28538 100644 --- a/test/zones/IANA.test.ts +++ b/test/zones/IANA.test.ts @@ -46,58 +46,58 @@ test("IANAZone.parseGMTOffset returns null for invalid input", () => { }); test("IANAZone.type returns a static string", () => { - expect(new IANAZone("America/Santiago").type).toBe("iana"); - expect(new IANAZone("America/Blorp").type).toBe("iana"); + expect(IANAZone.create("America/Santiago").type).toBe("iana"); + expect(IANAZone.create("America/Blorp").type).toBe("iana"); }); test("IANAZone.name returns the zone name passed to the constructor", () => { - expect(new IANAZone("America/Santiago").name).toBe("America/Santiago"); - expect(new IANAZone("America/Blorp").name).toBe("America/Blorp"); - expect(new IANAZone("foo").name).toBe("foo"); + expect(IANAZone.create("America/Santiago").name).toBe("America/Santiago"); + expect(IANAZone.create("America/Blorp").name).toBe("America/Blorp"); + expect(IANAZone.create("foo").name).toBe("foo"); }); test("IANAZone is not universal", () => { - expect(new IANAZone("America/Santiago").universal).toBe(false); + expect(IANAZone.create("America/Santiago").universal).toBe(false); }); test("IANAZone.offsetName with a long format", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); const offsetName = zone.offsetName(1552089600, { format: "long", locale: "en-US" }); expect(offsetName).toBe("Chile Summer Time"); }); test("IANAZone.offsetName with a short format", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); const offsetName = zone.offsetName(1552089600, { format: "short", locale: "en-US" }); expect(offsetName).toBe("GMT-3"); }); Helpers.withoutIntl("IANAZone.offsetName returns null", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); const offsetName = zone.offsetName(1552089600, { format: "short", locale: "en-US" }); expect(offsetName).toBe(null); }); test("IANAZone.formatOffset with a short format", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); const offsetName = zone.formatOffset(1552089600, "short"); expect(offsetName).toBe("-03:00"); }); test("IANAZone.formatOffset with a narrow format", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); const offsetName = zone.formatOffset(1552089600, "narrow"); expect(offsetName).toBe("-3"); }); test("IANAZone.formatOffset with a techie format", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); const offsetName = zone.formatOffset(1552089600, "techie"); expect(offsetName).toBe("-0300"); }); test("IANAZone.formatOffset throws for an invalid format", () => { - const zone = new IANAZone("America/Santiago"); + const zone = IANAZone.create("America/Santiago"); // @ts-ignore expect(() => zone.formatOffset(1552089600, "blorp")).toThrow(); }); @@ -113,17 +113,17 @@ test("IANAZone.equals returns false even if the two share offsets", () => { }); test("IANAZone.isValid returns true for valid zone names", () => { - expect(new IANAZone("UTC").isValid).toBe(true); - expect(new IANAZone("America/Santiago").isValid).toBe(true); - expect(new IANAZone("Europe/Paris").isValid).toBe(true); + expect(IANAZone.create("UTC").isValid).toBe(true); + expect(IANAZone.create("America/Santiago").isValid).toBe(true); + expect(IANAZone.create("Europe/Paris").isValid).toBe(true); }); test("IANAZone.isValid returns false for invalid zone names", () => { - expect(new IANAZone("").isValid).toBe(false); - expect(new IANAZone("foo").isValid).toBe(false); - expect(new IANAZone("CEDT").isValid).toBe(false); - expect(new IANAZone("GMT+2").isValid).toBe(false); - expect(new IANAZone("America/Blorp").isValid).toBe(false); + expect(IANAZone.create("").isValid).toBe(false); + expect(IANAZone.create("foo").isValid).toBe(false); + expect(IANAZone.create("CEDT").isValid).toBe(false); + expect(IANAZone.create("GMT+2").isValid).toBe(false); + expect(IANAZone.create("America/Blorp").isValid).toBe(false); // @ts-ignore - expect(new IANAZone(null).isValid).toBe(false); + expect(IANAZone.create(null).isValid).toBe(false); });