temporal-workdays 0.0.0-stage → 0.1.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.
- package/CHANGELOG.md +7 -0
- package/LICENSE +21 -0
- package/README.md +142 -2
- package/dist/index.cjs +263 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +119 -0
- package/dist/index.d.ts +119 -0
- package/dist/index.js +250 -0
- package/dist/index.js.map +1 -0
- package/package.json +70 -4
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Akash Gupta
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,143 @@
|
|
|
1
|
-
#
|
|
1
|
+
# temporal-workdays
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Business-day math for the JavaScript [Temporal API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal).
|
|
4
|
+
Add and subtract workdays, count workdays between dates, and handle custom weekends, holidays and make-up working days.
|
|
5
|
+
|
|
6
|
+
- **Built for Temporal** — works with `PlainDate`, `PlainDateTime` and `ZonedDateTime`, and returns the same type you pass in (time of day and time zone are kept, DST-safe).
|
|
7
|
+
- **Any Temporal implementation** — native, [`temporal-polyfill`](https://www.npmjs.com/package/temporal-polyfill) or `@js-temporal/polyfill`. No global `Temporal` required, at runtime or in your types.
|
|
8
|
+
- **Zero dependencies**, ~2 KB min+gzip, tree-shakeable, ESM + CommonJS, TypeScript types included.
|
|
9
|
+
- **Fast** — date math runs on integers, not on a chain of `Temporal` objects.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install temporal-workdays
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { addWorkdays, workdaysBetween, isWorkday } from 'temporal-workdays';
|
|
19
|
+
|
|
20
|
+
const friday = Temporal.PlainDate.from('2026-11-06');
|
|
21
|
+
|
|
22
|
+
addWorkdays(friday, 1).toString(); // '2026-11-09' (Monday)
|
|
23
|
+
addWorkdays(friday, -5).toString(); // '2026-10-30'
|
|
24
|
+
isWorkday(Temporal.PlainDate.from('2026-11-07')); // false (Saturday)
|
|
25
|
+
|
|
26
|
+
workdaysBetween(
|
|
27
|
+
Temporal.PlainDate.from('2026-11-02'),
|
|
28
|
+
Temporal.PlainDate.from('2026-11-09'),
|
|
29
|
+
); // 5
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Holidays, custom weekends and make-up days
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { createWorkCalendar } from 'temporal-workdays';
|
|
36
|
+
|
|
37
|
+
const cal = createWorkCalendar({
|
|
38
|
+
weekend: [5, 6], // Friday + Saturday (ISO: 1 = Mon … 7 = Sun)
|
|
39
|
+
holidays: [
|
|
40
|
+
'2026-03-20', // one specific date
|
|
41
|
+
'12-25', // every year on Dec 25
|
|
42
|
+
Temporal.PlainMonthDay.from('01-01'),
|
|
43
|
+
],
|
|
44
|
+
extraWorkdays: ['2026-03-28'], // a Saturday that is a working day
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
cal.addWorkdays(Temporal.PlainDate.from('2026-03-19'), 1);
|
|
48
|
+
cal.workdaysBetween(start, end);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`createWorkCalendar` parses the configuration once and reuses it. Every function also accepts the
|
|
52
|
+
same options as its last argument, as in `addWorkdays(date, 3, { holidays })`.
|
|
53
|
+
|
|
54
|
+
### Rule-based holidays
|
|
55
|
+
|
|
56
|
+
Pass a function to decide holidays yourself, for example to plug in a holiday data source:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const cal = createWorkCalendar({
|
|
60
|
+
// Receives an ISO-calendar Temporal.PlainDate. Not called on weekend days.
|
|
61
|
+
holidays: (date) => date.month === 7 && date.day === 4,
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
For example, with [`date-holidays`](https://www.npmjs.com/package/date-holidays):
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import Holidays from 'date-holidays';
|
|
69
|
+
|
|
70
|
+
const hd = new Holidays('US');
|
|
71
|
+
const cal = createWorkCalendar({
|
|
72
|
+
holidays: (d) => (hd.isHoliday(new Date(d.year, d.month - 1, d.day)) || []).some((h) => h.type === 'public'),
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Time zones and times of day
|
|
77
|
+
|
|
78
|
+
Only the **calendar date** decides whether a day is a workday. A `ZonedDateTime` keeps its local wall-clock time:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const t = Temporal.ZonedDateTime.from('2026-11-06T17:00[America/New_York]');
|
|
82
|
+
addWorkdays(t, 1).toString(); // '2026-11-09T17:00:00-05:00[America/New_York]'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## API
|
|
86
|
+
|
|
87
|
+
All functions take an optional `options` object as the last argument (see [Options](#options)).
|
|
88
|
+
`T` is the type you pass in: `Temporal.PlainDate`, `PlainDateTime` or `ZonedDateTime`.
|
|
89
|
+
|
|
90
|
+
| Function | Returns |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `isWorkday(date)` | `true` unless the date is a weekend day or holiday. `extraWorkdays` always count as workdays. |
|
|
93
|
+
| `isWeekend(date)` | `true` if the date falls on a configured weekend day. |
|
|
94
|
+
| `isHoliday(date)` | `true` if the date is a holiday and not in `extraWorkdays`. |
|
|
95
|
+
| `addWorkdays(date, n)` | `T`, `n` workdays later (earlier if `n` is negative). `n = 0` returns `date` unchanged. From a non-workday, `+1` gives the next workday. |
|
|
96
|
+
| `subtractWorkdays(date, n)` | Same as `addWorkdays(date, -n)`. |
|
|
97
|
+
| `nextWorkday(date)` / `previousWorkday(date)` | The nearest workday strictly after / before `date`. |
|
|
98
|
+
| `rollForward(date)` / `rollBackward(date)` | `date` if it is a workday, otherwise the next / previous workday. |
|
|
99
|
+
| `workdaysBetween(start, end)` | Number of workdays in `(start, end]`, negative if `end < start`. For `n >= 0`, `workdaysBetween(d, addWorkdays(d, n)) === n`. |
|
|
100
|
+
| `eachWorkday(start, end)` | A generator of every workday in `[start, end]` (both inclusive), as `T`. |
|
|
101
|
+
| `createWorkCalendar(options)` | An object with all of the above, bound to `options`. |
|
|
102
|
+
|
|
103
|
+
### Options
|
|
104
|
+
|
|
105
|
+
| Option | Type | Default |
|
|
106
|
+
| --- | --- | --- |
|
|
107
|
+
| `weekend` | `DayOfWeek[]`, ISO numbers `1` (Mon) … `7` (Sun) | `[6, 7]` |
|
|
108
|
+
| `holidays` | Iterable of `'YYYY-MM-DD'`, `'MM-DD'` (every year), `Temporal.PlainDate`, `Temporal.PlainMonthDay`, **or** `(date: Temporal.PlainDate) => boolean` | none |
|
|
109
|
+
| `extraWorkdays` | Iterable of `'YYYY-MM-DD'` or `Temporal.PlainDate`. These dates are workdays even on a weekend or holiday. | none |
|
|
110
|
+
|
|
111
|
+
Invalid input (an unknown weekend day, `'2026-02-30'`, a non-integer `n`) throws a `RangeError` or `TypeError` instead of silently producing a wrong date.
|
|
112
|
+
If a configuration has no workdays at all (every day is a weekend day, or a predicate that always returns `true`),
|
|
113
|
+
the functions throw a `RangeError` instead of looping forever.
|
|
114
|
+
|
|
115
|
+
## Requirements
|
|
116
|
+
|
|
117
|
+
You need a Temporal implementation:
|
|
118
|
+
|
|
119
|
+
- **Native** — Firefox 139+, Chrome 144+, Node.js 26+.
|
|
120
|
+
- **Elsewhere** (Safari/iOS, older Node) — use a polyfill:
|
|
121
|
+
```ts
|
|
122
|
+
import 'temporal-polyfill/global'; // installs a global Temporal
|
|
123
|
+
// or, without touching globals:
|
|
124
|
+
import { Temporal } from 'temporal-polyfill';
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Calendar dates in non-ISO calendars (`hebrew`, `japanese`, …) are supported. Weekends and holidays are always matched on the ISO date, and results keep the input's calendar.
|
|
128
|
+
|
|
129
|
+
## Migrating from moment-business-days / luxon-business-days
|
|
130
|
+
|
|
131
|
+
| moment-business-days | temporal-workdays |
|
|
132
|
+
| --- | --- |
|
|
133
|
+
| `moment(d).businessAdd(3)` | `addWorkdays(d, 3)` |
|
|
134
|
+
| `moment(d).businessSubtract(3)` | `subtractWorkdays(d, 3)` |
|
|
135
|
+
| `moment(d).isBusinessDay()` | `isWorkday(d)` |
|
|
136
|
+
| `moment(d).nextBusinessDay()` | `nextWorkday(d)` |
|
|
137
|
+
| `moment(d).prevBusinessDay()` | `previousWorkday(d)` |
|
|
138
|
+
| `moment(a).businessDiff(moment(b))` | `workdaysBetween(b, a)` (check the endpoint convention for your case) |
|
|
139
|
+
| `moment.updateLocale('us', { holidays, holidayFormat, workingWeekdays })` | `createWorkCalendar({ holidays, weekend })` |
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/epoch.ts
|
|
4
|
+
function epochDayFromIso(year, month, day) {
|
|
5
|
+
const y = month <= 2 ? year - 1 : year;
|
|
6
|
+
const era = Math.floor(y / 400);
|
|
7
|
+
const yoe = y - era * 400;
|
|
8
|
+
const mp = (month + 9) % 12;
|
|
9
|
+
const doy = Math.floor((153 * mp + 2) / 5) + day - 1;
|
|
10
|
+
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
11
|
+
return era * 146097 + doe - 719468;
|
|
12
|
+
}
|
|
13
|
+
function isoFromEpochDay(epochDay) {
|
|
14
|
+
const z = epochDay + 719468;
|
|
15
|
+
const era = Math.floor(z / 146097);
|
|
16
|
+
const doe = z - era * 146097;
|
|
17
|
+
const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
|
|
18
|
+
const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
|
|
19
|
+
const mp = Math.floor((5 * doy + 2) / 153);
|
|
20
|
+
const day = doy - Math.floor((153 * mp + 2) / 5) + 1;
|
|
21
|
+
const month = mp < 10 ? mp + 3 : mp - 9;
|
|
22
|
+
const year = yoe + era * 400 + (month <= 2 ? 1 : 0);
|
|
23
|
+
return [year, month, day];
|
|
24
|
+
}
|
|
25
|
+
function dayOfWeekFromEpochDay(epochDay) {
|
|
26
|
+
return ((epochDay + 3) % 7 + 7) % 7 + 1;
|
|
27
|
+
}
|
|
28
|
+
function isLeapYear(year) {
|
|
29
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
30
|
+
}
|
|
31
|
+
function daysInMonth(year, month) {
|
|
32
|
+
if (month === 2) return isLeapYear(year) ? 29 : 28;
|
|
33
|
+
return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/index.ts
|
|
37
|
+
var MAX_SCAN_DAYS = 366 * 20;
|
|
38
|
+
var DATE_RE = /^([+-]?\d{4,6})-(\d{2})-(\d{2})$/;
|
|
39
|
+
var MONTH_DAY_RE = /^(?:--)?(\d{2})-(\d{2})$/;
|
|
40
|
+
function checkDay(year, month, day, source) {
|
|
41
|
+
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) {
|
|
42
|
+
throw new RangeError(`temporal-workdays: invalid date ${JSON.stringify(String(source))}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function epochDayOfPlainDate(date) {
|
|
46
|
+
const iso = date.calendarId === "iso8601" ? date : date.withCalendar("iso8601");
|
|
47
|
+
return epochDayFromIso(iso.year, iso.month, iso.day);
|
|
48
|
+
}
|
|
49
|
+
function isTemporalLike(value, tag) {
|
|
50
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === `[object ${tag}]`;
|
|
51
|
+
}
|
|
52
|
+
function parseSingleDate(value) {
|
|
53
|
+
if (typeof value === "string") {
|
|
54
|
+
const m = DATE_RE.exec(value);
|
|
55
|
+
if (!m) throw new RangeError(`temporal-workdays: expected "YYYY-MM-DD", got ${JSON.stringify(value)}`);
|
|
56
|
+
const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
57
|
+
checkDay(y, mo, d, value);
|
|
58
|
+
return epochDayFromIso(y, mo, d);
|
|
59
|
+
}
|
|
60
|
+
if (isTemporalLike(value, "Temporal.PlainDate")) return epochDayOfPlainDate(value);
|
|
61
|
+
throw new TypeError(`temporal-workdays: expected a "YYYY-MM-DD" string or Temporal.PlainDate, got ${String(value)}`);
|
|
62
|
+
}
|
|
63
|
+
function compile(options) {
|
|
64
|
+
const weekendDays = options?.weekend ?? [6, 7];
|
|
65
|
+
const weekend = new Array(8).fill(false);
|
|
66
|
+
for (const d of weekendDays) {
|
|
67
|
+
if (!Number.isInteger(d) || d < 1 || d > 7) {
|
|
68
|
+
throw new RangeError(`temporal-workdays: weekend days must be integers 1 (Mon) \u2013 7 (Sun), got ${String(d)}`);
|
|
69
|
+
}
|
|
70
|
+
weekend[d] = true;
|
|
71
|
+
}
|
|
72
|
+
const dates = /* @__PURE__ */ new Set();
|
|
73
|
+
const annual = /* @__PURE__ */ new Set();
|
|
74
|
+
let predicate;
|
|
75
|
+
const holidays = options?.holidays;
|
|
76
|
+
if (typeof holidays === "function") {
|
|
77
|
+
predicate = holidays;
|
|
78
|
+
} else if (holidays != null) {
|
|
79
|
+
for (const h of holidays) {
|
|
80
|
+
if (typeof h === "string") {
|
|
81
|
+
const md = MONTH_DAY_RE.exec(h);
|
|
82
|
+
if (md) {
|
|
83
|
+
const [mo, d] = [Number(md[1]), Number(md[2])];
|
|
84
|
+
checkDay(2e3, mo, d, h);
|
|
85
|
+
annual.add(mo * 100 + d);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
dates.add(parseSingleDate(h));
|
|
89
|
+
} else if (isTemporalLike(h, "Temporal.PlainMonthDay")) {
|
|
90
|
+
const pmd = h;
|
|
91
|
+
if (pmd.calendarId !== "iso8601") {
|
|
92
|
+
throw new RangeError("temporal-workdays: Temporal.PlainMonthDay holidays must use the iso8601 calendar");
|
|
93
|
+
}
|
|
94
|
+
annual.add(Number(pmd.monthCode.slice(1)) * 100 + pmd.day);
|
|
95
|
+
} else {
|
|
96
|
+
dates.add(parseSingleDate(h));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const extra = /* @__PURE__ */ new Set();
|
|
101
|
+
if (options?.extraWorkdays != null) {
|
|
102
|
+
for (const e of options.extraWorkdays) extra.add(parseSingleDate(e));
|
|
103
|
+
}
|
|
104
|
+
return { weekend, dates, annual, predicate, extra };
|
|
105
|
+
}
|
|
106
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
107
|
+
var DEFAULT = compile(void 0);
|
|
108
|
+
function getCompiled(options) {
|
|
109
|
+
if (options === void 0) return DEFAULT;
|
|
110
|
+
let c = cache.get(options);
|
|
111
|
+
if (!c) {
|
|
112
|
+
c = compile(options);
|
|
113
|
+
cache.set(options, c);
|
|
114
|
+
}
|
|
115
|
+
return c;
|
|
116
|
+
}
|
|
117
|
+
function toPlainDate(date) {
|
|
118
|
+
if (date == null || typeof date.add !== "function") {
|
|
119
|
+
throw new TypeError("temporal-workdays: expected a Temporal.PlainDate, PlainDateTime or ZonedDateTime");
|
|
120
|
+
}
|
|
121
|
+
return "toPlainDate" in date ? date.toPlainDate() : date;
|
|
122
|
+
}
|
|
123
|
+
function isHolidayDay(c, epochDay, base, baseDay) {
|
|
124
|
+
if (c.dates.has(epochDay)) return true;
|
|
125
|
+
if (c.annual.size > 0) {
|
|
126
|
+
const [, mo, d] = isoFromEpochDay(epochDay);
|
|
127
|
+
if (c.annual.has(mo * 100 + d)) return true;
|
|
128
|
+
}
|
|
129
|
+
if (c.predicate) {
|
|
130
|
+
const iso = base.calendarId === "iso8601" ? base : base.withCalendar("iso8601");
|
|
131
|
+
return c.predicate(epochDay === baseDay ? iso : iso.add({ days: epochDay - baseDay }));
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
function isWorkdayDay(c, epochDay, base, baseDay) {
|
|
136
|
+
if (c.extra.has(epochDay)) return true;
|
|
137
|
+
if (c.weekend[dayOfWeekFromEpochDay(epochDay)]) return false;
|
|
138
|
+
return !isHolidayDay(c, epochDay, base, baseDay);
|
|
139
|
+
}
|
|
140
|
+
function checkInteger(n, name) {
|
|
141
|
+
if (!Number.isInteger(n)) throw new RangeError(`temporal-workdays: ${name} must be an integer, got ${String(n)}`);
|
|
142
|
+
}
|
|
143
|
+
function noWorkdayError() {
|
|
144
|
+
return new RangeError(
|
|
145
|
+
`temporal-workdays: no workday found within ${MAX_SCAN_DAYS} days \u2014 check your weekend/holiday configuration`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
function step(c, base, count, dir) {
|
|
149
|
+
const baseDay = epochDayOfPlainDate(base);
|
|
150
|
+
let day = baseDay;
|
|
151
|
+
let remaining = count;
|
|
152
|
+
let gap = 0;
|
|
153
|
+
while (remaining > 0) {
|
|
154
|
+
day += dir;
|
|
155
|
+
if (isWorkdayDay(c, day, base, baseDay)) {
|
|
156
|
+
remaining--;
|
|
157
|
+
gap = 0;
|
|
158
|
+
} else if (++gap > MAX_SCAN_DAYS) {
|
|
159
|
+
throw noWorkdayError();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return day - baseDay;
|
|
163
|
+
}
|
|
164
|
+
function shift(date, days) {
|
|
165
|
+
return days === 0 ? date : date.add({ days });
|
|
166
|
+
}
|
|
167
|
+
function isWeekend(date, options) {
|
|
168
|
+
return getCompiled(options).weekend[toPlainDate(date).dayOfWeek] === true;
|
|
169
|
+
}
|
|
170
|
+
function isHoliday(date, options) {
|
|
171
|
+
const c = getCompiled(options);
|
|
172
|
+
const base = toPlainDate(date);
|
|
173
|
+
const day = epochDayOfPlainDate(base);
|
|
174
|
+
return !c.extra.has(day) && isHolidayDay(c, day, base, day);
|
|
175
|
+
}
|
|
176
|
+
function isWorkday(date, options) {
|
|
177
|
+
const c = getCompiled(options);
|
|
178
|
+
const base = toPlainDate(date);
|
|
179
|
+
const day = epochDayOfPlainDate(base);
|
|
180
|
+
return isWorkdayDay(c, day, base, day);
|
|
181
|
+
}
|
|
182
|
+
function addWorkdays(date, amount, options) {
|
|
183
|
+
checkInteger(amount, "amount");
|
|
184
|
+
if (amount === 0) return date;
|
|
185
|
+
const c = getCompiled(options);
|
|
186
|
+
return shift(date, step(c, toPlainDate(date), Math.abs(amount), amount > 0 ? 1 : -1));
|
|
187
|
+
}
|
|
188
|
+
function subtractWorkdays(date, amount, options) {
|
|
189
|
+
checkInteger(amount, "amount");
|
|
190
|
+
return addWorkdays(date, -amount, options);
|
|
191
|
+
}
|
|
192
|
+
function nextWorkday(date, options) {
|
|
193
|
+
return shift(date, step(getCompiled(options), toPlainDate(date), 1, 1));
|
|
194
|
+
}
|
|
195
|
+
function previousWorkday(date, options) {
|
|
196
|
+
return shift(date, step(getCompiled(options), toPlainDate(date), 1, -1));
|
|
197
|
+
}
|
|
198
|
+
function rollForward(date, options) {
|
|
199
|
+
return isWorkday(date, options) ? date : nextWorkday(date, options);
|
|
200
|
+
}
|
|
201
|
+
function rollBackward(date, options) {
|
|
202
|
+
return isWorkday(date, options) ? date : previousWorkday(date, options);
|
|
203
|
+
}
|
|
204
|
+
function workdaysBetween(start, end, options) {
|
|
205
|
+
const c = getCompiled(options);
|
|
206
|
+
const a = toPlainDate(start);
|
|
207
|
+
const b = toPlainDate(end);
|
|
208
|
+
const aDay = epochDayOfPlainDate(a);
|
|
209
|
+
const bDay = epochDayOfPlainDate(b);
|
|
210
|
+
if (aDay === bDay) return 0;
|
|
211
|
+
const [lo, hi, base, baseDay, sign] = aDay < bDay ? [aDay, bDay, a, aDay, 1] : [bDay, aDay, b, bDay, -1];
|
|
212
|
+
let count = 0;
|
|
213
|
+
for (let day = lo + 1; day <= hi; day++) {
|
|
214
|
+
if (isWorkdayDay(c, day, base, baseDay)) count++;
|
|
215
|
+
}
|
|
216
|
+
return sign * count;
|
|
217
|
+
}
|
|
218
|
+
function* eachWorkday(start, end, options) {
|
|
219
|
+
const c = getCompiled(options);
|
|
220
|
+
const base = toPlainDate(start);
|
|
221
|
+
const baseDay = epochDayOfPlainDate(base);
|
|
222
|
+
const endDay = epochDayOfPlainDate(toPlainDate(end));
|
|
223
|
+
for (let day = baseDay; day <= endDay; day++) {
|
|
224
|
+
if (isWorkdayDay(c, day, base, baseDay)) yield shift(start, day - baseDay);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function createWorkCalendar(options = {}) {
|
|
228
|
+
const frozen = Object.freeze({
|
|
229
|
+
weekend: options.weekend ? Object.freeze([...options.weekend]) : void 0,
|
|
230
|
+
holidays: typeof options.holidays === "function" || options.holidays == null ? options.holidays : Object.freeze([...options.holidays]),
|
|
231
|
+
extraWorkdays: options.extraWorkdays ? Object.freeze([...options.extraWorkdays]) : void 0
|
|
232
|
+
});
|
|
233
|
+
cache.set(frozen, compile(frozen));
|
|
234
|
+
return Object.freeze({
|
|
235
|
+
options: frozen,
|
|
236
|
+
isWorkday: (d) => isWorkday(d, frozen),
|
|
237
|
+
isWeekend: (d) => isWeekend(d, frozen),
|
|
238
|
+
isHoliday: (d) => isHoliday(d, frozen),
|
|
239
|
+
addWorkdays: (d, n) => addWorkdays(d, n, frozen),
|
|
240
|
+
subtractWorkdays: (d, n) => subtractWorkdays(d, n, frozen),
|
|
241
|
+
nextWorkday: (d) => nextWorkday(d, frozen),
|
|
242
|
+
previousWorkday: (d) => previousWorkday(d, frozen),
|
|
243
|
+
rollForward: (d) => rollForward(d, frozen),
|
|
244
|
+
rollBackward: (d) => rollBackward(d, frozen),
|
|
245
|
+
workdaysBetween: (a, b) => workdaysBetween(a, b, frozen),
|
|
246
|
+
eachWorkday: (a, b) => eachWorkday(a, b, frozen)
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
exports.addWorkdays = addWorkdays;
|
|
251
|
+
exports.createWorkCalendar = createWorkCalendar;
|
|
252
|
+
exports.eachWorkday = eachWorkday;
|
|
253
|
+
exports.isHoliday = isHoliday;
|
|
254
|
+
exports.isWeekend = isWeekend;
|
|
255
|
+
exports.isWorkday = isWorkday;
|
|
256
|
+
exports.nextWorkday = nextWorkday;
|
|
257
|
+
exports.previousWorkday = previousWorkday;
|
|
258
|
+
exports.rollBackward = rollBackward;
|
|
259
|
+
exports.rollForward = rollForward;
|
|
260
|
+
exports.subtractWorkdays = subtractWorkdays;
|
|
261
|
+
exports.workdaysBetween = workdaysBetween;
|
|
262
|
+
//# sourceMappingURL=index.cjs.map
|
|
263
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/epoch.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["// Pure integer date arithmetic on the proleptic ISO calendar.\n// \"Epoch day\" = number of days since 1970-01-01 (which is day 0).\n// Working on integers keeps the hot loops fast and independent of any\n// particular Temporal implementation (native or polyfill).\n\n/** Days from 1970-01-01 to the given ISO date (Howard Hinnant's algorithm). */\nexport function epochDayFromIso(year: number, month: number, day: number): number {\n const y = month <= 2 ? year - 1 : year;\n const era = Math.floor(y / 400);\n const yoe = y - era * 400;\n const mp = (month + 9) % 12; // March = 0\n const doy = Math.floor((153 * mp + 2) / 5) + day - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\n/** Inverse of epochDayFromIso. Returns [year, month, day]. */\nexport function isoFromEpochDay(epochDay: number): [number, number, number] {\n const z = epochDay + 719468;\n const era = Math.floor(z / 146097);\n const doe = z - era * 146097;\n const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);\n const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));\n const mp = Math.floor((5 * doy + 2) / 153);\n const day = doy - Math.floor((153 * mp + 2) / 5) + 1;\n const month = mp < 10 ? mp + 3 : mp - 9;\n const year = yoe + era * 400 + (month <= 2 ? 1 : 0);\n return [year, month, day];\n}\n\n/** ISO day of week: 1 = Monday … 7 = Sunday. 1970-01-01 was a Thursday. */\nexport function dayOfWeekFromEpochDay(epochDay: number): number {\n return ((((epochDay + 3) % 7) + 7) % 7) + 1;\n}\n\nexport function isLeapYear(year: number): boolean {\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nexport function daysInMonth(year: number, month: number): number {\n if (month === 2) return isLeapYear(year) ? 29 : 28;\n return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;\n}\n","import { dayOfWeekFromEpochDay, daysInMonth, epochDayFromIso, isoFromEpochDay } from './epoch.js';\n\n/**\n * The parts of a `Temporal.PlainDate` this library relies on. Described structurally so that\n * native Temporal, `temporal-polyfill`, `@js-temporal/polyfill` and their TypeScript types all work,\n * with no global `Temporal` type required.\n */\nexport interface PlainDateLike {\n readonly calendarId: string;\n readonly year: number;\n readonly month: number;\n readonly monthCode: string;\n readonly day: number;\n readonly dayOfWeek: number;\n withCalendar(calendar: 'iso8601'): PlainDateLike;\n add(duration: { days: number }): PlainDateLike;\n toString(): string;\n}\n\n/** The parts of a `Temporal.PlainDateTime` / `Temporal.ZonedDateTime` this library relies on. */\nexport interface DateTimeLike {\n toPlainDate(): PlainDateLike;\n add(duration: { days: number }): DateTimeLike;\n}\n\n/** The parts of a `Temporal.PlainMonthDay` this library relies on. */\nexport interface PlainMonthDayLike {\n readonly calendarId: string;\n readonly monthCode: string;\n readonly day: number;\n}\n\n/**\n * Any Temporal value that has a calendar date: `Temporal.PlainDate`, `Temporal.PlainDateTime`\n * or `Temporal.ZonedDateTime`. Every helper returns the same type it was given.\n */\nexport type DateInput = PlainDateLike | DateTimeLike;\n\n/** ISO day of week: 1 = Monday … 7 = Sunday (same as `Temporal.PlainDate#dayOfWeek`). */\nexport type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;\n\n/**\n * A holiday entry:\n * - `'2026-12-25'` or a `Temporal.PlainDate` — a single date.\n * - `'12-25'` or a `Temporal.PlainMonthDay` — every year on that ISO month/day.\n */\nexport type HolidayInput = string | PlainDateLike | PlainMonthDayLike;\n\n/**\n * Decides whether a date is a holiday. Receives an ISO-calendar `Temporal.PlainDate`\n * from the same Temporal implementation as the input date. Not called for weekend days.\n */\n// Method syntax keeps the parameter bivariant, so `(d: Temporal.PlainDate) => boolean` is accepted.\nexport type HolidayPredicate = { bivarianceHack(date: PlainDateLike): boolean }['bivarianceHack'];\n\nexport interface WorkdayOptions {\n /** Non-working days of the week. Default `[6, 7]` (Saturday, Sunday). */\n weekend?: readonly DayOfWeek[];\n /** Holiday dates, or a function that decides whether a date is a holiday. */\n holidays?: Iterable<HolidayInput> | HolidayPredicate;\n /**\n * Dates that count as workdays even if they fall on the weekend or a holiday\n * (for example, make-up working Saturdays). Single dates only.\n */\n extraWorkdays?: Iterable<string | PlainDateLike>;\n}\n\n/** Longest run of consecutive non-workdays we search before giving up (≈ 20 years). */\nconst MAX_SCAN_DAYS = 366 * 20;\n\ninterface Compiled {\n weekend: boolean[]; // index 1..7\n dates: Set<number>; // epoch days\n annual: Set<number>; // month * 100 + day\n predicate: HolidayPredicate | undefined;\n extra: Set<number>; // epoch days\n}\n\nconst DATE_RE = /^([+-]?\\d{4,6})-(\\d{2})-(\\d{2})$/;\nconst MONTH_DAY_RE = /^(?:--)?(\\d{2})-(\\d{2})$/;\n\nfunction checkDay(year: number, month: number, day: number, source: unknown): void {\n if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) {\n throw new RangeError(`temporal-workdays: invalid date ${JSON.stringify(String(source))}`);\n }\n}\n\nfunction epochDayOfPlainDate(date: PlainDateLike): number {\n const iso = date.calendarId === 'iso8601' ? date : date.withCalendar('iso8601');\n return epochDayFromIso(iso.year, iso.month, iso.day);\n}\n\nfunction isTemporalLike(value: unknown, tag: string): boolean {\n return typeof value === 'object' && value !== null && Object.prototype.toString.call(value) === `[object ${tag}]`;\n}\n\nfunction parseSingleDate(value: unknown): number {\n if (typeof value === 'string') {\n const m = DATE_RE.exec(value);\n if (!m) throw new RangeError(`temporal-workdays: expected \"YYYY-MM-DD\", got ${JSON.stringify(value)}`);\n const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];\n checkDay(y, mo, d, value);\n return epochDayFromIso(y, mo, d);\n }\n if (isTemporalLike(value, 'Temporal.PlainDate')) return epochDayOfPlainDate(value as PlainDateLike);\n throw new TypeError(`temporal-workdays: expected a \"YYYY-MM-DD\" string or Temporal.PlainDate, got ${String(value)}`);\n}\n\nfunction compile(options: WorkdayOptions | undefined): Compiled {\n const weekendDays = options?.weekend ?? [6, 7];\n const weekend = new Array<boolean>(8).fill(false);\n for (const d of weekendDays) {\n if (!Number.isInteger(d) || d < 1 || d > 7) {\n throw new RangeError(`temporal-workdays: weekend days must be integers 1 (Mon) – 7 (Sun), got ${String(d)}`);\n }\n weekend[d] = true;\n }\n\n const dates = new Set<number>();\n const annual = new Set<number>();\n let predicate: HolidayPredicate | undefined;\n const holidays = options?.holidays;\n\n if (typeof holidays === 'function') {\n predicate = holidays;\n } else if (holidays != null) {\n for (const h of holidays) {\n if (typeof h === 'string') {\n const md = MONTH_DAY_RE.exec(h);\n if (md) {\n const [mo, d] = [Number(md[1]), Number(md[2])];\n checkDay(2000, mo, d, h); // leap year so that 02-29 is accepted\n annual.add(mo * 100 + d);\n continue;\n }\n dates.add(parseSingleDate(h));\n } else if (isTemporalLike(h, 'Temporal.PlainMonthDay')) {\n const pmd = h as PlainMonthDayLike;\n if (pmd.calendarId !== 'iso8601') {\n throw new RangeError('temporal-workdays: Temporal.PlainMonthDay holidays must use the iso8601 calendar');\n }\n annual.add(Number(pmd.monthCode.slice(1)) * 100 + pmd.day);\n } else {\n dates.add(parseSingleDate(h));\n }\n }\n }\n\n const extra = new Set<number>();\n if (options?.extraWorkdays != null) {\n for (const e of options.extraWorkdays) extra.add(parseSingleDate(e));\n }\n\n return { weekend, dates, annual, predicate, extra };\n}\n\nconst cache = new WeakMap<object, Compiled>();\nconst DEFAULT = compile(undefined);\n\nfunction getCompiled(options: WorkdayOptions | undefined): Compiled {\n if (options === undefined) return DEFAULT;\n let c = cache.get(options);\n if (!c) {\n c = compile(options);\n cache.set(options, c);\n }\n return c;\n}\n\n/** Calendar date of the input as an ISO epoch day, plus an ISO PlainDate for predicates. */\nfunction toPlainDate(date: DateInput): PlainDateLike {\n if (date == null || typeof (date as { add?: unknown }).add !== 'function') {\n throw new TypeError('temporal-workdays: expected a Temporal.PlainDate, PlainDateTime or ZonedDateTime');\n }\n return 'toPlainDate' in date ? date.toPlainDate() : date;\n}\n\nfunction isHolidayDay(c: Compiled, epochDay: number, base: PlainDateLike, baseDay: number): boolean {\n if (c.dates.has(epochDay)) return true;\n if (c.annual.size > 0) {\n const [, mo, d] = isoFromEpochDay(epochDay);\n if (c.annual.has(mo * 100 + d)) return true;\n }\n if (c.predicate) {\n const iso = base.calendarId === 'iso8601' ? base : base.withCalendar('iso8601');\n return c.predicate(epochDay === baseDay ? iso : iso.add({ days: epochDay - baseDay }));\n }\n return false;\n}\n\nfunction isWorkdayDay(c: Compiled, epochDay: number, base: PlainDateLike, baseDay: number): boolean {\n if (c.extra.has(epochDay)) return true;\n if (c.weekend[dayOfWeekFromEpochDay(epochDay)]) return false;\n return !isHolidayDay(c, epochDay, base, baseDay);\n}\n\nfunction checkInteger(n: number, name: string): void {\n if (!Number.isInteger(n)) throw new RangeError(`temporal-workdays: ${name} must be an integer, got ${String(n)}`);\n}\n\nfunction noWorkdayError(): RangeError {\n return new RangeError(\n `temporal-workdays: no workday found within ${MAX_SCAN_DAYS} days — check your weekend/holiday configuration`,\n );\n}\n\n/** Steps `count` workdays in direction `dir` (+1 / -1) and returns the epoch-day delta from start. */\nfunction step(c: Compiled, base: PlainDateLike, count: number, dir: 1 | -1): number {\n const baseDay = epochDayOfPlainDate(base);\n let day = baseDay;\n let remaining = count;\n let gap = 0;\n while (remaining > 0) {\n day += dir;\n if (isWorkdayDay(c, day, base, baseDay)) {\n remaining--;\n gap = 0;\n } else if (++gap > MAX_SCAN_DAYS) {\n throw noWorkdayError();\n }\n }\n return day - baseDay;\n}\n\nfunction shift<T extends DateInput>(date: T, days: number): T {\n return (days === 0 ? date : date.add({ days })) as T;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** `true` if the date falls on a configured weekend day (ignores holidays and `extraWorkdays`). */\nexport function isWeekend(date: DateInput, options?: WorkdayOptions): boolean {\n return getCompiled(options).weekend[toPlainDate(date).dayOfWeek] === true;\n}\n\n/** `true` if the date is a configured holiday and not listed in `extraWorkdays`. */\nexport function isHoliday(date: DateInput, options?: WorkdayOptions): boolean {\n const c = getCompiled(options);\n const base = toPlainDate(date);\n const day = epochDayOfPlainDate(base);\n return !c.extra.has(day) && isHolidayDay(c, day, base, day);\n}\n\n/** `true` if the date is a working day: not a weekend day and not a holiday, or listed in `extraWorkdays`. */\nexport function isWorkday(date: DateInput, options?: WorkdayOptions): boolean {\n const c = getCompiled(options);\n const base = toPlainDate(date);\n const day = epochDayOfPlainDate(base);\n return isWorkdayDay(c, day, base, day);\n}\n\n/**\n * Adds `amount` workdays (negative to go back). The result has the same type as the input,\n * and time of day / time zone are preserved. `amount = 0` returns the input unchanged.\n * Starting on a non-workday, `+1` gives the next workday.\n */\nexport function addWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T {\n checkInteger(amount, 'amount');\n if (amount === 0) return date;\n const c = getCompiled(options);\n return shift(date, step(c, toPlainDate(date), Math.abs(amount), amount > 0 ? 1 : -1));\n}\n\n/** Subtracts `amount` workdays. Same as `addWorkdays(date, -amount)`. */\nexport function subtractWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T {\n checkInteger(amount, 'amount');\n return addWorkdays(date, -amount, options);\n}\n\n/** The first workday strictly after `date`. */\nexport function nextWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return shift(date, step(getCompiled(options), toPlainDate(date), 1, 1));\n}\n\n/** The last workday strictly before `date`. */\nexport function previousWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return shift(date, step(getCompiled(options), toPlainDate(date), 1, -1));\n}\n\n/** `date` itself if it is a workday, otherwise the next workday after it. */\nexport function rollForward<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return isWorkday(date, options) ? date : nextWorkday(date, options);\n}\n\n/** `date` itself if it is a workday, otherwise the previous workday before it. */\nexport function rollBackward<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return isWorkday(date, options) ? date : previousWorkday(date, options);\n}\n\n/**\n * Number of workdays after `start` up to and including `end` — the range `(start, end]`.\n * Negative when `end` is before `start`. Only calendar dates matter; times are ignored.\n *\n * For `amount >= 0`: `workdaysBetween(d, addWorkdays(d, amount)) === amount`.\n */\nexport function workdaysBetween(start: DateInput, end: DateInput, options?: WorkdayOptions): number {\n const c = getCompiled(options);\n const a = toPlainDate(start);\n const b = toPlainDate(end);\n const aDay = epochDayOfPlainDate(a);\n const bDay = epochDayOfPlainDate(b);\n if (aDay === bDay) return 0;\n const [lo, hi, base, baseDay, sign] = aDay < bDay ? [aDay, bDay, a, aDay, 1] : [bDay, aDay, b, bDay, -1];\n let count = 0;\n for (let day = lo + 1; day <= hi; day++) {\n if (isWorkdayDay(c, day, base, baseDay)) count++;\n }\n return sign * count;\n}\n\n/**\n * Yields every workday from `start` to `end`, both inclusive, in ascending order.\n * Values have the type of `start` (time of day / zone preserved). Yields nothing if `end < start`.\n */\nexport function* eachWorkday<T extends DateInput>(start: T, end: DateInput, options?: WorkdayOptions): Generator<T> {\n const c = getCompiled(options);\n const base = toPlainDate(start);\n const baseDay = epochDayOfPlainDate(base);\n const endDay = epochDayOfPlainDate(toPlainDate(end));\n for (let day = baseDay; day <= endDay; day++) {\n if (isWorkdayDay(c, day, base, baseDay)) yield shift(start, day - baseDay);\n }\n}\n\n/** A set of workday helpers bound to one configuration. Holidays are parsed once. */\nexport interface WorkCalendar {\n readonly options: Readonly<WorkdayOptions>;\n isWorkday(date: DateInput): boolean;\n isWeekend(date: DateInput): boolean;\n isHoliday(date: DateInput): boolean;\n addWorkdays<T extends DateInput>(date: T, amount: number): T;\n subtractWorkdays<T extends DateInput>(date: T, amount: number): T;\n nextWorkday<T extends DateInput>(date: T): T;\n previousWorkday<T extends DateInput>(date: T): T;\n rollForward<T extends DateInput>(date: T): T;\n rollBackward<T extends DateInput>(date: T): T;\n workdaysBetween(start: DateInput, end: DateInput): number;\n eachWorkday<T extends DateInput>(start: T, end: DateInput): Generator<T>;\n}\n\n/**\n * Creates helpers bound to one configuration. The options are validated and copied\n * immediately, so later changes to the passed-in arrays don't affect the calendar.\n */\nexport function createWorkCalendar(options: WorkdayOptions = {}): WorkCalendar {\n const frozen: WorkdayOptions = Object.freeze({\n weekend: options.weekend ? Object.freeze([...options.weekend]) : undefined,\n holidays: typeof options.holidays === 'function' || options.holidays == null ? options.holidays : Object.freeze([...options.holidays]),\n extraWorkdays: options.extraWorkdays ? Object.freeze([...options.extraWorkdays]) : undefined,\n });\n cache.set(frozen, compile(frozen)); // validate eagerly\n return Object.freeze({\n options: frozen,\n isWorkday: (d) => isWorkday(d, frozen),\n isWeekend: (d) => isWeekend(d, frozen),\n isHoliday: (d) => isHoliday(d, frozen),\n addWorkdays: (d, n) => addWorkdays(d, n, frozen),\n subtractWorkdays: (d, n) => subtractWorkdays(d, n, frozen),\n nextWorkday: (d) => nextWorkday(d, frozen),\n previousWorkday: (d) => previousWorkday(d, frozen),\n rollForward: (d) => rollForward(d, frozen),\n rollBackward: (d) => rollBackward(d, frozen),\n workdaysBetween: (a, b) => workdaysBetween(a, b, frozen),\n eachWorkday: (a, b) => eachWorkday(a, b, frozen),\n } satisfies WorkCalendar);\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts of a `Temporal.PlainDate` this library relies on. Described structurally so that
|
|
3
|
+
* native Temporal, `temporal-polyfill`, `@js-temporal/polyfill` and their TypeScript types all work,
|
|
4
|
+
* with no global `Temporal` type required.
|
|
5
|
+
*/
|
|
6
|
+
interface PlainDateLike {
|
|
7
|
+
readonly calendarId: string;
|
|
8
|
+
readonly year: number;
|
|
9
|
+
readonly month: number;
|
|
10
|
+
readonly monthCode: string;
|
|
11
|
+
readonly day: number;
|
|
12
|
+
readonly dayOfWeek: number;
|
|
13
|
+
withCalendar(calendar: 'iso8601'): PlainDateLike;
|
|
14
|
+
add(duration: {
|
|
15
|
+
days: number;
|
|
16
|
+
}): PlainDateLike;
|
|
17
|
+
toString(): string;
|
|
18
|
+
}
|
|
19
|
+
/** The parts of a `Temporal.PlainDateTime` / `Temporal.ZonedDateTime` this library relies on. */
|
|
20
|
+
interface DateTimeLike {
|
|
21
|
+
toPlainDate(): PlainDateLike;
|
|
22
|
+
add(duration: {
|
|
23
|
+
days: number;
|
|
24
|
+
}): DateTimeLike;
|
|
25
|
+
}
|
|
26
|
+
/** The parts of a `Temporal.PlainMonthDay` this library relies on. */
|
|
27
|
+
interface PlainMonthDayLike {
|
|
28
|
+
readonly calendarId: string;
|
|
29
|
+
readonly monthCode: string;
|
|
30
|
+
readonly day: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Any Temporal value that has a calendar date: `Temporal.PlainDate`, `Temporal.PlainDateTime`
|
|
34
|
+
* or `Temporal.ZonedDateTime`. Every helper returns the same type it was given.
|
|
35
|
+
*/
|
|
36
|
+
type DateInput = PlainDateLike | DateTimeLike;
|
|
37
|
+
/** ISO day of week: 1 = Monday … 7 = Sunday (same as `Temporal.PlainDate#dayOfWeek`). */
|
|
38
|
+
type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
39
|
+
/**
|
|
40
|
+
* A holiday entry:
|
|
41
|
+
* - `'2026-12-25'` or a `Temporal.PlainDate` — a single date.
|
|
42
|
+
* - `'12-25'` or a `Temporal.PlainMonthDay` — every year on that ISO month/day.
|
|
43
|
+
*/
|
|
44
|
+
type HolidayInput = string | PlainDateLike | PlainMonthDayLike;
|
|
45
|
+
/**
|
|
46
|
+
* Decides whether a date is a holiday. Receives an ISO-calendar `Temporal.PlainDate`
|
|
47
|
+
* from the same Temporal implementation as the input date. Not called for weekend days.
|
|
48
|
+
*/
|
|
49
|
+
type HolidayPredicate = {
|
|
50
|
+
bivarianceHack(date: PlainDateLike): boolean;
|
|
51
|
+
}['bivarianceHack'];
|
|
52
|
+
interface WorkdayOptions {
|
|
53
|
+
/** Non-working days of the week. Default `[6, 7]` (Saturday, Sunday). */
|
|
54
|
+
weekend?: readonly DayOfWeek[];
|
|
55
|
+
/** Holiday dates, or a function that decides whether a date is a holiday. */
|
|
56
|
+
holidays?: Iterable<HolidayInput> | HolidayPredicate;
|
|
57
|
+
/**
|
|
58
|
+
* Dates that count as workdays even if they fall on the weekend or a holiday
|
|
59
|
+
* (for example, make-up working Saturdays). Single dates only.
|
|
60
|
+
*/
|
|
61
|
+
extraWorkdays?: Iterable<string | PlainDateLike>;
|
|
62
|
+
}
|
|
63
|
+
/** `true` if the date falls on a configured weekend day (ignores holidays and `extraWorkdays`). */
|
|
64
|
+
declare function isWeekend(date: DateInput, options?: WorkdayOptions): boolean;
|
|
65
|
+
/** `true` if the date is a configured holiday and not listed in `extraWorkdays`. */
|
|
66
|
+
declare function isHoliday(date: DateInput, options?: WorkdayOptions): boolean;
|
|
67
|
+
/** `true` if the date is a working day: not a weekend day and not a holiday, or listed in `extraWorkdays`. */
|
|
68
|
+
declare function isWorkday(date: DateInput, options?: WorkdayOptions): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Adds `amount` workdays (negative to go back). The result has the same type as the input,
|
|
71
|
+
* and time of day / time zone are preserved. `amount = 0` returns the input unchanged.
|
|
72
|
+
* Starting on a non-workday, `+1` gives the next workday.
|
|
73
|
+
*/
|
|
74
|
+
declare function addWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T;
|
|
75
|
+
/** Subtracts `amount` workdays. Same as `addWorkdays(date, -amount)`. */
|
|
76
|
+
declare function subtractWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T;
|
|
77
|
+
/** The first workday strictly after `date`. */
|
|
78
|
+
declare function nextWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
79
|
+
/** The last workday strictly before `date`. */
|
|
80
|
+
declare function previousWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
81
|
+
/** `date` itself if it is a workday, otherwise the next workday after it. */
|
|
82
|
+
declare function rollForward<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
83
|
+
/** `date` itself if it is a workday, otherwise the previous workday before it. */
|
|
84
|
+
declare function rollBackward<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
85
|
+
/**
|
|
86
|
+
* Number of workdays after `start` up to and including `end` — the range `(start, end]`.
|
|
87
|
+
* Negative when `end` is before `start`. Only calendar dates matter; times are ignored.
|
|
88
|
+
*
|
|
89
|
+
* For `amount >= 0`: `workdaysBetween(d, addWorkdays(d, amount)) === amount`.
|
|
90
|
+
*/
|
|
91
|
+
declare function workdaysBetween(start: DateInput, end: DateInput, options?: WorkdayOptions): number;
|
|
92
|
+
/**
|
|
93
|
+
* Yields every workday from `start` to `end`, both inclusive, in ascending order.
|
|
94
|
+
* Values have the type of `start` (time of day / zone preserved). Yields nothing if `end < start`.
|
|
95
|
+
*/
|
|
96
|
+
declare function eachWorkday<T extends DateInput>(start: T, end: DateInput, options?: WorkdayOptions): Generator<T>;
|
|
97
|
+
/** A set of workday helpers bound to one configuration. Holidays are parsed once. */
|
|
98
|
+
interface WorkCalendar {
|
|
99
|
+
readonly options: Readonly<WorkdayOptions>;
|
|
100
|
+
isWorkday(date: DateInput): boolean;
|
|
101
|
+
isWeekend(date: DateInput): boolean;
|
|
102
|
+
isHoliday(date: DateInput): boolean;
|
|
103
|
+
addWorkdays<T extends DateInput>(date: T, amount: number): T;
|
|
104
|
+
subtractWorkdays<T extends DateInput>(date: T, amount: number): T;
|
|
105
|
+
nextWorkday<T extends DateInput>(date: T): T;
|
|
106
|
+
previousWorkday<T extends DateInput>(date: T): T;
|
|
107
|
+
rollForward<T extends DateInput>(date: T): T;
|
|
108
|
+
rollBackward<T extends DateInput>(date: T): T;
|
|
109
|
+
workdaysBetween(start: DateInput, end: DateInput): number;
|
|
110
|
+
eachWorkday<T extends DateInput>(start: T, end: DateInput): Generator<T>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Creates helpers bound to one configuration. The options are validated and copied
|
|
114
|
+
* immediately, so later changes to the passed-in arrays don't affect the calendar.
|
|
115
|
+
*/
|
|
116
|
+
declare function createWorkCalendar(options?: WorkdayOptions): WorkCalendar;
|
|
117
|
+
|
|
118
|
+
export { addWorkdays, createWorkCalendar, eachWorkday, isHoliday, isWeekend, isWorkday, nextWorkday, previousWorkday, rollBackward, rollForward, subtractWorkdays, workdaysBetween };
|
|
119
|
+
export type { DateInput, DateTimeLike, DayOfWeek, HolidayInput, HolidayPredicate, PlainDateLike, PlainMonthDayLike, WorkCalendar, WorkdayOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts of a `Temporal.PlainDate` this library relies on. Described structurally so that
|
|
3
|
+
* native Temporal, `temporal-polyfill`, `@js-temporal/polyfill` and their TypeScript types all work,
|
|
4
|
+
* with no global `Temporal` type required.
|
|
5
|
+
*/
|
|
6
|
+
interface PlainDateLike {
|
|
7
|
+
readonly calendarId: string;
|
|
8
|
+
readonly year: number;
|
|
9
|
+
readonly month: number;
|
|
10
|
+
readonly monthCode: string;
|
|
11
|
+
readonly day: number;
|
|
12
|
+
readonly dayOfWeek: number;
|
|
13
|
+
withCalendar(calendar: 'iso8601'): PlainDateLike;
|
|
14
|
+
add(duration: {
|
|
15
|
+
days: number;
|
|
16
|
+
}): PlainDateLike;
|
|
17
|
+
toString(): string;
|
|
18
|
+
}
|
|
19
|
+
/** The parts of a `Temporal.PlainDateTime` / `Temporal.ZonedDateTime` this library relies on. */
|
|
20
|
+
interface DateTimeLike {
|
|
21
|
+
toPlainDate(): PlainDateLike;
|
|
22
|
+
add(duration: {
|
|
23
|
+
days: number;
|
|
24
|
+
}): DateTimeLike;
|
|
25
|
+
}
|
|
26
|
+
/** The parts of a `Temporal.PlainMonthDay` this library relies on. */
|
|
27
|
+
interface PlainMonthDayLike {
|
|
28
|
+
readonly calendarId: string;
|
|
29
|
+
readonly monthCode: string;
|
|
30
|
+
readonly day: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Any Temporal value that has a calendar date: `Temporal.PlainDate`, `Temporal.PlainDateTime`
|
|
34
|
+
* or `Temporal.ZonedDateTime`. Every helper returns the same type it was given.
|
|
35
|
+
*/
|
|
36
|
+
type DateInput = PlainDateLike | DateTimeLike;
|
|
37
|
+
/** ISO day of week: 1 = Monday … 7 = Sunday (same as `Temporal.PlainDate#dayOfWeek`). */
|
|
38
|
+
type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
39
|
+
/**
|
|
40
|
+
* A holiday entry:
|
|
41
|
+
* - `'2026-12-25'` or a `Temporal.PlainDate` — a single date.
|
|
42
|
+
* - `'12-25'` or a `Temporal.PlainMonthDay` — every year on that ISO month/day.
|
|
43
|
+
*/
|
|
44
|
+
type HolidayInput = string | PlainDateLike | PlainMonthDayLike;
|
|
45
|
+
/**
|
|
46
|
+
* Decides whether a date is a holiday. Receives an ISO-calendar `Temporal.PlainDate`
|
|
47
|
+
* from the same Temporal implementation as the input date. Not called for weekend days.
|
|
48
|
+
*/
|
|
49
|
+
type HolidayPredicate = {
|
|
50
|
+
bivarianceHack(date: PlainDateLike): boolean;
|
|
51
|
+
}['bivarianceHack'];
|
|
52
|
+
interface WorkdayOptions {
|
|
53
|
+
/** Non-working days of the week. Default `[6, 7]` (Saturday, Sunday). */
|
|
54
|
+
weekend?: readonly DayOfWeek[];
|
|
55
|
+
/** Holiday dates, or a function that decides whether a date is a holiday. */
|
|
56
|
+
holidays?: Iterable<HolidayInput> | HolidayPredicate;
|
|
57
|
+
/**
|
|
58
|
+
* Dates that count as workdays even if they fall on the weekend or a holiday
|
|
59
|
+
* (for example, make-up working Saturdays). Single dates only.
|
|
60
|
+
*/
|
|
61
|
+
extraWorkdays?: Iterable<string | PlainDateLike>;
|
|
62
|
+
}
|
|
63
|
+
/** `true` if the date falls on a configured weekend day (ignores holidays and `extraWorkdays`). */
|
|
64
|
+
declare function isWeekend(date: DateInput, options?: WorkdayOptions): boolean;
|
|
65
|
+
/** `true` if the date is a configured holiday and not listed in `extraWorkdays`. */
|
|
66
|
+
declare function isHoliday(date: DateInput, options?: WorkdayOptions): boolean;
|
|
67
|
+
/** `true` if the date is a working day: not a weekend day and not a holiday, or listed in `extraWorkdays`. */
|
|
68
|
+
declare function isWorkday(date: DateInput, options?: WorkdayOptions): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Adds `amount` workdays (negative to go back). The result has the same type as the input,
|
|
71
|
+
* and time of day / time zone are preserved. `amount = 0` returns the input unchanged.
|
|
72
|
+
* Starting on a non-workday, `+1` gives the next workday.
|
|
73
|
+
*/
|
|
74
|
+
declare function addWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T;
|
|
75
|
+
/** Subtracts `amount` workdays. Same as `addWorkdays(date, -amount)`. */
|
|
76
|
+
declare function subtractWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T;
|
|
77
|
+
/** The first workday strictly after `date`. */
|
|
78
|
+
declare function nextWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
79
|
+
/** The last workday strictly before `date`. */
|
|
80
|
+
declare function previousWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
81
|
+
/** `date` itself if it is a workday, otherwise the next workday after it. */
|
|
82
|
+
declare function rollForward<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
83
|
+
/** `date` itself if it is a workday, otherwise the previous workday before it. */
|
|
84
|
+
declare function rollBackward<T extends DateInput>(date: T, options?: WorkdayOptions): T;
|
|
85
|
+
/**
|
|
86
|
+
* Number of workdays after `start` up to and including `end` — the range `(start, end]`.
|
|
87
|
+
* Negative when `end` is before `start`. Only calendar dates matter; times are ignored.
|
|
88
|
+
*
|
|
89
|
+
* For `amount >= 0`: `workdaysBetween(d, addWorkdays(d, amount)) === amount`.
|
|
90
|
+
*/
|
|
91
|
+
declare function workdaysBetween(start: DateInput, end: DateInput, options?: WorkdayOptions): number;
|
|
92
|
+
/**
|
|
93
|
+
* Yields every workday from `start` to `end`, both inclusive, in ascending order.
|
|
94
|
+
* Values have the type of `start` (time of day / zone preserved). Yields nothing if `end < start`.
|
|
95
|
+
*/
|
|
96
|
+
declare function eachWorkday<T extends DateInput>(start: T, end: DateInput, options?: WorkdayOptions): Generator<T>;
|
|
97
|
+
/** A set of workday helpers bound to one configuration. Holidays are parsed once. */
|
|
98
|
+
interface WorkCalendar {
|
|
99
|
+
readonly options: Readonly<WorkdayOptions>;
|
|
100
|
+
isWorkday(date: DateInput): boolean;
|
|
101
|
+
isWeekend(date: DateInput): boolean;
|
|
102
|
+
isHoliday(date: DateInput): boolean;
|
|
103
|
+
addWorkdays<T extends DateInput>(date: T, amount: number): T;
|
|
104
|
+
subtractWorkdays<T extends DateInput>(date: T, amount: number): T;
|
|
105
|
+
nextWorkday<T extends DateInput>(date: T): T;
|
|
106
|
+
previousWorkday<T extends DateInput>(date: T): T;
|
|
107
|
+
rollForward<T extends DateInput>(date: T): T;
|
|
108
|
+
rollBackward<T extends DateInput>(date: T): T;
|
|
109
|
+
workdaysBetween(start: DateInput, end: DateInput): number;
|
|
110
|
+
eachWorkday<T extends DateInput>(start: T, end: DateInput): Generator<T>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Creates helpers bound to one configuration. The options are validated and copied
|
|
114
|
+
* immediately, so later changes to the passed-in arrays don't affect the calendar.
|
|
115
|
+
*/
|
|
116
|
+
declare function createWorkCalendar(options?: WorkdayOptions): WorkCalendar;
|
|
117
|
+
|
|
118
|
+
export { addWorkdays, createWorkCalendar, eachWorkday, isHoliday, isWeekend, isWorkday, nextWorkday, previousWorkday, rollBackward, rollForward, subtractWorkdays, workdaysBetween };
|
|
119
|
+
export type { DateInput, DateTimeLike, DayOfWeek, HolidayInput, HolidayPredicate, PlainDateLike, PlainMonthDayLike, WorkCalendar, WorkdayOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// src/epoch.ts
|
|
2
|
+
function epochDayFromIso(year, month, day) {
|
|
3
|
+
const y = month <= 2 ? year - 1 : year;
|
|
4
|
+
const era = Math.floor(y / 400);
|
|
5
|
+
const yoe = y - era * 400;
|
|
6
|
+
const mp = (month + 9) % 12;
|
|
7
|
+
const doy = Math.floor((153 * mp + 2) / 5) + day - 1;
|
|
8
|
+
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
9
|
+
return era * 146097 + doe - 719468;
|
|
10
|
+
}
|
|
11
|
+
function isoFromEpochDay(epochDay) {
|
|
12
|
+
const z = epochDay + 719468;
|
|
13
|
+
const era = Math.floor(z / 146097);
|
|
14
|
+
const doe = z - era * 146097;
|
|
15
|
+
const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);
|
|
16
|
+
const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));
|
|
17
|
+
const mp = Math.floor((5 * doy + 2) / 153);
|
|
18
|
+
const day = doy - Math.floor((153 * mp + 2) / 5) + 1;
|
|
19
|
+
const month = mp < 10 ? mp + 3 : mp - 9;
|
|
20
|
+
const year = yoe + era * 400 + (month <= 2 ? 1 : 0);
|
|
21
|
+
return [year, month, day];
|
|
22
|
+
}
|
|
23
|
+
function dayOfWeekFromEpochDay(epochDay) {
|
|
24
|
+
return ((epochDay + 3) % 7 + 7) % 7 + 1;
|
|
25
|
+
}
|
|
26
|
+
function isLeapYear(year) {
|
|
27
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
28
|
+
}
|
|
29
|
+
function daysInMonth(year, month) {
|
|
30
|
+
if (month === 2) return isLeapYear(year) ? 29 : 28;
|
|
31
|
+
return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/index.ts
|
|
35
|
+
var MAX_SCAN_DAYS = 366 * 20;
|
|
36
|
+
var DATE_RE = /^([+-]?\d{4,6})-(\d{2})-(\d{2})$/;
|
|
37
|
+
var MONTH_DAY_RE = /^(?:--)?(\d{2})-(\d{2})$/;
|
|
38
|
+
function checkDay(year, month, day, source) {
|
|
39
|
+
if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) {
|
|
40
|
+
throw new RangeError(`temporal-workdays: invalid date ${JSON.stringify(String(source))}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function epochDayOfPlainDate(date) {
|
|
44
|
+
const iso = date.calendarId === "iso8601" ? date : date.withCalendar("iso8601");
|
|
45
|
+
return epochDayFromIso(iso.year, iso.month, iso.day);
|
|
46
|
+
}
|
|
47
|
+
function isTemporalLike(value, tag) {
|
|
48
|
+
return typeof value === "object" && value !== null && Object.prototype.toString.call(value) === `[object ${tag}]`;
|
|
49
|
+
}
|
|
50
|
+
function parseSingleDate(value) {
|
|
51
|
+
if (typeof value === "string") {
|
|
52
|
+
const m = DATE_RE.exec(value);
|
|
53
|
+
if (!m) throw new RangeError(`temporal-workdays: expected "YYYY-MM-DD", got ${JSON.stringify(value)}`);
|
|
54
|
+
const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
55
|
+
checkDay(y, mo, d, value);
|
|
56
|
+
return epochDayFromIso(y, mo, d);
|
|
57
|
+
}
|
|
58
|
+
if (isTemporalLike(value, "Temporal.PlainDate")) return epochDayOfPlainDate(value);
|
|
59
|
+
throw new TypeError(`temporal-workdays: expected a "YYYY-MM-DD" string or Temporal.PlainDate, got ${String(value)}`);
|
|
60
|
+
}
|
|
61
|
+
function compile(options) {
|
|
62
|
+
const weekendDays = options?.weekend ?? [6, 7];
|
|
63
|
+
const weekend = new Array(8).fill(false);
|
|
64
|
+
for (const d of weekendDays) {
|
|
65
|
+
if (!Number.isInteger(d) || d < 1 || d > 7) {
|
|
66
|
+
throw new RangeError(`temporal-workdays: weekend days must be integers 1 (Mon) \u2013 7 (Sun), got ${String(d)}`);
|
|
67
|
+
}
|
|
68
|
+
weekend[d] = true;
|
|
69
|
+
}
|
|
70
|
+
const dates = /* @__PURE__ */ new Set();
|
|
71
|
+
const annual = /* @__PURE__ */ new Set();
|
|
72
|
+
let predicate;
|
|
73
|
+
const holidays = options?.holidays;
|
|
74
|
+
if (typeof holidays === "function") {
|
|
75
|
+
predicate = holidays;
|
|
76
|
+
} else if (holidays != null) {
|
|
77
|
+
for (const h of holidays) {
|
|
78
|
+
if (typeof h === "string") {
|
|
79
|
+
const md = MONTH_DAY_RE.exec(h);
|
|
80
|
+
if (md) {
|
|
81
|
+
const [mo, d] = [Number(md[1]), Number(md[2])];
|
|
82
|
+
checkDay(2e3, mo, d, h);
|
|
83
|
+
annual.add(mo * 100 + d);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
dates.add(parseSingleDate(h));
|
|
87
|
+
} else if (isTemporalLike(h, "Temporal.PlainMonthDay")) {
|
|
88
|
+
const pmd = h;
|
|
89
|
+
if (pmd.calendarId !== "iso8601") {
|
|
90
|
+
throw new RangeError("temporal-workdays: Temporal.PlainMonthDay holidays must use the iso8601 calendar");
|
|
91
|
+
}
|
|
92
|
+
annual.add(Number(pmd.monthCode.slice(1)) * 100 + pmd.day);
|
|
93
|
+
} else {
|
|
94
|
+
dates.add(parseSingleDate(h));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const extra = /* @__PURE__ */ new Set();
|
|
99
|
+
if (options?.extraWorkdays != null) {
|
|
100
|
+
for (const e of options.extraWorkdays) extra.add(parseSingleDate(e));
|
|
101
|
+
}
|
|
102
|
+
return { weekend, dates, annual, predicate, extra };
|
|
103
|
+
}
|
|
104
|
+
var cache = /* @__PURE__ */ new WeakMap();
|
|
105
|
+
var DEFAULT = compile(void 0);
|
|
106
|
+
function getCompiled(options) {
|
|
107
|
+
if (options === void 0) return DEFAULT;
|
|
108
|
+
let c = cache.get(options);
|
|
109
|
+
if (!c) {
|
|
110
|
+
c = compile(options);
|
|
111
|
+
cache.set(options, c);
|
|
112
|
+
}
|
|
113
|
+
return c;
|
|
114
|
+
}
|
|
115
|
+
function toPlainDate(date) {
|
|
116
|
+
if (date == null || typeof date.add !== "function") {
|
|
117
|
+
throw new TypeError("temporal-workdays: expected a Temporal.PlainDate, PlainDateTime or ZonedDateTime");
|
|
118
|
+
}
|
|
119
|
+
return "toPlainDate" in date ? date.toPlainDate() : date;
|
|
120
|
+
}
|
|
121
|
+
function isHolidayDay(c, epochDay, base, baseDay) {
|
|
122
|
+
if (c.dates.has(epochDay)) return true;
|
|
123
|
+
if (c.annual.size > 0) {
|
|
124
|
+
const [, mo, d] = isoFromEpochDay(epochDay);
|
|
125
|
+
if (c.annual.has(mo * 100 + d)) return true;
|
|
126
|
+
}
|
|
127
|
+
if (c.predicate) {
|
|
128
|
+
const iso = base.calendarId === "iso8601" ? base : base.withCalendar("iso8601");
|
|
129
|
+
return c.predicate(epochDay === baseDay ? iso : iso.add({ days: epochDay - baseDay }));
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
function isWorkdayDay(c, epochDay, base, baseDay) {
|
|
134
|
+
if (c.extra.has(epochDay)) return true;
|
|
135
|
+
if (c.weekend[dayOfWeekFromEpochDay(epochDay)]) return false;
|
|
136
|
+
return !isHolidayDay(c, epochDay, base, baseDay);
|
|
137
|
+
}
|
|
138
|
+
function checkInteger(n, name) {
|
|
139
|
+
if (!Number.isInteger(n)) throw new RangeError(`temporal-workdays: ${name} must be an integer, got ${String(n)}`);
|
|
140
|
+
}
|
|
141
|
+
function noWorkdayError() {
|
|
142
|
+
return new RangeError(
|
|
143
|
+
`temporal-workdays: no workday found within ${MAX_SCAN_DAYS} days \u2014 check your weekend/holiday configuration`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
function step(c, base, count, dir) {
|
|
147
|
+
const baseDay = epochDayOfPlainDate(base);
|
|
148
|
+
let day = baseDay;
|
|
149
|
+
let remaining = count;
|
|
150
|
+
let gap = 0;
|
|
151
|
+
while (remaining > 0) {
|
|
152
|
+
day += dir;
|
|
153
|
+
if (isWorkdayDay(c, day, base, baseDay)) {
|
|
154
|
+
remaining--;
|
|
155
|
+
gap = 0;
|
|
156
|
+
} else if (++gap > MAX_SCAN_DAYS) {
|
|
157
|
+
throw noWorkdayError();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return day - baseDay;
|
|
161
|
+
}
|
|
162
|
+
function shift(date, days) {
|
|
163
|
+
return days === 0 ? date : date.add({ days });
|
|
164
|
+
}
|
|
165
|
+
function isWeekend(date, options) {
|
|
166
|
+
return getCompiled(options).weekend[toPlainDate(date).dayOfWeek] === true;
|
|
167
|
+
}
|
|
168
|
+
function isHoliday(date, options) {
|
|
169
|
+
const c = getCompiled(options);
|
|
170
|
+
const base = toPlainDate(date);
|
|
171
|
+
const day = epochDayOfPlainDate(base);
|
|
172
|
+
return !c.extra.has(day) && isHolidayDay(c, day, base, day);
|
|
173
|
+
}
|
|
174
|
+
function isWorkday(date, options) {
|
|
175
|
+
const c = getCompiled(options);
|
|
176
|
+
const base = toPlainDate(date);
|
|
177
|
+
const day = epochDayOfPlainDate(base);
|
|
178
|
+
return isWorkdayDay(c, day, base, day);
|
|
179
|
+
}
|
|
180
|
+
function addWorkdays(date, amount, options) {
|
|
181
|
+
checkInteger(amount, "amount");
|
|
182
|
+
if (amount === 0) return date;
|
|
183
|
+
const c = getCompiled(options);
|
|
184
|
+
return shift(date, step(c, toPlainDate(date), Math.abs(amount), amount > 0 ? 1 : -1));
|
|
185
|
+
}
|
|
186
|
+
function subtractWorkdays(date, amount, options) {
|
|
187
|
+
checkInteger(amount, "amount");
|
|
188
|
+
return addWorkdays(date, -amount, options);
|
|
189
|
+
}
|
|
190
|
+
function nextWorkday(date, options) {
|
|
191
|
+
return shift(date, step(getCompiled(options), toPlainDate(date), 1, 1));
|
|
192
|
+
}
|
|
193
|
+
function previousWorkday(date, options) {
|
|
194
|
+
return shift(date, step(getCompiled(options), toPlainDate(date), 1, -1));
|
|
195
|
+
}
|
|
196
|
+
function rollForward(date, options) {
|
|
197
|
+
return isWorkday(date, options) ? date : nextWorkday(date, options);
|
|
198
|
+
}
|
|
199
|
+
function rollBackward(date, options) {
|
|
200
|
+
return isWorkday(date, options) ? date : previousWorkday(date, options);
|
|
201
|
+
}
|
|
202
|
+
function workdaysBetween(start, end, options) {
|
|
203
|
+
const c = getCompiled(options);
|
|
204
|
+
const a = toPlainDate(start);
|
|
205
|
+
const b = toPlainDate(end);
|
|
206
|
+
const aDay = epochDayOfPlainDate(a);
|
|
207
|
+
const bDay = epochDayOfPlainDate(b);
|
|
208
|
+
if (aDay === bDay) return 0;
|
|
209
|
+
const [lo, hi, base, baseDay, sign] = aDay < bDay ? [aDay, bDay, a, aDay, 1] : [bDay, aDay, b, bDay, -1];
|
|
210
|
+
let count = 0;
|
|
211
|
+
for (let day = lo + 1; day <= hi; day++) {
|
|
212
|
+
if (isWorkdayDay(c, day, base, baseDay)) count++;
|
|
213
|
+
}
|
|
214
|
+
return sign * count;
|
|
215
|
+
}
|
|
216
|
+
function* eachWorkday(start, end, options) {
|
|
217
|
+
const c = getCompiled(options);
|
|
218
|
+
const base = toPlainDate(start);
|
|
219
|
+
const baseDay = epochDayOfPlainDate(base);
|
|
220
|
+
const endDay = epochDayOfPlainDate(toPlainDate(end));
|
|
221
|
+
for (let day = baseDay; day <= endDay; day++) {
|
|
222
|
+
if (isWorkdayDay(c, day, base, baseDay)) yield shift(start, day - baseDay);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function createWorkCalendar(options = {}) {
|
|
226
|
+
const frozen = Object.freeze({
|
|
227
|
+
weekend: options.weekend ? Object.freeze([...options.weekend]) : void 0,
|
|
228
|
+
holidays: typeof options.holidays === "function" || options.holidays == null ? options.holidays : Object.freeze([...options.holidays]),
|
|
229
|
+
extraWorkdays: options.extraWorkdays ? Object.freeze([...options.extraWorkdays]) : void 0
|
|
230
|
+
});
|
|
231
|
+
cache.set(frozen, compile(frozen));
|
|
232
|
+
return Object.freeze({
|
|
233
|
+
options: frozen,
|
|
234
|
+
isWorkday: (d) => isWorkday(d, frozen),
|
|
235
|
+
isWeekend: (d) => isWeekend(d, frozen),
|
|
236
|
+
isHoliday: (d) => isHoliday(d, frozen),
|
|
237
|
+
addWorkdays: (d, n) => addWorkdays(d, n, frozen),
|
|
238
|
+
subtractWorkdays: (d, n) => subtractWorkdays(d, n, frozen),
|
|
239
|
+
nextWorkday: (d) => nextWorkday(d, frozen),
|
|
240
|
+
previousWorkday: (d) => previousWorkday(d, frozen),
|
|
241
|
+
rollForward: (d) => rollForward(d, frozen),
|
|
242
|
+
rollBackward: (d) => rollBackward(d, frozen),
|
|
243
|
+
workdaysBetween: (a, b) => workdaysBetween(a, b, frozen),
|
|
244
|
+
eachWorkday: (a, b) => eachWorkday(a, b, frozen)
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export { addWorkdays, createWorkCalendar, eachWorkday, isHoliday, isWeekend, isWorkday, nextWorkday, previousWorkday, rollBackward, rollForward, subtractWorkdays, workdaysBetween };
|
|
249
|
+
//# sourceMappingURL=index.js.map
|
|
250
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/epoch.ts","../src/index.ts"],"names":[],"mappings":";AAMO,SAAS,eAAA,CAAgB,IAAA,EAAc,KAAA,EAAe,GAAA,EAAqB;AAChF,EAAA,MAAM,CAAA,GAAI,KAAA,IAAS,CAAA,GAAI,IAAA,GAAO,CAAA,GAAI,IAAA;AAClC,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,GAAG,CAAA;AAC9B,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,GAAM,GAAA;AACtB,EAAA,MAAM,EAAA,GAAA,CAAM,QAAQ,CAAA,IAAK,EAAA;AACzB,EAAA,MAAM,GAAA,GAAM,KAAK,KAAA,CAAA,CAAO,GAAA,GAAM,KAAK,CAAA,IAAK,CAAC,IAAI,GAAA,GAAM,CAAA;AACnD,EAAA,MAAM,GAAA,GAAM,GAAA,GAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAG,CAAA,GAAI,GAAA;AACtE,EAAA,OAAO,GAAA,GAAM,SAAS,GAAA,GAAM,MAAA;AAC9B;AAGO,SAAS,gBAAgB,QAAA,EAA4C;AAC1E,EAAA,MAAM,IAAI,QAAA,GAAW,MAAA;AACrB,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,CAAA,GAAI,MAAM,CAAA;AACjC,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,GAAM,MAAA;AACtB,EAAA,MAAM,MAAM,IAAA,CAAK,KAAA,CAAA,CAAO,MAAM,IAAA,CAAK,KAAA,CAAM,MAAM,IAAI,CAAA,GAAI,KAAK,KAAA,CAAM,GAAA,GAAM,KAAK,CAAA,GAAI,IAAA,CAAK,MAAM,GAAA,GAAM,MAAM,KAAK,GAAG,CAAA;AAChH,EAAA,MAAM,GAAA,GAAM,GAAA,IAAO,GAAA,GAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,GAAA,GAAM,GAAG,CAAA,CAAA;AACzE,EAAA,MAAM,KAAK,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,GAAI,GAAA,GAAM,KAAK,GAAG,CAAA;AACzC,EAAA,MAAM,GAAA,GAAM,MAAM,IAAA,CAAK,KAAA,CAAA,CAAO,MAAM,EAAA,GAAK,CAAA,IAAK,CAAC,CAAA,GAAI,CAAA;AACnD,EAAA,MAAM,KAAA,GAAQ,EAAA,GAAK,EAAA,GAAK,EAAA,GAAK,IAAI,EAAA,GAAK,CAAA;AACtC,EAAA,MAAM,OAAO,GAAA,GAAM,GAAA,GAAM,GAAA,IAAO,KAAA,IAAS,IAAI,CAAA,GAAI,CAAA,CAAA;AACjD,EAAA,OAAO,CAAC,IAAA,EAAM,KAAA,EAAO,GAAG,CAAA;AAC1B;AAGO,SAAS,sBAAsB,QAAA,EAA0B;AAC9D,EAAA,OAAA,CAAA,CAAW,QAAA,GAAW,CAAA,IAAK,CAAA,GAAK,CAAA,IAAK,CAAA,GAAK,CAAA;AAC5C;AAEO,SAAS,WAAW,IAAA,EAAuB;AAChD,EAAA,OAAQ,OAAO,CAAA,KAAM,CAAA,IAAK,OAAO,GAAA,KAAQ,CAAA,IAAM,OAAO,GAAA,KAAQ,CAAA;AAChE;AAEO,SAAS,WAAA,CAAY,MAAc,KAAA,EAAuB;AAC/D,EAAA,IAAI,UAAU,CAAA,EAAG,OAAO,UAAA,CAAW,IAAI,IAAI,EAAA,GAAK,EAAA;AAChD,EAAA,OAAO,KAAA,KAAU,KAAK,KAAA,KAAU,CAAA,IAAK,UAAU,CAAA,IAAK,KAAA,KAAU,KAAK,EAAA,GAAK,EAAA;AAC1E;;;AC0BA,IAAM,gBAAgB,GAAA,GAAM,EAAA;AAU5B,IAAM,OAAA,GAAU,kCAAA;AAChB,IAAM,YAAA,GAAe,0BAAA;AAErB,SAAS,QAAA,CAAS,IAAA,EAAc,KAAA,EAAe,GAAA,EAAa,MAAA,EAAuB;AACjF,EAAA,IAAI,KAAA,GAAQ,CAAA,IAAK,KAAA,GAAQ,EAAA,IAAM,GAAA,GAAM,KAAK,GAAA,GAAM,WAAA,CAAY,IAAA,EAAM,KAAK,CAAA,EAAG;AACxE,IAAA,MAAM,IAAI,WAAW,CAAA,gCAAA,EAAmC,IAAA,CAAK,UAAU,MAAA,CAAO,MAAM,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,EAC1F;AACF;AAEA,SAAS,oBAAoB,IAAA,EAA6B;AACxD,EAAA,MAAM,MAAM,IAAA,CAAK,UAAA,KAAe,YAAY,IAAA,GAAO,IAAA,CAAK,aAAa,SAAS,CAAA;AAC9E,EAAA,OAAO,gBAAgB,GAAA,CAAI,IAAA,EAAM,GAAA,CAAI,KAAA,EAAO,IAAI,GAAG,CAAA;AACrD;AAEA,SAAS,cAAA,CAAe,OAAgB,GAAA,EAAsB;AAC5D,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,IAAQ,MAAA,CAAO,SAAA,CAAU,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA,KAAM,CAAA,QAAA,EAAW,GAAG,CAAA,CAAA,CAAA;AAChH;AAEA,SAAS,gBAAgB,KAAA,EAAwB;AAC/C,EAAA,IAAI,OAAO,UAAU,QAAA,EAAU;AAC7B,IAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA;AAC5B,IAAA,IAAI,CAAC,CAAA,EAAG,MAAM,IAAI,UAAA,CAAW,iDAAiD,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,CAAE,CAAA;AACrG,IAAA,MAAM,CAAC,GAAG,EAAA,EAAI,CAAC,IAAI,CAAC,MAAA,CAAO,EAAE,CAAC,CAAC,GAAG,MAAA,CAAO,CAAA,CAAE,CAAC,CAAC,CAAA,EAAG,OAAO,CAAA,CAAE,CAAC,CAAC,CAAC,CAAA;AAC5D,IAAA,QAAA,CAAS,CAAA,EAAG,EAAA,EAAI,CAAA,EAAG,KAAK,CAAA;AACxB,IAAA,OAAO,eAAA,CAAgB,CAAA,EAAG,EAAA,EAAI,CAAC,CAAA;AAAA,EACjC;AACA,EAAA,IAAI,eAAe,KAAA,EAAO,oBAAoB,CAAA,EAAG,OAAO,oBAAoB,KAAsB,CAAA;AAClG,EAAA,MAAM,IAAI,SAAA,CAAU,CAAA,6EAAA,EAAgF,MAAA,CAAO,KAAK,CAAC,CAAA,CAAE,CAAA;AACrH;AAEA,SAAS,QAAQ,OAAA,EAA+C;AAC9D,EAAA,MAAM,WAAA,GAAc,OAAA,EAAS,OAAA,IAAW,CAAC,GAAG,CAAC,CAAA;AAC7C,EAAA,MAAM,UAAU,IAAI,KAAA,CAAe,CAAC,CAAA,CAAE,KAAK,KAAK,CAAA;AAChD,EAAA,KAAA,MAAW,KAAK,WAAA,EAAa;AAC3B,IAAA,IAAI,CAAC,OAAO,SAAA,CAAU,CAAC,KAAK,CAAA,GAAI,CAAA,IAAK,IAAI,CAAA,EAAG;AAC1C,MAAA,MAAM,IAAI,UAAA,CAAW,CAAA,6EAAA,EAA2E,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,IAC7G;AACA,IAAA,OAAA,CAAQ,CAAC,CAAA,GAAI,IAAA;AAAA,EACf;AAEA,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,EAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAC/B,EAAA,IAAI,SAAA;AACJ,EAAA,MAAM,WAAW,OAAA,EAAS,QAAA;AAE1B,EAAA,IAAI,OAAO,aAAa,UAAA,EAAY;AAClC,IAAA,SAAA,GAAY,QAAA;AAAA,EACd,CAAA,MAAA,IAAW,YAAY,IAAA,EAAM;AAC3B,IAAA,KAAA,MAAW,KAAK,QAAA,EAAU;AACxB,MAAA,IAAI,OAAO,MAAM,QAAA,EAAU;AACzB,QAAA,MAAM,EAAA,GAAK,YAAA,CAAa,IAAA,CAAK,CAAC,CAAA;AAC9B,QAAA,IAAI,EAAA,EAAI;AACN,UAAA,MAAM,CAAC,EAAA,EAAI,CAAC,CAAA,GAAI,CAAC,MAAA,CAAO,EAAA,CAAG,CAAC,CAAC,CAAA,EAAG,MAAA,CAAO,EAAA,CAAG,CAAC,CAAC,CAAC,CAAA;AAC7C,UAAA,QAAA,CAAS,GAAA,EAAM,EAAA,EAAI,CAAA,EAAG,CAAC,CAAA;AACvB,UAAA,MAAA,CAAO,GAAA,CAAI,EAAA,GAAK,GAAA,GAAM,CAAC,CAAA;AACvB,UAAA;AAAA,QACF;AACA,QAAA,KAAA,CAAM,GAAA,CAAI,eAAA,CAAgB,CAAC,CAAC,CAAA;AAAA,MAC9B,CAAA,MAAA,IAAW,cAAA,CAAe,CAAA,EAAG,wBAAwB,CAAA,EAAG;AACtD,QAAA,MAAM,GAAA,GAAM,CAAA;AACZ,QAAA,IAAI,GAAA,CAAI,eAAe,SAAA,EAAW;AAChC,UAAA,MAAM,IAAI,WAAW,kFAAkF,CAAA;AAAA,QACzG;AACA,QAAA,MAAA,CAAO,GAAA,CAAI,MAAA,CAAO,GAAA,CAAI,SAAA,CAAU,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,GAAA,GAAM,GAAA,CAAI,GAAG,CAAA;AAAA,MAC3D,CAAA,MAAO;AACL,QAAA,KAAA,CAAM,GAAA,CAAI,eAAA,CAAgB,CAAC,CAAC,CAAA;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,EAAA,IAAI,OAAA,EAAS,iBAAiB,IAAA,EAAM;AAClC,IAAA,KAAA,MAAW,KAAK,OAAA,CAAQ,aAAA,QAAqB,GAAA,CAAI,eAAA,CAAgB,CAAC,CAAC,CAAA;AAAA,EACrE;AAEA,EAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,WAAW,KAAA,EAAM;AACpD;AAEA,IAAM,KAAA,uBAAY,OAAA,EAA0B;AAC5C,IAAM,OAAA,GAAU,QAAQ,MAAS,CAAA;AAEjC,SAAS,YAAY,OAAA,EAA+C;AAClE,EAAA,IAAI,OAAA,KAAY,QAAW,OAAO,OAAA;AAClC,EAAA,IAAI,CAAA,GAAI,KAAA,CAAM,GAAA,CAAI,OAAO,CAAA;AACzB,EAAA,IAAI,CAAC,CAAA,EAAG;AACN,IAAA,CAAA,GAAI,QAAQ,OAAO,CAAA;AACnB,IAAA,KAAA,CAAM,GAAA,CAAI,SAAS,CAAC,CAAA;AAAA,EACtB;AACA,EAAA,OAAO,CAAA;AACT;AAGA,SAAS,YAAY,IAAA,EAAgC;AACnD,EAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,OAAQ,IAAA,CAA2B,QAAQ,UAAA,EAAY;AACzE,IAAA,MAAM,IAAI,UAAU,kFAAkF,CAAA;AAAA,EACxG;AACA,EAAA,OAAO,aAAA,IAAiB,IAAA,GAAO,IAAA,CAAK,WAAA,EAAY,GAAI,IAAA;AACtD;AAEA,SAAS,YAAA,CAAa,CAAA,EAAa,QAAA,EAAkB,IAAA,EAAqB,OAAA,EAA0B;AAClG,EAAA,IAAI,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,QAAQ,GAAG,OAAO,IAAA;AAClC,EAAA,IAAI,CAAA,CAAE,MAAA,CAAO,IAAA,GAAO,CAAA,EAAG;AACrB,IAAA,MAAM,GAAG,EAAA,EAAI,CAAC,CAAA,GAAI,gBAAgB,QAAQ,CAAA;AAC1C,IAAA,IAAI,EAAE,MAAA,CAAO,GAAA,CAAI,KAAK,GAAA,GAAM,CAAC,GAAG,OAAO,IAAA;AAAA,EACzC;AACA,EAAA,IAAI,EAAE,SAAA,EAAW;AACf,IAAA,MAAM,MAAM,IAAA,CAAK,UAAA,KAAe,YAAY,IAAA,GAAO,IAAA,CAAK,aAAa,SAAS,CAAA;AAC9E,IAAA,OAAO,CAAA,CAAE,SAAA,CAAU,QAAA,KAAa,OAAA,GAAU,GAAA,GAAM,GAAA,CAAI,GAAA,CAAI,EAAE,IAAA,EAAM,QAAA,GAAW,OAAA,EAAS,CAAC,CAAA;AAAA,EACvF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,YAAA,CAAa,CAAA,EAAa,QAAA,EAAkB,IAAA,EAAqB,OAAA,EAA0B;AAClG,EAAA,IAAI,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,QAAQ,GAAG,OAAO,IAAA;AAClC,EAAA,IAAI,EAAE,OAAA,CAAQ,qBAAA,CAAsB,QAAQ,CAAC,GAAG,OAAO,KAAA;AACvD,EAAA,OAAO,CAAC,YAAA,CAAa,CAAA,EAAG,QAAA,EAAU,MAAM,OAAO,CAAA;AACjD;AAEA,SAAS,YAAA,CAAa,GAAW,IAAA,EAAoB;AACnD,EAAA,IAAI,CAAC,MAAA,CAAO,SAAA,CAAU,CAAC,GAAG,MAAM,IAAI,UAAA,CAAW,CAAA,mBAAA,EAAsB,IAAI,CAAA,yBAAA,EAA4B,MAAA,CAAO,CAAC,CAAC,CAAA,CAAE,CAAA;AAClH;AAEA,SAAS,cAAA,GAA6B;AACpC,EAAA,OAAO,IAAI,UAAA;AAAA,IACT,8CAA8C,aAAa,CAAA,qDAAA;AAAA,GAC7D;AACF;AAGA,SAAS,IAAA,CAAK,CAAA,EAAa,IAAA,EAAqB,KAAA,EAAe,GAAA,EAAqB;AAClF,EAAA,MAAM,OAAA,GAAU,oBAAoB,IAAI,CAAA;AACxC,EAAA,IAAI,GAAA,GAAM,OAAA;AACV,EAAA,IAAI,SAAA,GAAY,KAAA;AAChB,EAAA,IAAI,GAAA,GAAM,CAAA;AACV,EAAA,OAAO,YAAY,CAAA,EAAG;AACpB,IAAA,GAAA,IAAO,GAAA;AACP,IAAA,IAAI,YAAA,CAAa,CAAA,EAAG,GAAA,EAAK,IAAA,EAAM,OAAO,CAAA,EAAG;AACvC,MAAA,SAAA,EAAA;AACA,MAAA,GAAA,GAAM,CAAA;AAAA,IACR,CAAA,MAAA,IAAW,EAAE,GAAA,GAAM,aAAA,EAAe;AAChC,MAAA,MAAM,cAAA,EAAe;AAAA,IACvB;AAAA,EACF;AACA,EAAA,OAAO,GAAA,GAAM,OAAA;AACf;AAEA,SAAS,KAAA,CAA2B,MAAS,IAAA,EAAiB;AAC5D,EAAA,OAAQ,SAAS,CAAA,GAAI,IAAA,GAAO,KAAK,GAAA,CAAI,EAAE,MAAM,CAAA;AAC/C;AAOO,SAAS,SAAA,CAAU,MAAiB,OAAA,EAAmC;AAC5E,EAAA,OAAO,WAAA,CAAY,OAAO,CAAA,CAAE,OAAA,CAAQ,YAAY,IAAI,CAAA,CAAE,SAAS,CAAA,KAAM,IAAA;AACvE;AAGO,SAAS,SAAA,CAAU,MAAiB,OAAA,EAAmC;AAC5E,EAAA,MAAM,CAAA,GAAI,YAAY,OAAO,CAAA;AAC7B,EAAA,MAAM,IAAA,GAAO,YAAY,IAAI,CAAA;AAC7B,EAAA,MAAM,GAAA,GAAM,oBAAoB,IAAI,CAAA;AACpC,EAAA,OAAO,CAAC,CAAA,CAAE,KAAA,CAAM,GAAA,CAAI,GAAG,KAAK,YAAA,CAAa,CAAA,EAAG,GAAA,EAAK,IAAA,EAAM,GAAG,CAAA;AAC5D;AAGO,SAAS,SAAA,CAAU,MAAiB,OAAA,EAAmC;AAC5E,EAAA,MAAM,CAAA,GAAI,YAAY,OAAO,CAAA;AAC7B,EAAA,MAAM,IAAA,GAAO,YAAY,IAAI,CAAA;AAC7B,EAAA,MAAM,GAAA,GAAM,oBAAoB,IAAI,CAAA;AACpC,EAAA,OAAO,YAAA,CAAa,CAAA,EAAG,GAAA,EAAK,IAAA,EAAM,GAAG,CAAA;AACvC;AAOO,SAAS,WAAA,CAAiC,IAAA,EAAS,MAAA,EAAgB,OAAA,EAA6B;AACrG,EAAA,YAAA,CAAa,QAAQ,QAAQ,CAAA;AAC7B,EAAA,IAAI,MAAA,KAAW,GAAG,OAAO,IAAA;AACzB,EAAA,MAAM,CAAA,GAAI,YAAY,OAAO,CAAA;AAC7B,EAAA,OAAO,KAAA,CAAM,IAAA,EAAM,IAAA,CAAK,CAAA,EAAG,YAAY,IAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,EAAG,MAAA,GAAS,CAAA,GAAI,CAAA,GAAI,EAAE,CAAC,CAAA;AACtF;AAGO,SAAS,gBAAA,CAAsC,IAAA,EAAS,MAAA,EAAgB,OAAA,EAA6B;AAC1G,EAAA,YAAA,CAAa,QAAQ,QAAQ,CAAA;AAC7B,EAAA,OAAO,WAAA,CAAY,IAAA,EAAM,CAAC,MAAA,EAAQ,OAAO,CAAA;AAC3C;AAGO,SAAS,WAAA,CAAiC,MAAS,OAAA,EAA6B;AACrF,EAAA,OAAO,KAAA,CAAM,IAAA,EAAM,IAAA,CAAK,WAAA,CAAY,OAAO,CAAA,EAAG,WAAA,CAAY,IAAI,CAAA,EAAG,CAAA,EAAG,CAAC,CAAC,CAAA;AACxE;AAGO,SAAS,eAAA,CAAqC,MAAS,OAAA,EAA6B;AACzF,EAAA,OAAO,KAAA,CAAM,IAAA,EAAM,IAAA,CAAK,WAAA,CAAY,OAAO,CAAA,EAAG,WAAA,CAAY,IAAI,CAAA,EAAG,CAAA,EAAG,EAAE,CAAC,CAAA;AACzE;AAGO,SAAS,WAAA,CAAiC,MAAS,OAAA,EAA6B;AACrF,EAAA,OAAO,UAAU,IAAA,EAAM,OAAO,IAAI,IAAA,GAAO,WAAA,CAAY,MAAM,OAAO,CAAA;AACpE;AAGO,SAAS,YAAA,CAAkC,MAAS,OAAA,EAA6B;AACtF,EAAA,OAAO,UAAU,IAAA,EAAM,OAAO,IAAI,IAAA,GAAO,eAAA,CAAgB,MAAM,OAAO,CAAA;AACxE;AAQO,SAAS,eAAA,CAAgB,KAAA,EAAkB,GAAA,EAAgB,OAAA,EAAkC;AAClG,EAAA,MAAM,CAAA,GAAI,YAAY,OAAO,CAAA;AAC7B,EAAA,MAAM,CAAA,GAAI,YAAY,KAAK,CAAA;AAC3B,EAAA,MAAM,CAAA,GAAI,YAAY,GAAG,CAAA;AACzB,EAAA,MAAM,IAAA,GAAO,oBAAoB,CAAC,CAAA;AAClC,EAAA,MAAM,IAAA,GAAO,oBAAoB,CAAC,CAAA;AAClC,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,CAAA;AAC1B,EAAA,MAAM,CAAC,IAAI,EAAA,EAAI,IAAA,EAAM,SAAS,IAAI,CAAA,GAAI,OAAO,IAAA,GAAO,CAAC,MAAM,IAAA,EAAM,CAAA,EAAG,MAAM,CAAC,CAAA,GAAI,CAAC,IAAA,EAAM,IAAA,EAAM,CAAA,EAAG,IAAA,EAAM,EAAE,CAAA;AACvG,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,GAAA,GAAM,EAAA,GAAK,CAAA,EAAG,GAAA,IAAO,IAAI,GAAA,EAAA,EAAO;AACvC,IAAA,IAAI,YAAA,CAAa,CAAA,EAAG,GAAA,EAAK,IAAA,EAAM,OAAO,CAAA,EAAG,KAAA,EAAA;AAAA,EAC3C;AACA,EAAA,OAAO,IAAA,GAAO,KAAA;AAChB;AAMO,UAAU,WAAA,CAAiC,KAAA,EAAU,GAAA,EAAgB,OAAA,EAAwC;AAClH,EAAA,MAAM,CAAA,GAAI,YAAY,OAAO,CAAA;AAC7B,EAAA,MAAM,IAAA,GAAO,YAAY,KAAK,CAAA;AAC9B,EAAA,MAAM,OAAA,GAAU,oBAAoB,IAAI,CAAA;AACxC,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,WAAA,CAAY,GAAG,CAAC,CAAA;AACnD,EAAA,KAAA,IAAS,GAAA,GAAM,OAAA,EAAS,GAAA,IAAO,MAAA,EAAQ,GAAA,EAAA,EAAO;AAC5C,IAAA,IAAI,YAAA,CAAa,CAAA,EAAG,GAAA,EAAK,IAAA,EAAM,OAAO,GAAG,MAAM,KAAA,CAAM,KAAA,EAAO,GAAA,GAAM,OAAO,CAAA;AAAA,EAC3E;AACF;AAsBO,SAAS,kBAAA,CAAmB,OAAA,GAA0B,EAAC,EAAiB;AAC7E,EAAA,MAAM,MAAA,GAAyB,OAAO,MAAA,CAAO;AAAA,IAC3C,OAAA,EAAS,OAAA,CAAQ,OAAA,GAAU,MAAA,CAAO,MAAA,CAAO,CAAC,GAAG,OAAA,CAAQ,OAAO,CAAC,CAAA,GAAI,MAAA;AAAA,IACjE,UAAU,OAAO,OAAA,CAAQ,QAAA,KAAa,UAAA,IAAc,QAAQ,QAAA,IAAY,IAAA,GAAO,OAAA,CAAQ,QAAA,GAAW,OAAO,MAAA,CAAO,CAAC,GAAG,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAAA,IACrI,aAAA,EAAe,OAAA,CAAQ,aAAA,GAAgB,MAAA,CAAO,MAAA,CAAO,CAAC,GAAG,OAAA,CAAQ,aAAa,CAAC,CAAA,GAAI;AAAA,GACpF,CAAA;AACD,EAAA,KAAA,CAAM,GAAA,CAAI,MAAA,EAAQ,OAAA,CAAQ,MAAM,CAAC,CAAA;AACjC,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACnB,OAAA,EAAS,MAAA;AAAA,IACT,SAAA,EAAW,CAAC,CAAA,KAAM,SAAA,CAAU,GAAG,MAAM,CAAA;AAAA,IACrC,SAAA,EAAW,CAAC,CAAA,KAAM,SAAA,CAAU,GAAG,MAAM,CAAA;AAAA,IACrC,SAAA,EAAW,CAAC,CAAA,KAAM,SAAA,CAAU,GAAG,MAAM,CAAA;AAAA,IACrC,aAAa,CAAC,CAAA,EAAG,MAAM,WAAA,CAAY,CAAA,EAAG,GAAG,MAAM,CAAA;AAAA,IAC/C,kBAAkB,CAAC,CAAA,EAAG,MAAM,gBAAA,CAAiB,CAAA,EAAG,GAAG,MAAM,CAAA;AAAA,IACzD,WAAA,EAAa,CAAC,CAAA,KAAM,WAAA,CAAY,GAAG,MAAM,CAAA;AAAA,IACzC,eAAA,EAAiB,CAAC,CAAA,KAAM,eAAA,CAAgB,GAAG,MAAM,CAAA;AAAA,IACjD,WAAA,EAAa,CAAC,CAAA,KAAM,WAAA,CAAY,GAAG,MAAM,CAAA;AAAA,IACzC,YAAA,EAAc,CAAC,CAAA,KAAM,YAAA,CAAa,GAAG,MAAM,CAAA;AAAA,IAC3C,iBAAiB,CAAC,CAAA,EAAG,MAAM,eAAA,CAAgB,CAAA,EAAG,GAAG,MAAM,CAAA;AAAA,IACvD,aAAa,CAAC,CAAA,EAAG,MAAM,WAAA,CAAY,CAAA,EAAG,GAAG,MAAM;AAAA,GACzB,CAAA;AAC1B","file":"index.js","sourcesContent":["// Pure integer date arithmetic on the proleptic ISO calendar.\n// \"Epoch day\" = number of days since 1970-01-01 (which is day 0).\n// Working on integers keeps the hot loops fast and independent of any\n// particular Temporal implementation (native or polyfill).\n\n/** Days from 1970-01-01 to the given ISO date (Howard Hinnant's algorithm). */\nexport function epochDayFromIso(year: number, month: number, day: number): number {\n const y = month <= 2 ? year - 1 : year;\n const era = Math.floor(y / 400);\n const yoe = y - era * 400;\n const mp = (month + 9) % 12; // March = 0\n const doy = Math.floor((153 * mp + 2) / 5) + day - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\n/** Inverse of epochDayFromIso. Returns [year, month, day]. */\nexport function isoFromEpochDay(epochDay: number): [number, number, number] {\n const z = epochDay + 719468;\n const era = Math.floor(z / 146097);\n const doe = z - era * 146097;\n const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);\n const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));\n const mp = Math.floor((5 * doy + 2) / 153);\n const day = doy - Math.floor((153 * mp + 2) / 5) + 1;\n const month = mp < 10 ? mp + 3 : mp - 9;\n const year = yoe + era * 400 + (month <= 2 ? 1 : 0);\n return [year, month, day];\n}\n\n/** ISO day of week: 1 = Monday … 7 = Sunday. 1970-01-01 was a Thursday. */\nexport function dayOfWeekFromEpochDay(epochDay: number): number {\n return ((((epochDay + 3) % 7) + 7) % 7) + 1;\n}\n\nexport function isLeapYear(year: number): boolean {\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\nexport function daysInMonth(year: number, month: number): number {\n if (month === 2) return isLeapYear(year) ? 29 : 28;\n return month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;\n}\n","import { dayOfWeekFromEpochDay, daysInMonth, epochDayFromIso, isoFromEpochDay } from './epoch.js';\n\n/**\n * The parts of a `Temporal.PlainDate` this library relies on. Described structurally so that\n * native Temporal, `temporal-polyfill`, `@js-temporal/polyfill` and their TypeScript types all work,\n * with no global `Temporal` type required.\n */\nexport interface PlainDateLike {\n readonly calendarId: string;\n readonly year: number;\n readonly month: number;\n readonly monthCode: string;\n readonly day: number;\n readonly dayOfWeek: number;\n withCalendar(calendar: 'iso8601'): PlainDateLike;\n add(duration: { days: number }): PlainDateLike;\n toString(): string;\n}\n\n/** The parts of a `Temporal.PlainDateTime` / `Temporal.ZonedDateTime` this library relies on. */\nexport interface DateTimeLike {\n toPlainDate(): PlainDateLike;\n add(duration: { days: number }): DateTimeLike;\n}\n\n/** The parts of a `Temporal.PlainMonthDay` this library relies on. */\nexport interface PlainMonthDayLike {\n readonly calendarId: string;\n readonly monthCode: string;\n readonly day: number;\n}\n\n/**\n * Any Temporal value that has a calendar date: `Temporal.PlainDate`, `Temporal.PlainDateTime`\n * or `Temporal.ZonedDateTime`. Every helper returns the same type it was given.\n */\nexport type DateInput = PlainDateLike | DateTimeLike;\n\n/** ISO day of week: 1 = Monday … 7 = Sunday (same as `Temporal.PlainDate#dayOfWeek`). */\nexport type DayOfWeek = 1 | 2 | 3 | 4 | 5 | 6 | 7;\n\n/**\n * A holiday entry:\n * - `'2026-12-25'` or a `Temporal.PlainDate` — a single date.\n * - `'12-25'` or a `Temporal.PlainMonthDay` — every year on that ISO month/day.\n */\nexport type HolidayInput = string | PlainDateLike | PlainMonthDayLike;\n\n/**\n * Decides whether a date is a holiday. Receives an ISO-calendar `Temporal.PlainDate`\n * from the same Temporal implementation as the input date. Not called for weekend days.\n */\n// Method syntax keeps the parameter bivariant, so `(d: Temporal.PlainDate) => boolean` is accepted.\nexport type HolidayPredicate = { bivarianceHack(date: PlainDateLike): boolean }['bivarianceHack'];\n\nexport interface WorkdayOptions {\n /** Non-working days of the week. Default `[6, 7]` (Saturday, Sunday). */\n weekend?: readonly DayOfWeek[];\n /** Holiday dates, or a function that decides whether a date is a holiday. */\n holidays?: Iterable<HolidayInput> | HolidayPredicate;\n /**\n * Dates that count as workdays even if they fall on the weekend or a holiday\n * (for example, make-up working Saturdays). Single dates only.\n */\n extraWorkdays?: Iterable<string | PlainDateLike>;\n}\n\n/** Longest run of consecutive non-workdays we search before giving up (≈ 20 years). */\nconst MAX_SCAN_DAYS = 366 * 20;\n\ninterface Compiled {\n weekend: boolean[]; // index 1..7\n dates: Set<number>; // epoch days\n annual: Set<number>; // month * 100 + day\n predicate: HolidayPredicate | undefined;\n extra: Set<number>; // epoch days\n}\n\nconst DATE_RE = /^([+-]?\\d{4,6})-(\\d{2})-(\\d{2})$/;\nconst MONTH_DAY_RE = /^(?:--)?(\\d{2})-(\\d{2})$/;\n\nfunction checkDay(year: number, month: number, day: number, source: unknown): void {\n if (month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) {\n throw new RangeError(`temporal-workdays: invalid date ${JSON.stringify(String(source))}`);\n }\n}\n\nfunction epochDayOfPlainDate(date: PlainDateLike): number {\n const iso = date.calendarId === 'iso8601' ? date : date.withCalendar('iso8601');\n return epochDayFromIso(iso.year, iso.month, iso.day);\n}\n\nfunction isTemporalLike(value: unknown, tag: string): boolean {\n return typeof value === 'object' && value !== null && Object.prototype.toString.call(value) === `[object ${tag}]`;\n}\n\nfunction parseSingleDate(value: unknown): number {\n if (typeof value === 'string') {\n const m = DATE_RE.exec(value);\n if (!m) throw new RangeError(`temporal-workdays: expected \"YYYY-MM-DD\", got ${JSON.stringify(value)}`);\n const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];\n checkDay(y, mo, d, value);\n return epochDayFromIso(y, mo, d);\n }\n if (isTemporalLike(value, 'Temporal.PlainDate')) return epochDayOfPlainDate(value as PlainDateLike);\n throw new TypeError(`temporal-workdays: expected a \"YYYY-MM-DD\" string or Temporal.PlainDate, got ${String(value)}`);\n}\n\nfunction compile(options: WorkdayOptions | undefined): Compiled {\n const weekendDays = options?.weekend ?? [6, 7];\n const weekend = new Array<boolean>(8).fill(false);\n for (const d of weekendDays) {\n if (!Number.isInteger(d) || d < 1 || d > 7) {\n throw new RangeError(`temporal-workdays: weekend days must be integers 1 (Mon) – 7 (Sun), got ${String(d)}`);\n }\n weekend[d] = true;\n }\n\n const dates = new Set<number>();\n const annual = new Set<number>();\n let predicate: HolidayPredicate | undefined;\n const holidays = options?.holidays;\n\n if (typeof holidays === 'function') {\n predicate = holidays;\n } else if (holidays != null) {\n for (const h of holidays) {\n if (typeof h === 'string') {\n const md = MONTH_DAY_RE.exec(h);\n if (md) {\n const [mo, d] = [Number(md[1]), Number(md[2])];\n checkDay(2000, mo, d, h); // leap year so that 02-29 is accepted\n annual.add(mo * 100 + d);\n continue;\n }\n dates.add(parseSingleDate(h));\n } else if (isTemporalLike(h, 'Temporal.PlainMonthDay')) {\n const pmd = h as PlainMonthDayLike;\n if (pmd.calendarId !== 'iso8601') {\n throw new RangeError('temporal-workdays: Temporal.PlainMonthDay holidays must use the iso8601 calendar');\n }\n annual.add(Number(pmd.monthCode.slice(1)) * 100 + pmd.day);\n } else {\n dates.add(parseSingleDate(h));\n }\n }\n }\n\n const extra = new Set<number>();\n if (options?.extraWorkdays != null) {\n for (const e of options.extraWorkdays) extra.add(parseSingleDate(e));\n }\n\n return { weekend, dates, annual, predicate, extra };\n}\n\nconst cache = new WeakMap<object, Compiled>();\nconst DEFAULT = compile(undefined);\n\nfunction getCompiled(options: WorkdayOptions | undefined): Compiled {\n if (options === undefined) return DEFAULT;\n let c = cache.get(options);\n if (!c) {\n c = compile(options);\n cache.set(options, c);\n }\n return c;\n}\n\n/** Calendar date of the input as an ISO epoch day, plus an ISO PlainDate for predicates. */\nfunction toPlainDate(date: DateInput): PlainDateLike {\n if (date == null || typeof (date as { add?: unknown }).add !== 'function') {\n throw new TypeError('temporal-workdays: expected a Temporal.PlainDate, PlainDateTime or ZonedDateTime');\n }\n return 'toPlainDate' in date ? date.toPlainDate() : date;\n}\n\nfunction isHolidayDay(c: Compiled, epochDay: number, base: PlainDateLike, baseDay: number): boolean {\n if (c.dates.has(epochDay)) return true;\n if (c.annual.size > 0) {\n const [, mo, d] = isoFromEpochDay(epochDay);\n if (c.annual.has(mo * 100 + d)) return true;\n }\n if (c.predicate) {\n const iso = base.calendarId === 'iso8601' ? base : base.withCalendar('iso8601');\n return c.predicate(epochDay === baseDay ? iso : iso.add({ days: epochDay - baseDay }));\n }\n return false;\n}\n\nfunction isWorkdayDay(c: Compiled, epochDay: number, base: PlainDateLike, baseDay: number): boolean {\n if (c.extra.has(epochDay)) return true;\n if (c.weekend[dayOfWeekFromEpochDay(epochDay)]) return false;\n return !isHolidayDay(c, epochDay, base, baseDay);\n}\n\nfunction checkInteger(n: number, name: string): void {\n if (!Number.isInteger(n)) throw new RangeError(`temporal-workdays: ${name} must be an integer, got ${String(n)}`);\n}\n\nfunction noWorkdayError(): RangeError {\n return new RangeError(\n `temporal-workdays: no workday found within ${MAX_SCAN_DAYS} days — check your weekend/holiday configuration`,\n );\n}\n\n/** Steps `count` workdays in direction `dir` (+1 / -1) and returns the epoch-day delta from start. */\nfunction step(c: Compiled, base: PlainDateLike, count: number, dir: 1 | -1): number {\n const baseDay = epochDayOfPlainDate(base);\n let day = baseDay;\n let remaining = count;\n let gap = 0;\n while (remaining > 0) {\n day += dir;\n if (isWorkdayDay(c, day, base, baseDay)) {\n remaining--;\n gap = 0;\n } else if (++gap > MAX_SCAN_DAYS) {\n throw noWorkdayError();\n }\n }\n return day - baseDay;\n}\n\nfunction shift<T extends DateInput>(date: T, days: number): T {\n return (days === 0 ? date : date.add({ days })) as T;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** `true` if the date falls on a configured weekend day (ignores holidays and `extraWorkdays`). */\nexport function isWeekend(date: DateInput, options?: WorkdayOptions): boolean {\n return getCompiled(options).weekend[toPlainDate(date).dayOfWeek] === true;\n}\n\n/** `true` if the date is a configured holiday and not listed in `extraWorkdays`. */\nexport function isHoliday(date: DateInput, options?: WorkdayOptions): boolean {\n const c = getCompiled(options);\n const base = toPlainDate(date);\n const day = epochDayOfPlainDate(base);\n return !c.extra.has(day) && isHolidayDay(c, day, base, day);\n}\n\n/** `true` if the date is a working day: not a weekend day and not a holiday, or listed in `extraWorkdays`. */\nexport function isWorkday(date: DateInput, options?: WorkdayOptions): boolean {\n const c = getCompiled(options);\n const base = toPlainDate(date);\n const day = epochDayOfPlainDate(base);\n return isWorkdayDay(c, day, base, day);\n}\n\n/**\n * Adds `amount` workdays (negative to go back). The result has the same type as the input,\n * and time of day / time zone are preserved. `amount = 0` returns the input unchanged.\n * Starting on a non-workday, `+1` gives the next workday.\n */\nexport function addWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T {\n checkInteger(amount, 'amount');\n if (amount === 0) return date;\n const c = getCompiled(options);\n return shift(date, step(c, toPlainDate(date), Math.abs(amount), amount > 0 ? 1 : -1));\n}\n\n/** Subtracts `amount` workdays. Same as `addWorkdays(date, -amount)`. */\nexport function subtractWorkdays<T extends DateInput>(date: T, amount: number, options?: WorkdayOptions): T {\n checkInteger(amount, 'amount');\n return addWorkdays(date, -amount, options);\n}\n\n/** The first workday strictly after `date`. */\nexport function nextWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return shift(date, step(getCompiled(options), toPlainDate(date), 1, 1));\n}\n\n/** The last workday strictly before `date`. */\nexport function previousWorkday<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return shift(date, step(getCompiled(options), toPlainDate(date), 1, -1));\n}\n\n/** `date` itself if it is a workday, otherwise the next workday after it. */\nexport function rollForward<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return isWorkday(date, options) ? date : nextWorkday(date, options);\n}\n\n/** `date` itself if it is a workday, otherwise the previous workday before it. */\nexport function rollBackward<T extends DateInput>(date: T, options?: WorkdayOptions): T {\n return isWorkday(date, options) ? date : previousWorkday(date, options);\n}\n\n/**\n * Number of workdays after `start` up to and including `end` — the range `(start, end]`.\n * Negative when `end` is before `start`. Only calendar dates matter; times are ignored.\n *\n * For `amount >= 0`: `workdaysBetween(d, addWorkdays(d, amount)) === amount`.\n */\nexport function workdaysBetween(start: DateInput, end: DateInput, options?: WorkdayOptions): number {\n const c = getCompiled(options);\n const a = toPlainDate(start);\n const b = toPlainDate(end);\n const aDay = epochDayOfPlainDate(a);\n const bDay = epochDayOfPlainDate(b);\n if (aDay === bDay) return 0;\n const [lo, hi, base, baseDay, sign] = aDay < bDay ? [aDay, bDay, a, aDay, 1] : [bDay, aDay, b, bDay, -1];\n let count = 0;\n for (let day = lo + 1; day <= hi; day++) {\n if (isWorkdayDay(c, day, base, baseDay)) count++;\n }\n return sign * count;\n}\n\n/**\n * Yields every workday from `start` to `end`, both inclusive, in ascending order.\n * Values have the type of `start` (time of day / zone preserved). Yields nothing if `end < start`.\n */\nexport function* eachWorkday<T extends DateInput>(start: T, end: DateInput, options?: WorkdayOptions): Generator<T> {\n const c = getCompiled(options);\n const base = toPlainDate(start);\n const baseDay = epochDayOfPlainDate(base);\n const endDay = epochDayOfPlainDate(toPlainDate(end));\n for (let day = baseDay; day <= endDay; day++) {\n if (isWorkdayDay(c, day, base, baseDay)) yield shift(start, day - baseDay);\n }\n}\n\n/** A set of workday helpers bound to one configuration. Holidays are parsed once. */\nexport interface WorkCalendar {\n readonly options: Readonly<WorkdayOptions>;\n isWorkday(date: DateInput): boolean;\n isWeekend(date: DateInput): boolean;\n isHoliday(date: DateInput): boolean;\n addWorkdays<T extends DateInput>(date: T, amount: number): T;\n subtractWorkdays<T extends DateInput>(date: T, amount: number): T;\n nextWorkday<T extends DateInput>(date: T): T;\n previousWorkday<T extends DateInput>(date: T): T;\n rollForward<T extends DateInput>(date: T): T;\n rollBackward<T extends DateInput>(date: T): T;\n workdaysBetween(start: DateInput, end: DateInput): number;\n eachWorkday<T extends DateInput>(start: T, end: DateInput): Generator<T>;\n}\n\n/**\n * Creates helpers bound to one configuration. The options are validated and copied\n * immediately, so later changes to the passed-in arrays don't affect the calendar.\n */\nexport function createWorkCalendar(options: WorkdayOptions = {}): WorkCalendar {\n const frozen: WorkdayOptions = Object.freeze({\n weekend: options.weekend ? Object.freeze([...options.weekend]) : undefined,\n holidays: typeof options.holidays === 'function' || options.holidays == null ? options.holidays : Object.freeze([...options.holidays]),\n extraWorkdays: options.extraWorkdays ? Object.freeze([...options.extraWorkdays]) : undefined,\n });\n cache.set(frozen, compile(frozen)); // validate eagerly\n return Object.freeze({\n options: frozen,\n isWorkday: (d) => isWorkday(d, frozen),\n isWeekend: (d) => isWeekend(d, frozen),\n isHoliday: (d) => isHoliday(d, frozen),\n addWorkdays: (d, n) => addWorkdays(d, n, frozen),\n subtractWorkdays: (d, n) => subtractWorkdays(d, n, frozen),\n nextWorkday: (d) => nextWorkday(d, frozen),\n previousWorkday: (d) => previousWorkday(d, frozen),\n rollForward: (d) => rollForward(d, frozen),\n rollBackward: (d) => rollBackward(d, frozen),\n workdaysBetween: (a, b) => workdaysBetween(a, b, frozen),\n eachWorkday: (a, b) => eachWorkday(a, b, frozen),\n } satisfies WorkCalendar);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,72 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "temporal-workdays",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Business-day math for the JavaScript Temporal API: add/subtract workdays, count workdays between dates, custom weekends, holidays and make-up days. Zero dependencies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"temporal",
|
|
7
|
+
"business-days",
|
|
8
|
+
"workdays",
|
|
9
|
+
"working-days",
|
|
10
|
+
"holidays",
|
|
11
|
+
"weekend",
|
|
12
|
+
"date",
|
|
13
|
+
"calendar",
|
|
14
|
+
"sla",
|
|
15
|
+
"plaindate",
|
|
16
|
+
"zoneddatetime"
|
|
17
|
+
],
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "Akash Gupta",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/akashgupta1909/temporal-workdays.git"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/akashgupta1909/temporal-workdays#readme",
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/akashgupta1909/temporal-workdays/issues"
|
|
27
|
+
},
|
|
28
|
+
"type": "module",
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"main": "./dist/index.cjs",
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"exports": {
|
|
34
|
+
".": {
|
|
35
|
+
"import": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"default": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"require": {
|
|
40
|
+
"types": "./dist/index.d.cts",
|
|
41
|
+
"default": "./dist/index.cjs"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"./package.json": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"CHANGELOG.md",
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE"
|
|
51
|
+
],
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=18"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"build": "tsup",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:watch": "vitest",
|
|
59
|
+
"typecheck": "tsc",
|
|
60
|
+
"lint:pkg": "publint && attw --pack .",
|
|
61
|
+
"check": "npm run typecheck && npm test && npm run build && npm run lint:pkg",
|
|
62
|
+
"prepublishOnly": "npm run check"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
66
|
+
"publint": "^0.3.25",
|
|
67
|
+
"temporal-polyfill": "^1.0.8",
|
|
68
|
+
"tsup": "^9.0.0",
|
|
69
|
+
"typescript": "^6.0.3",
|
|
70
|
+
"vitest": "^5.1.0"
|
|
71
|
+
}
|
|
72
|
+
}
|