UNPKG

14.8 kBTypeScriptView Raw
1import { CanBeInvalid, DefaultValidity, IfValid, Invalid, Valid } from "./_util";
2import {
3 _UseLocaleWeekOption,
4 DateObjectUnits,
5 DateTime,
6 DateTimeOptions,
7 DiffOptions,
8 LocaleOptions,
9 ToISOTimeOptions,
10} from "./datetime";
11import { Duration, DurationLike, DurationMaybeValid, DurationUnit } from "./duration";
12
13export interface IntervalObject {
14 start?: DateTime | undefined;
15 end?: DateTime | undefined;
16}
17
18export type DateInput = DateTime | DateObjectUnits | Date;
19
20export type IntervalMaybeValid = CanBeInvalid extends true ? (Interval<Valid> | Interval<Invalid>) : Interval;
21
22export type CountOptions = _UseLocaleWeekOption;
23
24/**
25 * An Interval object represents a half-open interval of time, where each endpoint is a {@link DateTime}.
26 * Conceptually, it is a container for those two endpoints, accompanied by methods for
27 * creating, parsing, interrogating, comparing, transforming, and formatting them.
28 *
29 * Here is a brief overview of the most commonly used methods and getters in Interval:
30 *
31 * * **Creation** To create an Interval, use {@link Interval.fromDateTimes}, {@link Interval.after}, {@link Interval.before}, or {@link Interval.fromISO}.
32 * * **Accessors** Use {@link Interval#start} and {@link Interval#end} to get the start and end.
33 * * **Interrogation** To analyze the Interval, use {@link Interval#count}, {@link Interval#length}, {@link Interval#hasSame},
34 * * {@link Interval#contains}, {@link Interval#isAfter}, or {@link Interval#isBefore}.
35 * * **Transformation** To create other Intervals out of this one, use {@link Interval#set}, {@link Interval#splitAt}, {@link Interval#splitBy}, {@link Interval#divideEqually},
36 * * {@link Interval.merge}, {@link Interval.xor}, {@link Interval#union}, {@link Interval#intersection}, or {@link Interval#difference}.
37 * * **Comparison** To compare this Interval to another one, use {@link Interval#equals}, {@link Interval#overlaps}, {@link Interval#abutsStart}, {@link Interval#abutsEnd}, {@link Interval#engulfs}
38 * * **Output** To convert the Interval into other representations, see {@link Interval#toString}, {@link Interval#toLocaleString}, {@link Interval#toISO}, {@link Interval#toISODate},
39 * * {@link Interval#toISOTime}, {@link Interval#toFormat}, and {@link Interval#toDuration}.
40 */
41export class Interval<IsValid extends boolean = DefaultValidity> {
42 /**
43 * Create an invalid Interval.
44 *
45 * @param reason - simple string of why this Interval is invalid. Should not contain parameters or anything else data-dependent
46 * @param explanation - longer explanation, may include parameters and other useful debugging information.
47 */
48 static invalid(reason: string, explanation?: string): Interval<Invalid>;
49
50 /**
51 * Create an Interval from a start DateTime and an end DateTime. Inclusive of the start but not the end.
52 *
53 * @param start
54 * @param end
55 */
56 static fromDateTimes(start: DateInput, end: DateInput): IntervalMaybeValid;
57
58 /**
59 * Create an Interval from a start DateTime and a Duration to extend to.
60 *
61 * @param start
62 * @param duration - the length of the Interval.
63 */
64 static after(start: DateInput, duration: DurationLike): IntervalMaybeValid;
65
66 /**
67 * Create an Interval from an end DateTime and a Duration to extend backwards to.
68 *
69 * @param end
70 * @param duration - the length of the Interval.
71 */
72 static before(end: DateInput, duration: DurationLike): IntervalMaybeValid;
73
74 /**
75 * Create an Interval from an ISO 8601 string.
76 * Accepts `<start>/<end>`, `<start>/<duration>`, and `<duration>/<end>` formats.
77 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
78 *
79 * @param text - the ISO string to parse
80 * @param opts - options to pass {@link DateTime.fromISO} and optionally {@link Duration.fromISO}
81 */
82 static fromISO(text: string, opts?: DateTimeOptions): IntervalMaybeValid;
83
84 /**
85 * Check if an object is an Interval. Works across context boundaries
86 *
87 * @param o
88 */
89 static isInterval(o: unknown): o is IntervalMaybeValid;
90
91 private constructor(config: unknown);
92
93 /**
94 * Returns the start of the Interval
95 */
96 get start(): IfValid<DateTime<Valid>, null, IsValid>;
97
98 /**
99 * Returns the end of the Interval
100 */
101 get end(): IfValid<DateTime<Valid>, null, IsValid>;
102
103 /**
104 * Returns whether this Interval's end is at least its start, meaning that the Interval isn't 'backwards'.
105 */
106 get isValid(): IfValid<true, false, IsValid>;
107
108 /**
109 * Returns an error code if this Interval is invalid, or null if the Interval is valid
110 */
111 get invalidReason(): IfValid<null, string, IsValid>;
112
113 /**
114 * Returns an explanation of why this Interval became invalid, or null if the Interval is valid
115 */
116 get invalidExplanation(): IfValid<null, string | null, IsValid>;
117
118 /**
119 * Returns the length of the Interval in the specified unit.
120 *
121 * @param unit - the unit (such as 'hours' or 'days') to return the length in.
122 */
123 length(unit?: DurationUnit): IfValid<number, typeof NaN, IsValid>;
124
125 /**
126 * Returns the count of minutes, hours, days, months, or years included in the Interval, even in part.
127 * Unlike {@link Interval#length} this counts sections of the calendar, not periods of time, e.g. specifying 'day'
128 * asks 'what dates are included in this interval?', not 'how many days long is this interval?'
129 *
130 * @param unit - the unit of time to count. Defaults to 'milliseconds'.
131 */
132 count(unit?: DurationUnit, opts?: CountOptions): IfValid<number, typeof NaN, IsValid>;
133
134 /**
135 * Returns whether this Interval's start and end are both in the same unit of time
136 *
137 * @param unit - the unit of time to check sameness on
138 */
139 hasSame(unit: DurationUnit): IfValid<boolean, false, IsValid>;
140
141 /**
142 * Return whether this Interval has the same start and end DateTimes.
143 */
144 isEmpty(): boolean;
145
146 /**
147 * Return whether this Interval's start is after the specified DateTime.
148 *
149 * @param dateTime
150 */
151 isAfter(dateTime: DateTime): IfValid<boolean, false, IsValid>;
152
153 /**
154 * Return whether this Interval's end is before the specified DateTime.
155 *
156 * @param dateTime
157 */
158 isBefore(dateTime: DateTime): IfValid<boolean, false, IsValid>;
159
160 /**
161 * Return whether this Interval contains the specified DateTime.
162 *
163 * @param dateTime
164 */
165 contains(dateTime: DateTime): IfValid<boolean, false, IsValid>;
166
167 /**
168 * "Sets" the start and/or end dates. Returns a newly-constructed Interval.
169 *
170 * @param values - the values to set
171 * @param values.start - the starting DateTime
172 * @param values.end - the ending DateTime
173 */
174 set(values?: IntervalObject): IntervalMaybeValid;
175
176 /**
177 * Split this Interval at each of the specified DateTimes
178 *
179 * @param dateTimes - the unit of time to count.
180 */
181 splitAt(...dateTimes: DateTime[]): IfValid<Interval[], [], IsValid>;
182
183 /**
184 * Split this Interval into smaller Intervals, each of the specified length.
185 * Left over time is grouped into a smaller interval
186 *
187 * @param duration - The length of each resulting interval.
188 */
189 splitBy(duration: DurationLike): IfValid<Interval[], [], IsValid>;
190
191 /**
192 * Split this Interval into the specified number of smaller intervals.
193 *
194 * @param numberOfParts - The number of Intervals to divide the Interval into.
195 */
196 divideEqually(numberOfParts: number): IfValid<Interval[], [], IsValid>;
197
198 /**
199 * Return whether this Interval overlaps with the specified Interval
200 */
201 overlaps(other: Interval): boolean;
202
203 /**
204 * Return whether this Interval's end is adjacent to the specified Interval's start.
205 */
206 abutsStart(other: Interval): IfValid<boolean, false, IsValid>;
207
208 /**
209 * Return whether this Interval's start is adjacent to the specified Interval's end.
210 */
211 abutsEnd(other: Interval): IfValid<boolean, false, IsValid>;
212
213 /**
214 * Return whether this Interval engulfs the start and end of the specified Interval.
215 */
216 engulfs(other: Interval): IfValid<boolean, false, IsValid>;
217
218 /**
219 * Return whether this Interval has the same start and end as the specified Interval.
220 */
221 equals(other: Interval): IfValid<boolean, false, IsValid>;
222
223 /**
224 * Return an Interval representing the intersection of this Interval and the specified Interval.
225 * Specifically, the resulting Interval has the maximum start time and the minimum end time of the two Intervals.
226 * Returns null if the intersection is empty, meaning the intervals do not intersect.
227 */
228 intersection(other: Interval): Interval | null;
229
230 /**
231 * Return an Interval representing the union of this Interval and the specified Interval.
232 * Specifically, the resulting Interval has the minimum start time and the maximum end time of the two Intervals.
233 */
234 union(other: Interval): IntervalMaybeValid;
235
236 /**
237 * Merge an array of Intervals into an equivalent minimal set of Intervals.
238 * Combines overlapping and adjacent Intervals.
239 */
240 static merge(intervals: Interval[]): IntervalMaybeValid[];
241
242 /**
243 * Return an array of Intervals representing the spans of time that only appear in one of the specified Intervals.
244 */
245 static xor(intervals: Interval[]): IntervalMaybeValid[];
246
247 /**
248 * Return Intervals representing the spans of time in this Interval that not overlap with any of the specified Intervals.
249 */
250 difference(...intervals: Interval[]): IntervalMaybeValid[];
251
252 /**
253 * Returns a string representation of this Interval appropriate for debugging.
254 */
255 toString(): IfValid<string, "Invalid Interval", IsValid>;
256
257 /**
258 * Returns a localized string representing this Interval. Accepts the same options as the
259 * Intl.DateTimeFormat constructor and any presets defined by Luxon, such as
260 * {@link DateTime.DATE_FULL} or {@link DateTime.TIME_SIMPLE}. The exact behavior of this method
261 * is browser-specific, but in general it will return an appropriate representation of the
262 * Interval in the assigned locale. Defaults to the system's locale if no locale has been
263 * specified.
264 * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat
265 * @param formatOpts - Either a DateTime preset or Intl.DateTimeFormat constructor options. Defaults to DateTime.DATE_SHORT
266 * @param opts - Options to override the configuration of the start DateTime.
267 *
268 * @example
269 * Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(); //=> 11/7/2022 – 11/8/2022
270 * @example
271 * Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL); //=> November 7 – 8, 2022
272 * @example
273 * Interval.fromISO('2022-11-07T09:00Z/2022-11-08T09:00Z').toLocaleString(DateTime.DATE_FULL, { locale: 'fr-FR' }); //=> 7–8 novembre 2022
274 * @example
275 * Interval.fromISO('2022-11-07T17:00Z/2022-11-07T19:00Z').toLocaleString(DateTime.TIME_SIMPLE); //=> 6:00 – 8:00 PM
276 * @example
277 * Interval.fromISO("2022-11-07T17:00Z/2022-11-07T19:00Z").toLocaleString({
278 * weekday: "short",
279 * month: "short",
280 * day: "2-digit",
281 * hour: "2-digit",
282 * minute: "2-digit",
283 * }); //=> Mon, Nov 07, 6:00 – 8:00 p
284 */
285 toLocaleString(
286 formatOpts?: Intl.DateTimeFormatOptions,
287 opts?: LocaleOptions,
288 ): IfValid<string, "Invalid Interval", IsValid>;
289
290 /**
291 * Returns an ISO 8601-compliant string representation of this Interval.
292 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
293 *
294 * @param opts - The same options as {@link DateTime#toISO}
295 */
296 toISO(opts?: ToISOTimeOptions): IfValid<string, "Invalid Interval", IsValid>;
297
298 /**
299 * Returns an ISO 8601-compliant string representation of the dates in this Interval.
300 * The time components are ignored.
301 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
302 */
303 toISODate(): IfValid<string, "Invalid Interval", IsValid>;
304
305 /**
306 * Returns an ISO 8601-compliant string representation of the times in this Interval.
307 * The date components are ignored.
308 * @see https://en.wikipedia.org/wiki/ISO_8601#Time_intervals
309 *
310 * @param opts - The same options as {@link DateTime.toISO}
311 */
312 toISOTime(opts?: ToISOTimeOptions): IfValid<string, "Invalid Interval", IsValid>;
313
314 /**
315 * Returns a string representation of this Interval formatted according to the specified format string.
316 *
317 * @param dateFormat - the format string. This string formats the start and end time. See {@link DateTime.toFormat} for details.
318 * @param opts - options
319 * @param opts.separator - a separator to place between the start and end representations. Defaults to ' - '.
320 */
321 toFormat(
322 dateFormat: string,
323 opts?: {
324 separator?: string | undefined;
325 },
326 ): IfValid<string, "Invalid Interval", IsValid>;
327
328 /**
329 * Return a Duration representing the time spanned by this interval.
330 *
331 * @param unit - the unit or units (such as 'hours' or 'days') to include in the duration. Defaults to ['milliseconds'].
332 * @param opts - options that affect the creation of the Duration
333 * @param opts.conversionAccuracy - the conversion system to use. Defaults to 'casual'.
334 *
335 * @example
336 * Interval.fromDateTimes(dt1, dt2).toDuration().toObject() //=> { milliseconds: 88489257 }
337 * @example
338 * Interval.fromDateTimes(dt1, dt2).toDuration('days').toObject() //=> { days: 1.0241812152777778 }
339 * @example
340 * Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes']).toObject() //=> { hours: 24, minutes: 34.82095 }
341 * @example
342 * Interval.fromDateTimes(dt1, dt2).toDuration(['hours', 'minutes', 'seconds']).toObject() //=> { hours: 24, minutes: 34, seconds: 49.257 }
343 * @example
344 * Interval.fromDateTimes(dt1, dt2).toDuration('seconds').toObject() //=> { seconds: 88489.257 }
345 */
346 toDuration(unit?: DurationUnit | DurationUnit[], opts?: DiffOptions): DurationMaybeValid;
347
348 /**
349 * Run mapFn on the interval start and end, returning a new Interval from the resulting DateTimes
350 *
351 * @example
352 * Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.toUTC())
353 * @example
354 * Interval.fromDateTimes(dt1, dt2).mapEndpoints(endpoint => endpoint.plus({ hours: 2 }))
355 */
356 mapEndpoints(mapFn: (d: DateTime) => DateTime): IntervalMaybeValid;
357}