temporal-workdays 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,320 @@
1
+ /** The parts of a `Temporal.PlainDate` this library relies on. */
2
+ interface PlainDateLike {
3
+ readonly calendarId: string;
4
+ readonly year: number;
5
+ readonly month: number;
6
+ readonly monthCode: string;
7
+ readonly day: number;
8
+ readonly dayOfWeek: number;
9
+ withCalendar(calendar: 'iso8601'): PlainDateLike;
10
+ add(duration: {
11
+ days: number;
12
+ }): PlainDateLike;
13
+ toString(): string;
14
+ }
15
+ /** The parts of a `Temporal.PlainDateTime` / `Temporal.ZonedDateTime` this library relies on. */
16
+ interface DateTimeLike {
17
+ toPlainDate(): PlainDateLike;
18
+ add(duration: {
19
+ days: number;
20
+ }): DateTimeLike;
21
+ }
22
+ /** The parts of a `Temporal.PlainMonthDay` this library relies on. */
23
+ interface PlainMonthDayLike {
24
+ readonly calendarId: string;
25
+ readonly monthCode: string;
26
+ readonly day: number;
27
+ }
28
+ /**
29
+ * Any Temporal value that has a calendar date: `Temporal.PlainDate`, `Temporal.PlainDateTime`
30
+ * or `Temporal.ZonedDateTime`. Every helper returns the same type it was given.
31
+ */
32
+ type DateInput = PlainDateLike | DateTimeLike;
33
+ /** ISO day of week: 1 = Monday … 7 = Sunday (same as `Temporal.PlainDate#dayOfWeek`). */
34
+ type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;
35
+ /**
36
+ * A holiday entry:
37
+ * - `'2026-12-25'` or a `Temporal.PlainDate` — a single date.
38
+ * - `'12-25'` or a `Temporal.PlainMonthDay` — every year on that ISO month/day.
39
+ */
40
+ type HolidayInput = string | PlainDateLike | PlainMonthDayLike;
41
+ /**
42
+ * Decides whether a date is a holiday. Receives an ISO-calendar `Temporal.PlainDate`
43
+ * from the same Temporal implementation as the input date. Not called for weekend days.
44
+ */
45
+ type HolidayPredicate = {
46
+ bivarianceHack(date: PlainDateLike): boolean;
47
+ }['bivarianceHack'];
48
+ /** Which days are workdays. */
49
+ interface WorkdayOptions {
50
+ /** Non-working days of the week. Default `[6, 7]` (Saturday, Sunday). */
51
+ readonly weekend?: readonly DayOfWeek[] | undefined;
52
+ /** Holiday dates, or a function that decides whether a date is a holiday. */
53
+ readonly holidays?: Iterable<HolidayInput> | HolidayPredicate | undefined;
54
+ /**
55
+ * Dates that count as workdays even if they fall on the weekend or a holiday
56
+ * (for example, make-up working Saturdays). Single dates only.
57
+ */
58
+ readonly extraWorkdays?: Iterable<string | PlainDateLike> | undefined;
59
+ }
60
+ /** The parts of a `Temporal.PlainTime` this library relies on. */
61
+ interface PlainTimeLike {
62
+ readonly hour: number;
63
+ readonly minute: number;
64
+ readonly second: number;
65
+ readonly millisecond: number;
66
+ readonly microsecond: number;
67
+ readonly nanosecond: number;
68
+ }
69
+ /** Calendar and clock fields, as accepted by `Temporal.PlainDateTime#with`. */
70
+ interface DateTimeFields {
71
+ year: number;
72
+ month: number;
73
+ day: number;
74
+ hour: number;
75
+ minute: number;
76
+ second: number;
77
+ millisecond: number;
78
+ microsecond: number;
79
+ nanosecond: number;
80
+ }
81
+ /** A duration of clock time. `Temporal.Duration` and plain objects such as `{ hours: 4 }` both fit. */
82
+ interface DurationInput {
83
+ readonly years?: number | undefined;
84
+ readonly months?: number | undefined;
85
+ readonly weeks?: number | undefined;
86
+ readonly days?: number | undefined;
87
+ readonly hours?: number | undefined;
88
+ readonly minutes?: number | undefined;
89
+ readonly seconds?: number | undefined;
90
+ readonly milliseconds?: number | undefined;
91
+ readonly microseconds?: number | undefined;
92
+ readonly nanoseconds?: number | undefined;
93
+ }
94
+ /** The parts of a `Temporal.Duration` this library relies on. */
95
+ interface DurationLike {
96
+ add(other: {
97
+ seconds: number;
98
+ nanoseconds: number;
99
+ }): DurationLike;
100
+ round(options: {
101
+ largestUnit: 'hours';
102
+ }): DurationLike;
103
+ }
104
+ /** The parts of a `Temporal.PlainDateTime` this library relies on. */
105
+ interface PlainDateTimeLike extends PlainTimeLike {
106
+ readonly calendarId: string;
107
+ readonly year: number;
108
+ readonly month: number;
109
+ readonly day: number;
110
+ withCalendar(calendar: string): PlainDateTimeLike;
111
+ with(fields: DateTimeFields): PlainDateTimeLike;
112
+ /** Only ever called as `x.until(x)` to obtain a zero Duration; `never` accepts every implementation's signature. */
113
+ until(other: never): DurationLike;
114
+ toPlainDate(): PlainDateLike;
115
+ add(duration: {
116
+ days: number;
117
+ }): PlainDateTimeLike;
118
+ }
119
+ /** The parts of a `Temporal.ZonedDateTime` this library relies on. */
120
+ interface ZonedDateTimeLike extends PlainTimeLike {
121
+ readonly calendarId: string;
122
+ readonly timeZoneId: string;
123
+ readonly epochNanoseconds: bigint;
124
+ readonly offsetNanoseconds: number;
125
+ readonly year: number;
126
+ readonly month: number;
127
+ readonly day: number;
128
+ withCalendar(calendar: string): ZonedDateTimeLike;
129
+ withTimeZone(timeZone: string): ZonedDateTimeLike;
130
+ with(fields: DateTimeFields, options: {
131
+ disambiguation: 'compatible';
132
+ offset: 'ignore';
133
+ }): ZonedDateTimeLike;
134
+ /** Only ever called as `x.until(x)` to obtain a zero Duration; `never` accepts every implementation's signature. */
135
+ until(other: never): DurationLike;
136
+ toPlainDate(): PlainDateLike;
137
+ add(duration: {
138
+ days?: number;
139
+ seconds?: number;
140
+ nanoseconds?: number;
141
+ }): ZonedDateTimeLike;
142
+ getTimeZoneTransition(direction: 'next' | 'previous'): ZonedDateTimeLike | null;
143
+ }
144
+ /** A date with a time of day: `Temporal.PlainDateTime` or `Temporal.ZonedDateTime`. */
145
+ type DateTimeInput = PlainDateTimeLike | ZonedDateTimeLike;
146
+ /** The `Temporal.Duration` type of the Temporal implementation that `T` comes from. */
147
+ type DurationOf<T> = T extends {
148
+ until(...args: never[]): infer D;
149
+ } ? D : never;
150
+ /** A time of day: `'HH:MM'`, `'HH:MM:SS'`, `'HH:MM:SS.fffffffff'` or a `Temporal.PlainTime`. */
151
+ type TimeInput = string | PlainTimeLike;
152
+ /** A working period within one day, `[start, end)`. `'24:00'` is allowed as an end. */
153
+ type TimeRange = readonly [start: TimeInput, end: TimeInput];
154
+ /** The working periods of one day, e.g. `[['09:00', '12:00'], ['13:00', '17:00']]`. */
155
+ type DayHours = readonly TimeRange[];
156
+ /** A normalized working period as returned by this library, e.g. `['09:00', '17:30']`. */
157
+ type TimeRangeText = readonly [start: string, end: string];
158
+ /** Hours per ISO day of week (1 = Monday … 7 = Sunday); `default` applies to days not listed. */
159
+ type WeeklyHours = Readonly<Partial<Record<DayOfWeek, DayHours>>> & {
160
+ readonly default?: DayHours;
161
+ };
162
+ /** The same hours on every workday, or hours per day of week. */
163
+ type WorkingHours = DayHours | WeeklyHours;
164
+ /**
165
+ * Decides the hours of a specific date. Receives an ISO `Temporal.PlainDate` and that date's
166
+ * regular hours (empty on weekends and holidays). Return `undefined` to keep the regular hours,
167
+ * `[]` to close, or other hours.
168
+ */
169
+ type SpecialHoursFunction = {
170
+ bivarianceHack(date: PlainDateLike, regular: readonly TimeRangeText[]): DayHours | undefined;
171
+ }['bivarianceHack'];
172
+ /** Hours for specific dates, keyed by `'YYYY-MM-DD'` (`[]` = closed), or a function. */
173
+ type SpecialHours = Readonly<Record<string, DayHours>> | SpecialHoursFunction;
174
+ /** Workdays plus working hours, for the working-time functions. */
175
+ interface WorkingTimeOptions extends WorkdayOptions {
176
+ /** Working hours on workdays. Weekends and holidays have no working hours. */
177
+ readonly hours: WorkingHours;
178
+ /** Overrides for specific dates (half days, extended hours, closures). Takes precedence over everything else. */
179
+ readonly specialHours?: SpecialHours | undefined;
180
+ }
181
+
182
+ /** Workday helpers bound to one configuration. See the standalone functions for details. */
183
+ interface WorkCalendar {
184
+ /** A frozen snapshot of the options the calendar was created with. */
185
+ readonly options: Readonly<WorkdayOptions>;
186
+ isWorkday(date: DateInput): boolean;
187
+ isWeekend(date: DateInput): boolean;
188
+ isHoliday(date: DateInput): boolean;
189
+ addWorkdays<T extends DateInput>(date: T, amount: number): T;
190
+ subtractWorkdays<T extends DateInput>(date: T, amount: number): T;
191
+ nextWorkday<T extends DateInput>(date: T): T;
192
+ previousWorkday<T extends DateInput>(date: T): T;
193
+ rollForward<T extends DateInput>(date: T): T;
194
+ rollBackward<T extends DateInput>(date: T): T;
195
+ workdaysBetween(start: DateInput, end: DateInput): number;
196
+ eachWorkday<T extends DateInput>(start: T, end: DateInput): Generator<T, void>;
197
+ }
198
+ /** A calendar created with `hours`: workday helpers plus working-time helpers. */
199
+ interface WorkTimeCalendar extends WorkCalendar {
200
+ readonly options: Readonly<WorkingTimeOptions>;
201
+ isWorkingTime(dateTime: DateTimeInput): boolean;
202
+ addWorkingTime<T extends DateTimeInput>(dateTime: T, duration: DurationInput): T;
203
+ subtractWorkingTime<T extends DateTimeInput>(dateTime: T, duration: DurationInput): T;
204
+ workingTimeBetween<T extends DateTimeInput>(start: T, end: NoInfer<T>): DurationOf<T>;
205
+ rollForwardToWorkingTime<T extends DateTimeInput>(dateTime: T): T;
206
+ rollBackwardToWorkingTime<T extends DateTimeInput>(dateTime: T): T;
207
+ getWorkingHours(date: DateInput): readonly TimeRangeText[];
208
+ }
209
+ /**
210
+ * Creates helpers bound to one configuration. The options are validated and read
211
+ * immediately (holidays and hours are parsed once), so later changes to the passed-in
212
+ * arrays don't affect the calendar. Prefer this over the standalone functions when the
213
+ * same configuration is used repeatedly. With `hours`, the working-time helpers are included.
214
+ */
215
+ declare function createWorkCalendar(options: WorkingTimeOptions): WorkTimeCalendar;
216
+ declare function createWorkCalendar(options?: WorkdayOptions): WorkCalendar;
217
+
218
+ /** `true` if the date falls on a configured weekend day (ignores holidays and `extraWorkdays`). */
219
+ declare function isWeekend(date: DateInput, options?: WorkdayOptions): boolean;
220
+ /** `true` if the date is a configured holiday and not listed in `extraWorkdays`. */
221
+ declare function isHoliday(date: DateInput, options?: WorkdayOptions): boolean;
222
+ /** `true` if the date is a working day: not a weekend day and not a holiday, or listed in `extraWorkdays`. */
223
+ declare function isWorkday(date: DateInput, options?: WorkdayOptions): boolean;
224
+ /**
225
+ * Adds `amount` workdays (negative to go back). The result has the same type as the input,
226
+ * and time of day / time zone are preserved. `amount = 0` returns the input unchanged.
227
+ * Starting on a non-workday, `+1` gives the next workday.
228
+ */
229
+ declare function addWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T;
230
+ /** Subtracts `amount` workdays. Same as `addWorkdays(date, -amount)`. */
231
+ declare function subtractWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T;
232
+ /** The first workday strictly after `date`. */
233
+ declare function nextWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T;
234
+ /** The last workday strictly before `date`. */
235
+ declare function previousWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T;
236
+ /** `date` itself if it is a workday, otherwise the next workday after it. */
237
+ declare function rollForward<T extends DateInput>(date: T, options?: WorkdayOptions): T;
238
+ /** `date` itself if it is a workday, otherwise the previous workday before it. */
239
+ declare function rollBackward<T extends DateInput>(date: T, options?: WorkdayOptions): T;
240
+ /**
241
+ * Number of workdays after `start` up to and including `end` — the range `(start, end]`.
242
+ * Negative when `end` is before `start`. Only calendar dates matter; times are ignored.
243
+ *
244
+ * For `amount >= 0`: `workdaysBetween(d, addWorkdays(d, amount)) === amount`.
245
+ */
246
+ declare function workdaysBetween(start: DateInput, end: DateInput, options?: WorkdayOptions): number;
247
+ /**
248
+ * Yields every workday from `start` to `end`, both inclusive, in ascending order.
249
+ * Values have the type of `start` (time of day / zone preserved). Yields nothing if `end < start`.
250
+ */
251
+ declare function eachWorkday<T extends DateInput>(start: T, end: DateInput, options?: WorkdayOptions): Generator<T, void>;
252
+
253
+ /** `true` if `dateTime` is inside working hours (periods are `[start, end)`: 17:00 is not working time in 09:00–17:00). */
254
+ declare function isWorkingTime(dateTime: DateTimeInput, options: WorkingTimeOptions): boolean;
255
+ /**
256
+ * Adds working time: only time inside working hours counts. Negative durations go back.
257
+ * Starting outside working hours, counting begins at the next working period. Landing
258
+ * exactly at the end of a period returns that end (`16:00 + 1h` → `17:00`, not the next morning).
259
+ *
260
+ * The result has the type of the input. For `ZonedDateTime`, elapsed time is exact across DST changes.
261
+ */
262
+ declare function addWorkingTime<T extends DateTimeInput>(dateTime: T, duration: DurationInput, options: WorkingTimeOptions): T;
263
+ /** Subtracts working time. Landing exactly at the start of a period returns that start. */
264
+ declare function subtractWorkingTime<T extends DateTimeInput>(dateTime: T, duration: DurationInput, options: WorkingTimeOptions): T;
265
+ /**
266
+ * Working time in `[start, end)` as a `Temporal.Duration` balanced up to hours (e.g. `PT27H30M`);
267
+ * negative when `end` is before `start`. Use `.total('hours')` for a number.
268
+ */
269
+ declare function workingTimeBetween<T extends DateTimeInput>(start: T, end: NoInfer<T>, options: WorkingTimeOptions): DurationOf<T>;
270
+ /** `dateTime` itself if it is working time, otherwise the start of the next working period. */
271
+ declare function rollForwardToWorkingTime<T extends DateTimeInput>(dateTime: T, options: WorkingTimeOptions): T;
272
+ /** `dateTime` itself if it is working time, otherwise the end of the previous working period. */
273
+ declare function rollBackwardToWorkingTime<T extends DateTimeInput>(dateTime: T, options: WorkingTimeOptions): T;
274
+ /** The working periods of a date after weekends, holidays and special hours, e.g. `[['09:00', '17:00']]`. */
275
+ declare function getWorkingHours(date: DateInput, options: WorkingTimeOptions): readonly TimeRangeText[];
276
+
277
+ /** The parts of a `date-holidays` holiday this adapter uses. */
278
+ interface DateHolidaysEntry {
279
+ /** Local start, `'YYYY-MM-DD hh:mm:ss'`, optionally with a `' -0600'`-style shift (eve-start holidays). */
280
+ readonly date: string;
281
+ /** `'public'`, `'bank'`, `'school'`, `'optional'` or `'observance'`. */
282
+ readonly type: string;
283
+ readonly start: Date;
284
+ readonly end: Date;
285
+ }
286
+ /** The parts of a `date-holidays` `Holidays` instance this adapter uses. */
287
+ interface DateHolidaysSource {
288
+ getHolidays(year: number): readonly DateHolidaysEntry[];
289
+ }
290
+ interface FromDateHolidaysOptions {
291
+ /** Holiday types that close the business. Default `['public']`. */
292
+ readonly types?: readonly string[] | undefined;
293
+ }
294
+ /** Spread into calendar options: `createWorkCalendar({ ...fromDateHolidays(hd), hours })`. */
295
+ interface DateHolidaysOptions {
296
+ /** `true` for dates that a holiday covers completely. */
297
+ readonly holidays: HolidayPredicate;
298
+ /** Regular hours minus partial-day closures (e.g. closing at 14:00 on Christmas Eve). */
299
+ readonly specialHours: SpecialHoursFunction;
300
+ }
301
+ /**
302
+ * Turns a `date-holidays` instance into `holidays` and `specialHours` options:
303
+ *
304
+ * ```ts
305
+ * import Holidays from 'date-holidays';
306
+ * const cal = createWorkCalendar({
307
+ * ...fromDateHolidays(new Holidays('DE', 'BY'), { types: ['public', 'bank'] }),
308
+ * hours: [['09:00', '17:00']],
309
+ * });
310
+ * ```
311
+ *
312
+ * Days a holiday covers completely are holidays. Days it covers partly (Christmas Eve from
313
+ * 14:00) stay workdays with reduced hours. Holidays that `date-holidays` starts on the
314
+ * evening before (Hijri and Hebrew calendar holidays) are taken as it reports them.
315
+ * Results are cached per year.
316
+ */
317
+ declare function fromDateHolidays(source: DateHolidaysSource, options?: FromDateHolidaysOptions): DateHolidaysOptions;
318
+
319
+ export { addWorkdays, addWorkingTime, createWorkCalendar, eachWorkday, fromDateHolidays, getWorkingHours, isHoliday, isWeekend, isWorkday, isWorkingTime, nextWorkday, previousWorkday, rollBackward, rollBackwardToWorkingTime, rollForward, rollForwardToWorkingTime, subtractWorkdays, subtractWorkingTime, workdaysBetween, workingTimeBetween };
320
+ export type { DateHolidaysEntry, DateHolidaysOptions, DateHolidaysSource, DateInput, DateTimeFields, DateTimeInput, DateTimeLike, DayHours, DayOfWeek, DurationInput, DurationLike, DurationOf, FromDateHolidaysOptions, HolidayInput, HolidayPredicate, PlainDateLike, PlainDateTimeLike, PlainMonthDayLike, PlainTimeLike, SpecialHours, SpecialHoursFunction, TimeInput, TimeRange, TimeRangeText, WeeklyHours, WorkCalendar, WorkTimeCalendar, WorkdayOptions, WorkingHours, WorkingTimeOptions, ZonedDateTimeLike };