@typescript-calendar-lib/core 0.5.2 → 0.5.3
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/README.md +195 -0
- package/dist/index.d.ts +120 -0
- package/dist/index.js +407 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -124,6 +124,15 @@ getWeekdayHeaders("en", "sunday"); // ["Sun", "Mon", ..., "Sat"]
|
|
|
124
124
|
getWeekdayHeaders("en", "monday"); // ["Mon", "Tue", ..., "Sun"]
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
+
### `getLocaleData(locale): LocaleData`
|
|
128
|
+
|
|
129
|
+
Returns the raw locale data (`months`, `weekdays`, `weekdaysShort`, plus the Monday-start rotations `weekdaysMonday` and `weekdaysMondayShort`). Throws `RangeError` for an unknown locale.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
getLocaleData("en").weekdays; // ["Sun", "Mon", ..., "Sat"]
|
|
133
|
+
getLocaleData("ja").weekdaysMonday; // ["月", "火", ..., "日"]
|
|
134
|
+
```
|
|
135
|
+
|
|
127
136
|
### `buildMonthGrid(year, month, weekStart): (number | null)[][]`
|
|
128
137
|
|
|
129
138
|
Builds a 6×7 grid of day numbers (`1`–`31`) with `null` for empty cells. The grid is always 6 rows tall for stable layouts:
|
|
@@ -184,6 +193,159 @@ Creates a local `Date` at `00:00:00` without the `new Date(year, ...)` 1900-inte
|
|
|
184
193
|
|
|
185
194
|
Throws `RangeError` when `date` is not a `Date` instance or is an `Invalid Date`.
|
|
186
195
|
|
|
196
|
+
#### Arithmetic
|
|
197
|
+
|
|
198
|
+
### `addDays(date, amount): Date`
|
|
199
|
+
|
|
200
|
+
Adds whole days (negative subtracts). Calendar-day based (`setDate`), so DST transitions don't shift the time — the same local time is preserved. Returns a new `Date`; the input is unchanged.
|
|
201
|
+
|
|
202
|
+
```ts
|
|
203
|
+
addDays(new Date(2026, 8, 25), 7); // Oct 02 2026, same time
|
|
204
|
+
addDays(new Date(2026, 8, 25), -25); // Aug 31 2026
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### `addWeeks(date, amount): Date`
|
|
208
|
+
|
|
209
|
+
Adds whole weeks — equivalent to `addDays(date, amount * 7)`. `amount` must be an integer.
|
|
210
|
+
|
|
211
|
+
### `addMonths(date, amount): Date`
|
|
212
|
+
|
|
213
|
+
Adds whole months. When the target day doesn't exist in the destination month, the result clamps to the month's last day.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
addMonths(new Date(2026, 0, 31), 1); // Feb 28 2026 (Jan 31 + 1 month)
|
|
217
|
+
addMonths(new Date(2028, 0, 31), 1); // Feb 29 2028 (leap year)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### `addYears(date, amount): Date`
|
|
221
|
+
|
|
222
|
+
Adds whole years. Feb 29 clamps to Feb 28 of the target year.
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
addYears(new Date(2028, 1, 29), 1); // Feb 28 2029
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### Period boundaries
|
|
229
|
+
|
|
230
|
+
`startOf*` normalize to `00:00:00.000`, `endOf*` to `23:59:59.999`.
|
|
231
|
+
|
|
232
|
+
### `startOfDay(date): Date`
|
|
233
|
+
|
|
234
|
+
Returns the date at `00:00:00.000`.
|
|
235
|
+
|
|
236
|
+
### `endOfDay(date): Date`
|
|
237
|
+
|
|
238
|
+
Returns the date at `23:59:59.999`.
|
|
239
|
+
|
|
240
|
+
### `startOfWeek(date, weekStart?): Date`
|
|
241
|
+
|
|
242
|
+
Returns the first day of the containing week at `00:00:00.000`. `weekStart` defaults to `"sunday"`.
|
|
243
|
+
|
|
244
|
+
### `endOfWeek(date, weekStart?): Date`
|
|
245
|
+
|
|
246
|
+
Returns the last day of the containing week at `23:59:59.999`. `weekStart` defaults to `"sunday"`.
|
|
247
|
+
|
|
248
|
+
### `startOfMonth(date): Date`
|
|
249
|
+
|
|
250
|
+
Returns the 1st of the month at `00:00:00.000`.
|
|
251
|
+
|
|
252
|
+
### `endOfMonth(date): Date`
|
|
253
|
+
|
|
254
|
+
Returns the last day of the month at `23:59:59.999`.
|
|
255
|
+
|
|
256
|
+
### `startOfYear(date): Date`
|
|
257
|
+
|
|
258
|
+
Returns Jan 1 at `00:00:00.000`.
|
|
259
|
+
|
|
260
|
+
### `endOfYear(date): Date`
|
|
261
|
+
|
|
262
|
+
Returns Dec 31 at `23:59:59.999`.
|
|
263
|
+
|
|
264
|
+
#### Differences
|
|
265
|
+
|
|
266
|
+
Calendar differences ignore the time component, may be negative, and are DST-safe.
|
|
267
|
+
|
|
268
|
+
### `diffInCalendarDays(from, to): number`
|
|
269
|
+
|
|
270
|
+
Returns `to - from` in calendar days.
|
|
271
|
+
|
|
272
|
+
### `diffInCalendarMonths(from, to): number`
|
|
273
|
+
|
|
274
|
+
Returns `to - from` in whole calendar months.
|
|
275
|
+
|
|
276
|
+
### `diffInCalendarYears(from, to): number`
|
|
277
|
+
|
|
278
|
+
Returns `to - from` in whole calendar years.
|
|
279
|
+
|
|
280
|
+
#### Comparison
|
|
281
|
+
|
|
282
|
+
### `isBefore(date, other): boolean`
|
|
283
|
+
|
|
284
|
+
`true` when `date` is strictly before `other` (ms comparison).
|
|
285
|
+
|
|
286
|
+
### `isAfter(date, other): boolean`
|
|
287
|
+
|
|
288
|
+
`true` when `date` is strictly after `other` (ms comparison).
|
|
289
|
+
|
|
290
|
+
### `isSameMonth(a, b): boolean`
|
|
291
|
+
|
|
292
|
+
`true` when both dates share the year and month (day ignored).
|
|
293
|
+
|
|
294
|
+
### `isSameYear(a, b): boolean`
|
|
295
|
+
|
|
296
|
+
`true` when both dates share the year.
|
|
297
|
+
|
|
298
|
+
### `isWeekend(date): boolean`
|
|
299
|
+
|
|
300
|
+
`true` for Saturday or Sunday.
|
|
301
|
+
|
|
302
|
+
#### Week & year position
|
|
303
|
+
|
|
304
|
+
### `getISOWeek(date): number`
|
|
305
|
+
|
|
306
|
+
Returns the ISO 8601 week number (`1`–`53`; week 1 contains the first Thursday).
|
|
307
|
+
|
|
308
|
+
### `getWeekOfYear(date, weekStart?): number`
|
|
309
|
+
|
|
310
|
+
Returns the week number using the US convention — the week containing Jan 1 is week 1 (like `cal -w`). `weekStart` defaults to `"sunday"`.
|
|
311
|
+
|
|
312
|
+
### `getDayOfYear(date): number`
|
|
313
|
+
|
|
314
|
+
Returns the 1-based day of year (Jan 1 = `1`, Dec 31 = `365`/`366`).
|
|
315
|
+
|
|
316
|
+
#### Clamping
|
|
317
|
+
|
|
318
|
+
### `clampDate(date, min, max): Date`
|
|
319
|
+
|
|
320
|
+
Returns `date` clamped into `[min, max]`: `min` when earlier, `max` when later, otherwise the original `date`. All three dates are validated.
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
clampDate(new Date(2026, 8, 25), new Date(2026, 8, 1), new Date(2026, 8, 15)); // Sep 15 2026
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
#### Formatting
|
|
327
|
+
|
|
328
|
+
### `formatDate(date, pattern, locale?): string`
|
|
329
|
+
|
|
330
|
+
Formats a date with the given pattern. Tokens are replaced with localized values; everything else is output verbatim.
|
|
331
|
+
|
|
332
|
+
| Token | Meaning |
|
|
333
|
+
| :--- | :--- |
|
|
334
|
+
| `yyyy` | 4-digit year (`2026`) |
|
|
335
|
+
| `yy` | last 2 digits of the year (`26`) |
|
|
336
|
+
| `MMMM` | full month name (`September`, `9月`) |
|
|
337
|
+
| `MM` / `M` | zero-padded / plain month (`09` / `9`) |
|
|
338
|
+
| `dd` / `d` | zero-padded / plain day (`05` / `5`) |
|
|
339
|
+
| `EEE` | short weekday, localized (`Fri`, `金`) |
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
formatDate(date, "yyyy-MM-dd"); // "2026-09-25"
|
|
343
|
+
formatDate(date, "MMMM d, yyyy (EEE)"); // "September 25, 2026 (Fri)"
|
|
344
|
+
formatDate(date, "yyyy年M月d日", "ja"); // "2026年9月25日"
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
> **Note:** all of the above validate their inputs and throw `RangeError` on invalid values: dates are checked with `assertValidDate`, `add*` amounts must be integers, and `weekStart` must be `"sunday" | "monday"`.
|
|
348
|
+
|
|
187
349
|
## Input validation
|
|
188
350
|
|
|
189
351
|
All functions validate their inputs and throw `RangeError` on invalid values instead of silently producing wrong results:
|
|
@@ -203,11 +365,14 @@ All functions validate their inputs and throw `RangeError` on invalid values ins
|
|
|
203
365
|
```ts
|
|
204
366
|
// Types
|
|
205
367
|
import type {
|
|
368
|
+
CalendarCellState,
|
|
206
369
|
CalendarOptions,
|
|
207
370
|
CalendarRangeOptions,
|
|
208
371
|
CalendarYearOptions,
|
|
372
|
+
DateRange,
|
|
209
373
|
HighlightStyle,
|
|
210
374
|
Locale,
|
|
375
|
+
LocaleData,
|
|
211
376
|
RenderMonthOptions,
|
|
212
377
|
WeekStart,
|
|
213
378
|
} from "@typescript-calendar-lib/core";
|
|
@@ -217,16 +382,46 @@ import {
|
|
|
217
382
|
LOCALES,
|
|
218
383
|
MAX_YEAR,
|
|
219
384
|
MIN_YEAR,
|
|
385
|
+
addDays,
|
|
386
|
+
addMonths,
|
|
387
|
+
addWeeks,
|
|
388
|
+
addYears,
|
|
220
389
|
assertValidDate,
|
|
221
390
|
buildMonthGrid,
|
|
391
|
+
clampDate,
|
|
222
392
|
createDate,
|
|
393
|
+
daysInMonth,
|
|
394
|
+
diffInCalendarDays,
|
|
395
|
+
diffInCalendarMonths,
|
|
396
|
+
diffInCalendarYears,
|
|
397
|
+
endOfDay,
|
|
398
|
+
endOfMonth,
|
|
399
|
+
endOfWeek,
|
|
400
|
+
endOfYear,
|
|
223
401
|
firstDayOfMonth,
|
|
402
|
+
formatDate,
|
|
403
|
+
getCalendarCellState,
|
|
404
|
+
getDayOfYear,
|
|
405
|
+
getISOWeek,
|
|
406
|
+
getLocaleData,
|
|
224
407
|
getMonthName,
|
|
225
408
|
getMonthRange,
|
|
409
|
+
getWeekOfYear,
|
|
226
410
|
getWeekdayHeaders,
|
|
411
|
+
isAfter,
|
|
412
|
+
isBefore,
|
|
227
413
|
isDateInRange,
|
|
414
|
+
isLeapYear,
|
|
228
415
|
isSameDay,
|
|
416
|
+
isSameMonth,
|
|
417
|
+
isSameYear,
|
|
418
|
+
isWeekend,
|
|
229
419
|
lastDayOfMonth,
|
|
420
|
+
sortRange,
|
|
421
|
+
startOfDay,
|
|
422
|
+
startOfMonth,
|
|
423
|
+
startOfWeek,
|
|
424
|
+
startOfYear,
|
|
230
425
|
} from "@typescript-calendar-lib/core";
|
|
231
426
|
```
|
|
232
427
|
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ type WeekStart = "sunday" | "monday";
|
|
|
4
4
|
type HighlightStyle = "bracket" | "reverse";
|
|
5
5
|
interface BaseCalendarOptions {
|
|
6
6
|
locale?: Locale;
|
|
7
|
+
/** 祝日判定に使うロケール。省略時は "ja"(日本の祝日) */
|
|
8
|
+
holidayLocale?: Locale;
|
|
7
9
|
weekStart?: WeekStart;
|
|
8
10
|
highlight?: Date;
|
|
9
11
|
highlightStyle?: HighlightStyle;
|
|
@@ -26,6 +28,118 @@ interface CalendarRangeOptions extends BaseCalendarOptions {
|
|
|
26
28
|
}
|
|
27
29
|
interface RenderMonthOptions extends BaseCalendarOptions {}
|
|
28
30
|
//#endregion
|
|
31
|
+
//#region src/date-math.d.ts
|
|
32
|
+
/**
|
|
33
|
+
* 日付に日数を加算する(負数で減算)。時刻は保持される。
|
|
34
|
+
* `setDate` ベースの暦日加算のため DST 遷移でも 1 日ずれない。
|
|
35
|
+
* 入力 Date は変更されず、新しい Date を返す。
|
|
36
|
+
*/
|
|
37
|
+
export declare function addDays(date: Date, amount: number): Date;
|
|
38
|
+
/** 日付に週数を加算する(負数で減算)。addDays(date, amount * 7) と同じ */
|
|
39
|
+
export declare function addWeeks(date: Date, amount: number): Date;
|
|
40
|
+
/**
|
|
41
|
+
* 日付に月数を加算する(負数で減算)。時刻は保持される。
|
|
42
|
+
* 月中旬の日付は月を跨いでも同じ日付を保つ(例: 1月15日 + 1ヶ月 = 2月15日)。
|
|
43
|
+
* 移動先の月に存在しない日付は月末に丸められる
|
|
44
|
+
* (例: 1月31日 + 1ヶ月 = 2月28日/29日、1月30日 + 1ヶ月 = 2月28日/29日)。
|
|
45
|
+
*/
|
|
46
|
+
export declare function addMonths(date: Date, amount: number): Date;
|
|
47
|
+
/** 日付に年数を加算する(負数で減算)。うるう日の 2月29日は 2月28日に丸められる */
|
|
48
|
+
export declare function addYears(date: Date, amount: number): Date;
|
|
49
|
+
/** その日の 00:00:00.000 を返す */
|
|
50
|
+
export declare function startOfDay(date: Date): Date;
|
|
51
|
+
/** その日の 23:59:59.999 を返す */
|
|
52
|
+
export declare function endOfDay(date: Date): Date;
|
|
53
|
+
/** 日付を含む週の初日(weekStart 曜日)の 00:00:00.000 を返す */
|
|
54
|
+
export declare function startOfWeek(date: Date, weekStart?: WeekStart): Date;
|
|
55
|
+
/** 日付を含む週の最終日の 23:59:59.999 を返す(週末を含む範囲に便利) */
|
|
56
|
+
export declare function endOfWeek(date: Date, weekStart?: WeekStart): Date;
|
|
57
|
+
/** 日付を含む月の初日の 00:00:00.000 を返す */
|
|
58
|
+
export declare function startOfMonth(date: Date): Date;
|
|
59
|
+
/** 日付を含む月の最終日の 23:59:59.999 を返す */
|
|
60
|
+
export declare function endOfMonth(date: Date): Date;
|
|
61
|
+
/** 日付を含む年の初日(1月1日)の 00:00:00.000 を返す */
|
|
62
|
+
export declare function startOfYear(date: Date): Date;
|
|
63
|
+
/** 日付を含む年の最終日の 23:59:59.999 を返す */
|
|
64
|
+
export declare function endOfYear(date: Date): Date;
|
|
65
|
+
/**
|
|
66
|
+
* 2つの日付の間の暦日数(to - from)を返す。マイナスもありうる。
|
|
67
|
+
* 時刻は無視され、DST 遷移があっても 1 日単位の差は正しい。
|
|
68
|
+
*/
|
|
69
|
+
export declare function diffInCalendarDays(from: Date, to: Date): number;
|
|
70
|
+
/** 2つの日付の間の暦月数(to - from)を返す。マイナスもありうる */
|
|
71
|
+
export declare function diffInCalendarMonths(from: Date, to: Date): number;
|
|
72
|
+
/** 2つの日付の間の暦年数(to - from)を返す。マイナスもありうる */
|
|
73
|
+
export declare function diffInCalendarYears(from: Date, to: Date): number;
|
|
74
|
+
/** date が other より前(厳密に時刻が小さい)か */
|
|
75
|
+
export declare function isBefore(date: Date, other: Date): boolean;
|
|
76
|
+
/** date が other より後(厳密に時刻が大きい)か */
|
|
77
|
+
export declare function isAfter(date: Date, other: Date): boolean;
|
|
78
|
+
/** 同じ年月か(日は無視) */
|
|
79
|
+
export declare function isSameMonth(a: Date, b: Date): boolean;
|
|
80
|
+
/** 同じ年か(月・日は無視) */
|
|
81
|
+
export declare function isSameYear(a: Date, b: Date): boolean;
|
|
82
|
+
/** 週末(土曜 or 日曜)か */
|
|
83
|
+
export declare function isWeekend(date: Date): boolean;
|
|
84
|
+
/** ISO 8601 の週番号(1-53)を返す。第1週は最初の木曜日を含む週 */
|
|
85
|
+
export declare function getISOWeek(date: Date): number;
|
|
86
|
+
/**
|
|
87
|
+
* 年始(1月1日)を含む週を第1週とする週番号を返す。
|
|
88
|
+
* 1月1日より前の週の末日(例: 日曜始まりの 1月1日が土曜のとき)は前年の第52週/第53週になる。
|
|
89
|
+
* 北米のカレンダー(cal -w 相当)で使われる規則。
|
|
90
|
+
*/
|
|
91
|
+
export declare function getWeekOfYear(date: Date, weekStart?: WeekStart): number;
|
|
92
|
+
/** 年の通算日(1月1日 = 1、12月31日 = 365/366)を返す */
|
|
93
|
+
export declare function getDayOfYear(date: Date): number;
|
|
94
|
+
/** 日付を [min, max] の範囲内にクランプする。範囲外なら min/max を、範囲内なら元の Date を返す */
|
|
95
|
+
export declare function clampDate(date: Date, min: Date, max: Date): Date;
|
|
96
|
+
/**
|
|
97
|
+
* パターン文字列に従って日付をフォーマットする。
|
|
98
|
+
*
|
|
99
|
+
* 対応トークン:
|
|
100
|
+
* - `yyyy` — 4桁の年(例: 2026)
|
|
101
|
+
* - `yy` — 西暦下2桁(例: 26)
|
|
102
|
+
* - `MMMM` — フル月名(ロケール依存。例: September / 9月)
|
|
103
|
+
* - `MM` — 0埋め2桁の月(01-12)
|
|
104
|
+
* - `M` — 月(1-12)
|
|
105
|
+
* - `dd` — 0埋め2桁の日(01-31)
|
|
106
|
+
* - `d` — 日(1-31)
|
|
107
|
+
* - `EEE` — 曜日の短縮名(ロケール依存。例: Fri / 金)
|
|
108
|
+
*
|
|
109
|
+
* トークン以外の文字はそのまま出力される。
|
|
110
|
+
*
|
|
111
|
+
* ```ts
|
|
112
|
+
* formatDate(date, "yyyy-MM-dd"); // "2026-09-25"
|
|
113
|
+
* formatDate(date, "MMMM d, yyyy (EEE)"); // "September 25, 2026 (Fri)"
|
|
114
|
+
* formatDate(date, "yyyy年M月d日", "ja"); // "2026年9月25日"
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function formatDate(date: Date, pattern: string, locale?: Locale): string;
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/holidays.d.ts
|
|
120
|
+
/** 祝日が計算できる年範囲(1949 年以降の現行祝日法制に基づく) */
|
|
121
|
+
export declare const HOLIDAY_MIN_YEAR = 1949;
|
|
122
|
+
/** 祝日が計算できる年範囲(秋分の日の近似式が有効な上限。実用上十分) */
|
|
123
|
+
export declare const HOLIDAY_MAX_YEAR = 2100;
|
|
124
|
+
/** 日付が祝日(日本の国民の祝日・振替休日・国民の休日)か判定する。対象外ロケール/年は false */
|
|
125
|
+
export declare function isHoliday(date: Date, locale?: Locale): boolean;
|
|
126
|
+
/** 日付の祝日名を返す。祝日でない・対象外ロケール/年は undefined */
|
|
127
|
+
export declare function getHolidayName(date: Date, locale?: Locale): string | undefined;
|
|
128
|
+
/** 日付が営業日(平日かつ祝日でない)か判定する */
|
|
129
|
+
export declare function isBusinessDay(date: Date, locale?: Locale): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* 日付に営業日数を加算する(負数で減算)。土日・祝日はスキップされる。
|
|
132
|
+
* 加算の起点は date そのもの(date 自体は数えない)。date === 戻り値になるのは amount = 0 のとき。
|
|
133
|
+
* 例: 2026-05-01(金)に 1 を足すと 2026-05-07(木)になる(5/3-5/6 が祝日)。
|
|
134
|
+
*/
|
|
135
|
+
export declare function addBusinessDays(date: Date, amount: number, locale?: Locale): Date;
|
|
136
|
+
/**
|
|
137
|
+
* 2つの日付の間の営業日数(to - from 方向)を返す。マイナスもありうる。
|
|
138
|
+
* from 自身は数えず、to は数える。例: 月曜→火曜 = 1、金曜→翌月曜 = 1(土日はスキップ)。
|
|
139
|
+
* `addBusinessDays(from, n) === to` と `diffInBusinessDays(from, to) === n` は同値。
|
|
140
|
+
*/
|
|
141
|
+
export declare function diffInBusinessDays(from: Date, to: Date, locale?: Locale): number;
|
|
142
|
+
//#endregion
|
|
29
143
|
//#region src/locale.d.ts
|
|
30
144
|
interface LocaleData {
|
|
31
145
|
months: readonly string[];
|
|
@@ -35,12 +149,16 @@ interface LocaleData {
|
|
|
35
149
|
weekdaysMondayShort: readonly string[];
|
|
36
150
|
}
|
|
37
151
|
export declare const LOCALES: Record<Locale, LocaleData>;
|
|
152
|
+
/** ロケールデータを取得する(未知のロケールは RangeError) */
|
|
153
|
+
export declare function getLocaleData(locale: Locale): LocaleData;
|
|
38
154
|
export declare function getWeekdayHeaders(locale: Locale, weekStart: WeekStart): readonly string[];
|
|
39
155
|
export declare function getMonthName(locale: Locale, month: number): string;
|
|
40
156
|
//#endregion
|
|
41
157
|
//#region src/utils.d.ts
|
|
42
158
|
interface CalendarCellState {
|
|
43
159
|
isWeekend: boolean;
|
|
160
|
+
/** 祝日かどうか(holidayLocale に従って判定。指定なしは "ja") */
|
|
161
|
+
isHoliday: boolean;
|
|
44
162
|
isToday: boolean;
|
|
45
163
|
isHighlight: boolean;
|
|
46
164
|
isInRange: boolean;
|
|
@@ -81,6 +199,8 @@ export declare function getCalendarCellState(date: Date, options?: {
|
|
|
81
199
|
to: Date;
|
|
82
200
|
};
|
|
83
201
|
isDateDisabled?: (date: Date) => boolean;
|
|
202
|
+
/** 祝日判定に使うロケール。省略時は "ja"(isHoliday のデフォルト) */
|
|
203
|
+
holidayLocale?: Locale;
|
|
84
204
|
}): CalendarCellState;
|
|
85
205
|
/** 日付が範囲の開始年から終了年までの月リストを返す */
|
|
86
206
|
export declare function getMonthRange(from: Date, to: Date): {
|
package/dist/index.js
CHANGED
|
@@ -256,6 +256,411 @@ function getMonthName(locale, month) {
|
|
|
256
256
|
return getLocaleData(locale).months[month - 1];
|
|
257
257
|
}
|
|
258
258
|
//#endregion
|
|
259
|
+
//#region src/date-math.ts
|
|
260
|
+
/** 加算量が整数であることを検証する */
|
|
261
|
+
function assertAmount(amount) {
|
|
262
|
+
if (!Number.isInteger(amount)) throw new RangeError(`Invalid amount: ${amount} (expected an integer)`);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* 日付に日数を加算する(負数で減算)。時刻は保持される。
|
|
266
|
+
* `setDate` ベースの暦日加算のため DST 遷移でも 1 日ずれない。
|
|
267
|
+
* 入力 Date は変更されず、新しい Date を返す。
|
|
268
|
+
*/
|
|
269
|
+
function addDays(date, amount) {
|
|
270
|
+
assertValidDate(date);
|
|
271
|
+
assertAmount(amount);
|
|
272
|
+
const result = new Date(date.getTime());
|
|
273
|
+
result.setDate(result.getDate() + amount);
|
|
274
|
+
return result;
|
|
275
|
+
}
|
|
276
|
+
/** 日付に週数を加算する(負数で減算)。addDays(date, amount * 7) と同じ */
|
|
277
|
+
function addWeeks(date, amount) {
|
|
278
|
+
return addDays(date, amount * 7);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* 日付に月数を加算する(負数で減算)。時刻は保持される。
|
|
282
|
+
* 月中旬の日付は月を跨いでも同じ日付を保つ(例: 1月15日 + 1ヶ月 = 2月15日)。
|
|
283
|
+
* 移動先の月に存在しない日付は月末に丸められる
|
|
284
|
+
* (例: 1月31日 + 1ヶ月 = 2月28日/29日、1月30日 + 1ヶ月 = 2月28日/29日)。
|
|
285
|
+
*/
|
|
286
|
+
function addMonths(date, amount) {
|
|
287
|
+
assertValidDate(date);
|
|
288
|
+
assertAmount(amount);
|
|
289
|
+
const result = startOfDay(date);
|
|
290
|
+
const targetDay = date.getDate();
|
|
291
|
+
result.setDate(1);
|
|
292
|
+
result.setMonth(result.getMonth() + amount);
|
|
293
|
+
const lastDay = daysInMonthOf(result);
|
|
294
|
+
result.setDate(Math.min(targetDay, lastDay));
|
|
295
|
+
result.setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
/** 日付に年数を加算する(負数で減算)。うるう日の 2月29日は 2月28日に丸められる */
|
|
299
|
+
function addYears(date, amount) {
|
|
300
|
+
return addMonths(date, amount * 12);
|
|
301
|
+
}
|
|
302
|
+
/** 月の日数(1-31)を返す(Date 基準) */
|
|
303
|
+
function daysInMonthOf(date) {
|
|
304
|
+
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
|
305
|
+
}
|
|
306
|
+
/** その日の 00:00:00.000 を返す */
|
|
307
|
+
function startOfDay(date) {
|
|
308
|
+
assertValidDate(date);
|
|
309
|
+
return createDate(date.getFullYear(), date.getMonth(), date.getDate());
|
|
310
|
+
}
|
|
311
|
+
/** その日の 23:59:59.999 を返す */
|
|
312
|
+
function endOfDay(date) {
|
|
313
|
+
const result = startOfDay(date);
|
|
314
|
+
result.setHours(23, 59, 59, 999);
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
/** 日付を含む週の初日(weekStart 曜日)の 00:00:00.000 を返す */
|
|
318
|
+
function startOfWeek(date, weekStart = "sunday") {
|
|
319
|
+
assertValidDate(date);
|
|
320
|
+
assertValidWeekStart(weekStart);
|
|
321
|
+
const day = startOfDay(date);
|
|
322
|
+
return addDays(day, -(weekStart === "sunday" ? day.getDay() : (day.getDay() + 6) % 7));
|
|
323
|
+
}
|
|
324
|
+
/** 日付を含む週の最終日の 23:59:59.999 を返す(週末を含む範囲に便利) */
|
|
325
|
+
function endOfWeek(date, weekStart = "sunday") {
|
|
326
|
+
return endOfDay(addDays(startOfWeek(date, weekStart), 6));
|
|
327
|
+
}
|
|
328
|
+
/** 日付を含む月の初日の 00:00:00.000 を返す */
|
|
329
|
+
function startOfMonth(date) {
|
|
330
|
+
assertValidDate(date);
|
|
331
|
+
return createDate(date.getFullYear(), date.getMonth(), 1);
|
|
332
|
+
}
|
|
333
|
+
/** 日付を含む月の最終日の 23:59:59.999 を返す */
|
|
334
|
+
function endOfMonth(date) {
|
|
335
|
+
return endOfDay(createDate(date.getFullYear(), date.getMonth() + 1, 0));
|
|
336
|
+
}
|
|
337
|
+
/** 日付を含む年の初日(1月1日)の 00:00:00.000 を返す */
|
|
338
|
+
function startOfYear(date) {
|
|
339
|
+
assertValidDate(date);
|
|
340
|
+
return createDate(date.getFullYear(), 0, 1);
|
|
341
|
+
}
|
|
342
|
+
/** 日付を含む年の最終日の 23:59:59.999 を返す */
|
|
343
|
+
function endOfYear(date) {
|
|
344
|
+
return endOfDay(createDate(date.getFullYear(), 12, 0));
|
|
345
|
+
}
|
|
346
|
+
/** 日付の「日」部分を UTC 日数(epoch からの日数)に正規化する。DST や year 0-99 の影響を受けない */
|
|
347
|
+
function toUtcDays(date) {
|
|
348
|
+
const utc = /* @__PURE__ */ new Date(0);
|
|
349
|
+
utc.setUTCFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
|
350
|
+
return utc.getTime() / 864e5;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* 2つの日付の間の暦日数(to - from)を返す。マイナスもありうる。
|
|
354
|
+
* 時刻は無視され、DST 遷移があっても 1 日単位の差は正しい。
|
|
355
|
+
*/
|
|
356
|
+
function diffInCalendarDays(from, to) {
|
|
357
|
+
assertValidDate(from);
|
|
358
|
+
assertValidDate(to);
|
|
359
|
+
return toUtcDays(to) - toUtcDays(from);
|
|
360
|
+
}
|
|
361
|
+
/** 2つの日付の間の暦月数(to - from)を返す。マイナスもありうる */
|
|
362
|
+
function diffInCalendarMonths(from, to) {
|
|
363
|
+
assertValidDate(from);
|
|
364
|
+
assertValidDate(to);
|
|
365
|
+
return (to.getFullYear() - from.getFullYear()) * 12 + (to.getMonth() - from.getMonth());
|
|
366
|
+
}
|
|
367
|
+
/** 2つの日付の間の暦年数(to - from)を返す。マイナスもありうる */
|
|
368
|
+
function diffInCalendarYears(from, to) {
|
|
369
|
+
assertValidDate(from);
|
|
370
|
+
assertValidDate(to);
|
|
371
|
+
return to.getFullYear() - from.getFullYear();
|
|
372
|
+
}
|
|
373
|
+
/** date が other より前(厳密に時刻が小さい)か */
|
|
374
|
+
function isBefore(date, other) {
|
|
375
|
+
assertValidDate(date);
|
|
376
|
+
assertValidDate(other);
|
|
377
|
+
return date.getTime() < other.getTime();
|
|
378
|
+
}
|
|
379
|
+
/** date が other より後(厳密に時刻が大きい)か */
|
|
380
|
+
function isAfter(date, other) {
|
|
381
|
+
assertValidDate(date);
|
|
382
|
+
assertValidDate(other);
|
|
383
|
+
return date.getTime() > other.getTime();
|
|
384
|
+
}
|
|
385
|
+
/** 同じ年月か(日は無視) */
|
|
386
|
+
function isSameMonth(a, b) {
|
|
387
|
+
assertValidDate(a);
|
|
388
|
+
assertValidDate(b);
|
|
389
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
390
|
+
}
|
|
391
|
+
/** 同じ年か(月・日は無視) */
|
|
392
|
+
function isSameYear(a, b) {
|
|
393
|
+
assertValidDate(a);
|
|
394
|
+
assertValidDate(b);
|
|
395
|
+
return a.getFullYear() === b.getFullYear();
|
|
396
|
+
}
|
|
397
|
+
/** 週末(土曜 or 日曜)か */
|
|
398
|
+
function isWeekend(date) {
|
|
399
|
+
assertValidDate(date);
|
|
400
|
+
const day = date.getDay();
|
|
401
|
+
return day === 0 || day === 6;
|
|
402
|
+
}
|
|
403
|
+
/** ISO 8601 の週番号(1-53)を返す。第1週は最初の木曜日を含む週 */
|
|
404
|
+
function getISOWeek(date) {
|
|
405
|
+
assertValidDate(date);
|
|
406
|
+
const day = startOfDay(date);
|
|
407
|
+
const thursday = addDays(day, 3 - (day.getDay() + 6) % 7);
|
|
408
|
+
const jan1 = createDate(thursday.getFullYear(), 0, 1);
|
|
409
|
+
const firstThursday = addDays(jan1, (3 - (jan1.getDay() + 6) % 7 + 7) % 7);
|
|
410
|
+
return Math.floor(diffInCalendarDays(firstThursday, thursday) / 7) + 1;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* 年始(1月1日)を含む週を第1週とする週番号を返す。
|
|
414
|
+
* 1月1日より前の週の末日(例: 日曜始まりの 1月1日が土曜のとき)は前年の第52週/第53週になる。
|
|
415
|
+
* 北米のカレンダー(cal -w 相当)で使われる規則。
|
|
416
|
+
*/
|
|
417
|
+
function getWeekOfYear(date, weekStart = "sunday") {
|
|
418
|
+
assertValidDate(date);
|
|
419
|
+
assertValidWeekStart(weekStart);
|
|
420
|
+
const day = startOfDay(date);
|
|
421
|
+
const weekStartJan1 = startOfWeek(createDate(day.getFullYear(), 0, 1), weekStart);
|
|
422
|
+
return Math.floor(diffInCalendarDays(weekStartJan1, day) / 7) + 1;
|
|
423
|
+
}
|
|
424
|
+
/** 年の通算日(1月1日 = 1、12月31日 = 365/366)を返す */
|
|
425
|
+
function getDayOfYear(date) {
|
|
426
|
+
assertValidDate(date);
|
|
427
|
+
return diffInCalendarDays(startOfYear(date), date) + 1;
|
|
428
|
+
}
|
|
429
|
+
/** 日付を [min, max] の範囲内にクランプする。範囲外なら min/max を、範囲内なら元の Date を返す */
|
|
430
|
+
function clampDate(date, min, max) {
|
|
431
|
+
assertValidDate(date);
|
|
432
|
+
assertValidDate(min);
|
|
433
|
+
assertValidDate(max);
|
|
434
|
+
if (date.getTime() < min.getTime()) return min;
|
|
435
|
+
if (date.getTime() > max.getTime()) return max;
|
|
436
|
+
return date;
|
|
437
|
+
}
|
|
438
|
+
const TOKEN_RE = /yyyy|MMMM|EEE|yy|MM|dd|M|d/g;
|
|
439
|
+
/**
|
|
440
|
+
* パターン文字列に従って日付をフォーマットする。
|
|
441
|
+
*
|
|
442
|
+
* 対応トークン:
|
|
443
|
+
* - `yyyy` — 4桁の年(例: 2026)
|
|
444
|
+
* - `yy` — 西暦下2桁(例: 26)
|
|
445
|
+
* - `MMMM` — フル月名(ロケール依存。例: September / 9月)
|
|
446
|
+
* - `MM` — 0埋め2桁の月(01-12)
|
|
447
|
+
* - `M` — 月(1-12)
|
|
448
|
+
* - `dd` — 0埋め2桁の日(01-31)
|
|
449
|
+
* - `d` — 日(1-31)
|
|
450
|
+
* - `EEE` — 曜日の短縮名(ロケール依存。例: Fri / 金)
|
|
451
|
+
*
|
|
452
|
+
* トークン以外の文字はそのまま出力される。
|
|
453
|
+
*
|
|
454
|
+
* ```ts
|
|
455
|
+
* formatDate(date, "yyyy-MM-dd"); // "2026-09-25"
|
|
456
|
+
* formatDate(date, "MMMM d, yyyy (EEE)"); // "September 25, 2026 (Fri)"
|
|
457
|
+
* formatDate(date, "yyyy年M月d日", "ja"); // "2026年9月25日"
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
function formatDate(date, pattern, locale = "en") {
|
|
461
|
+
assertValidDate(date);
|
|
462
|
+
const data = getLocaleData(locale);
|
|
463
|
+
return pattern.replace(TOKEN_RE, (token) => {
|
|
464
|
+
switch (token) {
|
|
465
|
+
case "yyyy": return String(date.getFullYear()).padStart(4, "0");
|
|
466
|
+
case "yy": return String(date.getFullYear() % 100).padStart(2, "0");
|
|
467
|
+
case "MMMM": return getMonthName(locale, date.getMonth() + 1);
|
|
468
|
+
case "MM": return String(date.getMonth() + 1).padStart(2, "0");
|
|
469
|
+
case "M": return String(date.getMonth() + 1);
|
|
470
|
+
case "dd": return String(date.getDate()).padStart(2, "0");
|
|
471
|
+
case "d": return String(date.getDate());
|
|
472
|
+
case "EEE": return data.weekdays[date.getDay()];
|
|
473
|
+
default: return token;
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
//#endregion
|
|
478
|
+
//#region src/holidays.ts
|
|
479
|
+
/** 祝日が計算できる年範囲(1949 年以降の現行祝日法制に基づく) */
|
|
480
|
+
const HOLIDAY_MIN_YEAR = 1949;
|
|
481
|
+
/** 祝日が計算できる年範囲(秋分の日の近似式が有効な上限。実用上十分) */
|
|
482
|
+
const HOLIDAY_MAX_YEAR = 2100;
|
|
483
|
+
/** 1回限りの祝日(皇室行事・改元に伴う法律で定められたもの): "YYYY-MM-DD" → 名称 */
|
|
484
|
+
const ONE_OFF_HOLIDAYS = {
|
|
485
|
+
"1959-04-10": "皇太子明仁親王の結婚の儀",
|
|
486
|
+
"1989-02-24": "昭和天皇の大喪の礼",
|
|
487
|
+
"1990-11-12": "即位の礼正殿の儀",
|
|
488
|
+
"1993-06-09": "皇太子徳仁親王の結婚の儀",
|
|
489
|
+
"2019-05-01": "天皇即位の日",
|
|
490
|
+
"2019-10-22": "即位礼正殿の儀"
|
|
491
|
+
};
|
|
492
|
+
/** 年月日の曜日(0=日曜) */
|
|
493
|
+
function dayOfWeek(year, month, day) {
|
|
494
|
+
return createDate(year, month - 1, day).getDay();
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* 国民の祝日に関する法律と特例法に基づき、年の祝日一覧を規則から算出する。
|
|
498
|
+
*
|
|
499
|
+
* 計算順: ①祝日(固定日・第n月曜・春分/秋分)→ ②特例の祝日 → ③振替休日
|
|
500
|
+
* (1973年以降、祝日が日曜のとき直後の祝日でない日)→ ④国民の休日
|
|
501
|
+
* (1986年以降、祝日(日曜を除く)に挟まれた平日)。
|
|
502
|
+
* 祝日の名称は「元日」「成人の日」など法律上の名称、「振替休日」「国民の休日」はそのままの名称。
|
|
503
|
+
* 2020/2021 年の東京五輪特例(海の日・山の日・スポーツの日の振替)も反映する。
|
|
504
|
+
* 秋分の日・春分の日は天文学的な秋分/春分の日付(1851〜2099 年で有効な近似式)で算出する。
|
|
505
|
+
*/
|
|
506
|
+
function holidaysOfYear(year) {
|
|
507
|
+
const list = [];
|
|
508
|
+
const add = (month, day, name) => {
|
|
509
|
+
list.push({
|
|
510
|
+
month,
|
|
511
|
+
day,
|
|
512
|
+
name
|
|
513
|
+
});
|
|
514
|
+
};
|
|
515
|
+
/** 指定月の第 n 曜日(weekday: 0=日曜 .. 6=土曜)の日付を返す */
|
|
516
|
+
const nthWeekday = (weekday, n, month) => {
|
|
517
|
+
return 1 + (weekday - createDate(year, month - 1, 1).getDay() + 7) % 7 + (n - 1) * 7;
|
|
518
|
+
};
|
|
519
|
+
const monday = (n, month) => nthWeekday(1, n, month);
|
|
520
|
+
const springEquinox = Math.floor(20.8431 + .242194 * (year - 1980) - Math.floor((year - 1980) / 4));
|
|
521
|
+
const fallEquinox = Math.floor(23.2488 + .242194 * (year - 1980) - Math.floor((year - 1980) / 4));
|
|
522
|
+
if (year >= 1949) add(1, 1, "元日");
|
|
523
|
+
if (year >= 1949) add(1, year <= 1999 ? 15 : monday(2, 1), "成人の日");
|
|
524
|
+
if (year >= 1967) add(2, 11, "建国記念の日");
|
|
525
|
+
if (year >= 1949) {
|
|
526
|
+
if (year <= 1988) add(4, 29, "天皇誕生日");
|
|
527
|
+
else if (year <= 2018) add(12, 23, "天皇誕生日");
|
|
528
|
+
else if (year >= 2020) add(2, 23, "天皇誕生日");
|
|
529
|
+
}
|
|
530
|
+
if (year >= 2007) add(4, 29, "昭和の日");
|
|
531
|
+
if (year >= 1949) add(5, 3, "憲法記念日");
|
|
532
|
+
if (year >= 1989 && year <= 2006) add(4, 29, "みどりの日");
|
|
533
|
+
if (year >= 2007) add(5, 4, "みどりの日");
|
|
534
|
+
if (year >= 1949) add(5, 5, "こどもの日");
|
|
535
|
+
if (year >= 1996) {
|
|
536
|
+
if (year <= 2002) add(7, 20, "海の日");
|
|
537
|
+
else if (year === 2020) add(7, 23, "海の日");
|
|
538
|
+
else if (year === 2021) add(7, 22, "海の日");
|
|
539
|
+
else add(7, monday(3, 7), "海の日");
|
|
540
|
+
}
|
|
541
|
+
if (year >= 2016) {
|
|
542
|
+
if (year === 2020) add(8, 10, "山の日");
|
|
543
|
+
else if (year === 2021) add(8, 8, "山の日");
|
|
544
|
+
else add(8, 11, "山の日");
|
|
545
|
+
}
|
|
546
|
+
if (year >= 1949) add(3, springEquinox, "春分の日");
|
|
547
|
+
if (year >= 1966) add(9, year <= 2002 ? 15 : monday(3, 9), "敬老の日");
|
|
548
|
+
if (year >= 1949) add(9, fallEquinox, "秋分の日");
|
|
549
|
+
if (year >= 1966) {
|
|
550
|
+
if (year <= 1999) add(10, 10, "体育の日");
|
|
551
|
+
else if (year <= 2019) add(10, monday(2, 10), "体育の日");
|
|
552
|
+
else if (year === 2020) add(7, 24, "スポーツの日");
|
|
553
|
+
else if (year === 2021) add(7, 23, "スポーツの日");
|
|
554
|
+
else add(10, monday(2, 10), "スポーツの日");
|
|
555
|
+
}
|
|
556
|
+
if (year >= 1949) add(11, 3, "文化の日");
|
|
557
|
+
if (year >= 1949) add(11, 23, "勤労感謝の日");
|
|
558
|
+
for (const [key, name] of Object.entries(ONE_OFF_HOLIDAYS)) {
|
|
559
|
+
const [y, m, d] = key.split("-").map(Number);
|
|
560
|
+
if (y === year) add(m, d, name);
|
|
561
|
+
}
|
|
562
|
+
const inList = (month, day) => list.some((h) => h.month === month && h.day === day);
|
|
563
|
+
if (year >= 1973) {
|
|
564
|
+
const substituteFrom = createDate(1973, 3, 12).getTime();
|
|
565
|
+
for (const h of [...list]) {
|
|
566
|
+
if (dayOfWeek(year, h.month, h.day) !== 0) continue;
|
|
567
|
+
const base = createDate(year, h.month - 1, h.day);
|
|
568
|
+
if (base.getTime() < substituteFrom) continue;
|
|
569
|
+
for (let i = 1; i <= 7; i++) {
|
|
570
|
+
const next = addDays(base, i);
|
|
571
|
+
if (!inList(next.getMonth() + 1, next.getDate())) {
|
|
572
|
+
add(next.getMonth() + 1, next.getDate(), "振替休日");
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
if (year >= 1986) for (let month = 1; month <= 12; month++) {
|
|
579
|
+
const days = createDate(year, month, 0).getDate();
|
|
580
|
+
for (let day = 1; day <= days; day++) {
|
|
581
|
+
if (inList(month, day)) continue;
|
|
582
|
+
const date = createDate(year, month - 1, day);
|
|
583
|
+
if (date.getDay() === 0) continue;
|
|
584
|
+
const prev = addDays(date, -1);
|
|
585
|
+
const next = addDays(date, 1);
|
|
586
|
+
if (inList(prev.getMonth() + 1, prev.getDate()) && inList(next.getMonth() + 1, next.getDate())) add(month, day, "国民の休日");
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return list;
|
|
590
|
+
}
|
|
591
|
+
/** 年の計算結果キャッシュ(同じ年を何度も参照する描画用途のため) */
|
|
592
|
+
const holidayCache = /* @__PURE__ */ new Map();
|
|
593
|
+
/** 祝日一覧を取得する。対象外ロケール・範囲外の年は null */
|
|
594
|
+
function holidaysOf(date, locale) {
|
|
595
|
+
if (locale !== "ja") return null;
|
|
596
|
+
const year = date.getFullYear();
|
|
597
|
+
if (year < 1949 || year > 2100) return null;
|
|
598
|
+
const cached = holidayCache.get(year);
|
|
599
|
+
if (cached !== void 0) return cached;
|
|
600
|
+
const list = holidaysOfYear(year);
|
|
601
|
+
holidayCache.set(year, list);
|
|
602
|
+
return list;
|
|
603
|
+
}
|
|
604
|
+
/** 日付が祝日(日本の国民の祝日・振替休日・国民の休日)か判定する。対象外ロケール/年は false */
|
|
605
|
+
function isHoliday(date, locale = "ja") {
|
|
606
|
+
assertValidDate(date);
|
|
607
|
+
const list = holidaysOf(date, locale);
|
|
608
|
+
if (list === null) return false;
|
|
609
|
+
return list.some((h) => h.month === date.getMonth() + 1 && h.day === date.getDate());
|
|
610
|
+
}
|
|
611
|
+
/** 日付の祝日名を返す。祝日でない・対象外ロケール/年は undefined */
|
|
612
|
+
function getHolidayName(date, locale = "ja") {
|
|
613
|
+
assertValidDate(date);
|
|
614
|
+
const list = holidaysOf(date, locale);
|
|
615
|
+
if (list === null) return void 0;
|
|
616
|
+
return list.find((h) => h.month === date.getMonth() + 1 && h.day === date.getDate())?.name;
|
|
617
|
+
}
|
|
618
|
+
/** 日付が営業日(平日かつ祝日でない)か判定する */
|
|
619
|
+
function isBusinessDay(date, locale = "ja") {
|
|
620
|
+
assertValidDate(date);
|
|
621
|
+
if (date.getDay() === 0 || date.getDay() === 6) return false;
|
|
622
|
+
return !isHoliday(date, locale);
|
|
623
|
+
}
|
|
624
|
+
/** 加算する営業日数が整数であることを検証する */
|
|
625
|
+
function assertBusinessAmount(amount) {
|
|
626
|
+
if (!Number.isInteger(amount)) throw new RangeError(`Invalid amount: ${amount} (expected an integer)`);
|
|
627
|
+
}
|
|
628
|
+
/**
|
|
629
|
+
* 日付に営業日数を加算する(負数で減算)。土日・祝日はスキップされる。
|
|
630
|
+
* 加算の起点は date そのもの(date 自体は数えない)。date === 戻り値になるのは amount = 0 のとき。
|
|
631
|
+
* 例: 2026-05-01(金)に 1 を足すと 2026-05-07(木)になる(5/3-5/6 が祝日)。
|
|
632
|
+
*/
|
|
633
|
+
function addBusinessDays(date, amount, locale = "ja") {
|
|
634
|
+
assertValidDate(date);
|
|
635
|
+
assertBusinessAmount(amount);
|
|
636
|
+
const result = new Date(date.getTime());
|
|
637
|
+
const step = amount >= 0 ? 1 : -1;
|
|
638
|
+
let remaining = Math.abs(amount);
|
|
639
|
+
while (remaining > 0) {
|
|
640
|
+
result.setDate(result.getDate() + step);
|
|
641
|
+
if (isBusinessDay(result, locale)) remaining--;
|
|
642
|
+
}
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* 2つの日付の間の営業日数(to - from 方向)を返す。マイナスもありうる。
|
|
647
|
+
* from 自身は数えず、to は数える。例: 月曜→火曜 = 1、金曜→翌月曜 = 1(土日はスキップ)。
|
|
648
|
+
* `addBusinessDays(from, n) === to` と `diffInBusinessDays(from, to) === n` は同値。
|
|
649
|
+
*/
|
|
650
|
+
function diffInBusinessDays(from, to, locale = "ja") {
|
|
651
|
+
assertValidDate(from);
|
|
652
|
+
assertValidDate(to);
|
|
653
|
+
const sign = to.getTime() >= from.getTime() ? 1 : -1;
|
|
654
|
+
const cursor = new Date(from.getTime());
|
|
655
|
+
let count = 0;
|
|
656
|
+
for (;;) {
|
|
657
|
+
cursor.setDate(cursor.getDate() + sign);
|
|
658
|
+
if (sign > 0 ? cursor.getTime() > to.getTime() : cursor.getTime() < to.getTime()) break;
|
|
659
|
+
if (isBusinessDay(cursor, locale)) count++;
|
|
660
|
+
}
|
|
661
|
+
return count * sign;
|
|
662
|
+
}
|
|
663
|
+
//#endregion
|
|
259
664
|
//#region src/utils.ts
|
|
260
665
|
/**
|
|
261
666
|
* 2つの日付を from <= to の順にソートした範囲を返す。
|
|
@@ -326,6 +731,7 @@ function getCalendarCellState(date, options = {}) {
|
|
|
326
731
|
assertValidDate(date);
|
|
327
732
|
return {
|
|
328
733
|
isWeekend: date.getDay() === 0 || date.getDay() === 6,
|
|
734
|
+
isHoliday: isHoliday(date, options.holidayLocale),
|
|
329
735
|
isToday: options.today !== void 0 && isSameDay(date, options.today),
|
|
330
736
|
isHighlight: options.highlight !== void 0 && isSameDay(date, options.highlight),
|
|
331
737
|
isInRange: options.range !== void 0 && isDateInRange(date, options.range),
|
|
@@ -356,6 +762,6 @@ function daysInMonth(year, month) {
|
|
|
356
762
|
return lastDayOfMonth(year, month).getDate();
|
|
357
763
|
}
|
|
358
764
|
//#endregion
|
|
359
|
-
export { LOCALES, MAX_YEAR, MIN_YEAR, assertValidDate, buildMonthGrid, createDate, daysInMonth, firstDayOfMonth, getCalendarCellState, getMonthName, getMonthRange, getWeekdayHeaders, isDateInRange, isLeapYear, isSameDay, lastDayOfMonth, sortRange };
|
|
765
|
+
export { HOLIDAY_MAX_YEAR, HOLIDAY_MIN_YEAR, LOCALES, MAX_YEAR, MIN_YEAR, addBusinessDays, addDays, addMonths, addWeeks, addYears, assertValidDate, buildMonthGrid, clampDate, createDate, daysInMonth, diffInBusinessDays, diffInCalendarDays, diffInCalendarMonths, diffInCalendarYears, endOfDay, endOfMonth, endOfWeek, endOfYear, firstDayOfMonth, formatDate, getCalendarCellState, getDayOfYear, getHolidayName, getISOWeek, getLocaleData, getMonthName, getMonthRange, getWeekOfYear, getWeekdayHeaders, isAfter, isBefore, isBusinessDay, isDateInRange, isHoliday, isLeapYear, isSameDay, isSameMonth, isSameYear, isWeekend, lastDayOfMonth, sortRange, startOfDay, startOfMonth, startOfWeek, startOfYear };
|
|
360
766
|
|
|
361
767
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/validation.ts","../src/locale.ts","../src/utils.ts"],"sourcesContent":["// ─── 入力検証 ─────────────────────────────────────────────\n\n/** サポートする年の最小値(JS Date は 0-99 を 1900+年 と解釈するため 1 から) */\nexport const MIN_YEAR = 1;\n/** サポートする年の最大値 */\nexport const MAX_YEAR = 9999;\n\n/** 値が min-max の整数であることを検証する(不正なら RangeError) */\nfunction assertIntegerInRange(\n name: string,\n value: number,\n min: number,\n max: number,\n): void {\n if (!Number.isInteger(value) || value < min || value > max) {\n throw new RangeError(\n `Invalid ${name}: ${value} (expected an integer between ${min} and ${max})`,\n );\n }\n}\n\n/** 年が 1-9999 の整数であることを検証する */\nexport function assertValidYear(year: number): void {\n assertIntegerInRange(\"year\", year, MIN_YEAR, MAX_YEAR);\n}\n\n/** 月が 1-12 の整数であることを検証する */\nexport function assertValidMonth(month: number): void {\n assertIntegerInRange(\"month\", month, 1, 12);\n}\n\n/** 年の組み合わせ(year と month)を検証する */\nexport function assertValidYearMonth(year: number, month: number): void {\n assertValidYear(year);\n assertValidMonth(month);\n}\n\n/** 週の開始曜日を検証する */\nexport function assertValidWeekStart(weekStart: string): void {\n if (weekStart !== \"sunday\" && weekStart !== \"monday\") {\n throw new RangeError(\n `Invalid weekStart: \"${weekStart}\" (expected \"sunday\" or \"monday\")`,\n );\n }\n}\n\n/** Date が有効(Invalid Date でない)ことを検証する */\nexport function assertValidDate(date: Date): void {\n if (!(date instanceof Date) || Number.isNaN(date.getTime())) {\n throw new RangeError(`Invalid Date: ${String(date)}`);\n }\n}\n\n/**\n * ローカルの指定日 00:00:00 を生成する。\n * `new Date(year, ...)` は year 0-99 を 1900+year と解釈するため、\n * setFullYear を使って year 0-99 も正しく扱えるようにする。\n *\n * - year は 1-9999、monthIndex は 0-12、day は 0-31 の整数である必要がある(それ以外は RangeError)\n * - monthIndex 12 は翌年1月(`new Date` と同じロールオーバー。lastDayOfMonth が 12月の月末取得に使う)\n * - day 0 は前月の月末(`new Date` と同じ規則)\n * - day がその月の日数を超える場合は翌月にロールする(`new Date` と同じ挙動)\n */\nexport function createDate(\n year: number,\n monthIndex: number,\n day: number,\n): Date {\n assertValidYear(year);\n assertIntegerInRange(\"monthIndex\", monthIndex, 0, 12);\n assertIntegerInRange(\"day\", day, 0, 31);\n const date = new Date(0);\n date.setHours(0, 0, 0, 0);\n date.setFullYear(year, monthIndex, day);\n return date;\n}\n","import type { Locale, WeekStart } from \"./types.ts\";\nimport { assertValidMonth, assertValidWeekStart } from \"./validation.ts\";\n\nexport interface LocaleData {\n months: readonly string[];\n weekdays: readonly string[];\n weekdaysShort: readonly string[];\n weekdaysMonday: readonly string[];\n weekdaysMondayShort: readonly string[];\n}\n\n/** 月曜始まりの配列は日曜始まりの配列を前ローテーションして生成する */\nfunction makeLocale(\n months: readonly string[],\n weekdays: readonly string[],\n weekdaysShort: readonly string[] = weekdays,\n): LocaleData {\n const rotate = (arr: readonly string[]) => [...arr.slice(1), arr[0]!];\n return {\n months,\n weekdays,\n weekdaysShort,\n weekdaysMonday: rotate(weekdays),\n weekdaysMondayShort: rotate(weekdaysShort),\n };\n}\n\nexport const LOCALES: Record<Locale, LocaleData> = {\n en: makeLocale(\n [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n ],\n [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"],\n ),\n ja: makeLocale(\n [\n \"1月\",\n \"2月\",\n \"3月\",\n \"4月\",\n \"5月\",\n \"6月\",\n \"7月\",\n \"8月\",\n \"9月\",\n \"10月\",\n \"11月\",\n \"12月\",\n ],\n [\"日\", \"月\", \"火\", \"水\", \"木\", \"金\", \"土\"],\n ),\n es: makeLocale(\n [\n \"enero\",\n \"febrero\",\n \"marzo\",\n \"abril\",\n \"mayo\",\n \"junio\",\n \"julio\",\n \"agosto\",\n \"septiembre\",\n \"octubre\",\n \"noviembre\",\n \"diciembre\",\n ],\n [\"dom\", \"lun\", \"mar\", \"mié\", \"jue\", \"vie\", \"sáb\"],\n [\"do\", \"lu\", \"ma\", \"mi\", \"ju\", \"vi\", \"sá\"],\n ),\n de: makeLocale(\n [\n \"Januar\",\n \"Februar\",\n \"März\",\n \"April\",\n \"Mai\",\n \"Juni\",\n \"Juli\",\n \"August\",\n \"September\",\n \"Oktober\",\n \"November\",\n \"Dezember\",\n ],\n [\"So\", \"Mo\", \"Di\", \"Mi\", \"Do\", \"Fr\", \"Sa\"],\n ),\n fr: makeLocale(\n [\n \"janvier\",\n \"février\",\n \"mars\",\n \"avril\",\n \"mai\",\n \"juin\",\n \"juillet\",\n \"août\",\n \"septembre\",\n \"octobre\",\n \"novembre\",\n \"décembre\",\n ],\n [\"dim.\", \"lun.\", \"mar.\", \"mer.\", \"jeu.\", \"ven.\", \"sam.\"],\n [\"di\", \"lu\", \"ma\", \"me\", \"je\", \"ve\", \"sa\"],\n ),\n ko: makeLocale(\n [\n \"1월\",\n \"2월\",\n \"3월\",\n \"4월\",\n \"5월\",\n \"6월\",\n \"7월\",\n \"8월\",\n \"9월\",\n \"10월\",\n \"11월\",\n \"12월\",\n ],\n [\"일\", \"월\", \"화\", \"수\", \"목\", \"금\", \"토\"],\n ),\n zh: makeLocale(\n [\n \"一月\",\n \"二月\",\n \"三月\",\n \"四月\",\n \"五月\",\n \"六月\",\n \"七月\",\n \"八月\",\n \"九月\",\n \"十月\",\n \"十一月\",\n \"十二月\",\n ],\n [\"日\", \"一\", \"二\", \"三\", \"四\", \"五\", \"六\"],\n ),\n};\n\n/** ロケールデータを取得する(未知のロケールは RangeError) */\nfunction getLocaleData(locale: Locale): LocaleData {\n const data = LOCALES[locale];\n if (data === undefined) {\n throw new RangeError(`Invalid locale: \"${locale}\"`);\n }\n return data;\n}\n\nexport function getWeekdayHeaders(\n locale: Locale,\n weekStart: WeekStart,\n): readonly string[] {\n assertValidWeekStart(weekStart);\n const data = getLocaleData(locale);\n return weekStart === \"monday\" ? data.weekdaysMonday : data.weekdays;\n}\n\nexport function getMonthName(locale: Locale, month: number): string {\n assertValidMonth(month);\n const data = getLocaleData(locale);\n return data.months[month - 1]!;\n}\n","import type { WeekStart } from \"./types.ts\";\nimport {\n assertValidDate,\n assertValidWeekStart,\n assertValidYear,\n assertValidYearMonth,\n createDate,\n} from \"./validation.ts\";\n\nexport interface CalendarCellState {\n isWeekend: boolean;\n isToday: boolean;\n isHighlight: boolean;\n isInRange: boolean;\n isDisabled: boolean;\n}\n\n/** 日付範囲(from <= to) */\nexport interface DateRange {\n from: Date;\n to: Date;\n}\n\n/**\n * 2つの日付を from <= to の順にソートした範囲を返す。\n * どちらかが null なら undefined(範囲なし)。\n */\nexport function sortRange(\n a: Date | null,\n b: Date | null,\n): DateRange | undefined {\n if (a === null || b === null) return undefined;\n return a.getTime() <= b.getTime() ? { from: a, to: b } : { from: b, to: a };\n}\n\n/** 月初日(1日)を返す */\nexport function firstDayOfMonth(year: number, month: number): Date {\n assertValidYearMonth(year, month);\n return createDate(year, month - 1, 1);\n}\n\n/** 月末日を返す */\nexport function lastDayOfMonth(year: number, month: number): Date {\n assertValidYearMonth(year, month);\n return createDate(year, month, 0);\n}\n\n/**\n * 月の日付グリッドを生成する(6行×7列)\n * 各セルは日数(1-31)または null(空欄)\n */\nexport function buildMonthGrid(\n year: number,\n month: number,\n weekStart: WeekStart,\n): (number | null)[][] {\n assertValidYearMonth(year, month);\n assertValidWeekStart(weekStart);\n\n const daysInMonth = lastDayOfMonth(year, month).getDate();\n let skip = firstDayOfMonth(year, month).getDay();\n if (weekStart === \"monday\") {\n skip = (skip + 6) % 7;\n }\n\n const grid: (number | null)[][] = [];\n let day = 1;\n for (let week = 0; week < 6; week++) {\n const row: (number | null)[] = [];\n for (let col = 0; col < 7; col++) {\n if (day > daysInMonth || (week === 0 && col < skip)) {\n row.push(null);\n } else {\n row.push(day++);\n }\n }\n grid.push(row);\n }\n return grid;\n}\n\n/** 範囲(from/to)が有効であることを検証する */\nfunction assertValidRange(from: Date, to: Date): void {\n assertValidDate(from);\n assertValidDate(to);\n if (from.getTime() > to.getTime()) {\n throw new RangeError(\"Invalid range: from must not be after to\");\n }\n}\n\n/** 日付が範囲内に含まれるか */\nexport function isDateInRange(\n date: Date,\n range?: { from: Date; to: Date },\n): boolean {\n if (!range) return false;\n assertValidRange(range.from, range.to);\n assertValidDate(date);\n const time = date.getTime();\n return time >= range.from.getTime() && time <= range.to.getTime();\n}\n\n/** 日付が今日と一致するか(日付のみ比較) */\nexport function isSameDay(a: Date, b: Date): boolean {\n assertValidDate(a);\n assertValidDate(b);\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** 日付セルの表示状態を計算する */\nexport function getCalendarCellState(\n date: Date,\n options: {\n today?: Date;\n highlight?: Date;\n range?: { from: Date; to: Date };\n isDateDisabled?: (date: Date) => boolean;\n } = {},\n): CalendarCellState {\n assertValidDate(date);\n return {\n isWeekend: date.getDay() === 0 || date.getDay() === 6,\n isToday: options.today !== undefined && isSameDay(date, options.today),\n isHighlight:\n options.highlight !== undefined && isSameDay(date, options.highlight),\n isInRange:\n options.range !== undefined && isDateInRange(date, options.range),\n isDisabled: options.isDateDisabled?.(date) ?? false,\n };\n}\n\n/** 日付が範囲の開始年から終了年までの月リストを返す */\nexport function getMonthRange(\n from: Date,\n to: Date,\n): { year: number; month: number }[] {\n assertValidRange(from, to);\n const start = from.getFullYear() * 12 + from.getMonth();\n const end = to.getFullYear() * 12 + to.getMonth();\n return Array.from({ length: end - start + 1 }, (_, i) => {\n const n = start + i;\n return { year: Math.floor(n / 12), month: (n % 12) + 1 };\n });\n}\n\n/** グレゴリオ暦のうるう年判定(4で割れるが100では割れない、または400で割れる) */\nexport function isLeapYear(year: number): boolean {\n assertValidYear(year);\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\n/** 月の日数(1-31)を返す */\nexport function daysInMonth(year: number, month: number): number {\n assertValidYearMonth(year, month);\n return lastDayOfMonth(year, month).getDate();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/validation.ts","../src/locale.ts","../src/date-math.ts","../src/holidays.ts","../src/utils.ts"],"sourcesContent":["// ─── 入力検証 ─────────────────────────────────────────────\n\n/** サポートする年の最小値(JS Date は 0-99 を 1900+年 と解釈するため 1 から) */\nexport const MIN_YEAR = 1;\n/** サポートする年の最大値 */\nexport const MAX_YEAR = 9999;\n\n/** 値が min-max の整数であることを検証する(不正なら RangeError) */\nfunction assertIntegerInRange(\n name: string,\n value: number,\n min: number,\n max: number,\n): void {\n if (!Number.isInteger(value) || value < min || value > max) {\n throw new RangeError(\n `Invalid ${name}: ${value} (expected an integer between ${min} and ${max})`,\n );\n }\n}\n\n/** 年が 1-9999 の整数であることを検証する */\nexport function assertValidYear(year: number): void {\n assertIntegerInRange(\"year\", year, MIN_YEAR, MAX_YEAR);\n}\n\n/** 月が 1-12 の整数であることを検証する */\nexport function assertValidMonth(month: number): void {\n assertIntegerInRange(\"month\", month, 1, 12);\n}\n\n/** 年の組み合わせ(year と month)を検証する */\nexport function assertValidYearMonth(year: number, month: number): void {\n assertValidYear(year);\n assertValidMonth(month);\n}\n\n/** 週の開始曜日を検証する */\nexport function assertValidWeekStart(weekStart: string): void {\n if (weekStart !== \"sunday\" && weekStart !== \"monday\") {\n throw new RangeError(\n `Invalid weekStart: \"${weekStart}\" (expected \"sunday\" or \"monday\")`,\n );\n }\n}\n\n/** Date が有効(Invalid Date でない)ことを検証する */\nexport function assertValidDate(date: Date): void {\n if (!(date instanceof Date) || Number.isNaN(date.getTime())) {\n throw new RangeError(`Invalid Date: ${String(date)}`);\n }\n}\n\n/**\n * ローカルの指定日 00:00:00 を生成する。\n * `new Date(year, ...)` は year 0-99 を 1900+year と解釈するため、\n * setFullYear を使って year 0-99 も正しく扱えるようにする。\n *\n * - year は 1-9999、monthIndex は 0-12、day は 0-31 の整数である必要がある(それ以外は RangeError)\n * - monthIndex 12 は翌年1月(`new Date` と同じロールオーバー。lastDayOfMonth が 12月の月末取得に使う)\n * - day 0 は前月の月末(`new Date` と同じ規則)\n * - day がその月の日数を超える場合は翌月にロールする(`new Date` と同じ挙動)\n */\nexport function createDate(\n year: number,\n monthIndex: number,\n day: number,\n): Date {\n assertValidYear(year);\n assertIntegerInRange(\"monthIndex\", monthIndex, 0, 12);\n assertIntegerInRange(\"day\", day, 0, 31);\n const date = new Date(0);\n date.setHours(0, 0, 0, 0);\n date.setFullYear(year, monthIndex, day);\n return date;\n}\n","import type { Locale, WeekStart } from \"./types.ts\";\nimport { assertValidMonth, assertValidWeekStart } from \"./validation.ts\";\n\nexport interface LocaleData {\n months: readonly string[];\n weekdays: readonly string[];\n weekdaysShort: readonly string[];\n weekdaysMonday: readonly string[];\n weekdaysMondayShort: readonly string[];\n}\n\n/** 月曜始まりの配列は日曜始まりの配列を前ローテーションして生成する */\nfunction makeLocale(\n months: readonly string[],\n weekdays: readonly string[],\n weekdaysShort: readonly string[] = weekdays,\n): LocaleData {\n const rotate = (arr: readonly string[]) => [...arr.slice(1), arr[0]!];\n return {\n months,\n weekdays,\n weekdaysShort,\n weekdaysMonday: rotate(weekdays),\n weekdaysMondayShort: rotate(weekdaysShort),\n };\n}\n\nexport const LOCALES: Record<Locale, LocaleData> = {\n en: makeLocale(\n [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n ],\n [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"],\n ),\n ja: makeLocale(\n [\n \"1月\",\n \"2月\",\n \"3月\",\n \"4月\",\n \"5月\",\n \"6月\",\n \"7月\",\n \"8月\",\n \"9月\",\n \"10月\",\n \"11月\",\n \"12月\",\n ],\n [\"日\", \"月\", \"火\", \"水\", \"木\", \"金\", \"土\"],\n ),\n es: makeLocale(\n [\n \"enero\",\n \"febrero\",\n \"marzo\",\n \"abril\",\n \"mayo\",\n \"junio\",\n \"julio\",\n \"agosto\",\n \"septiembre\",\n \"octubre\",\n \"noviembre\",\n \"diciembre\",\n ],\n [\"dom\", \"lun\", \"mar\", \"mié\", \"jue\", \"vie\", \"sáb\"],\n [\"do\", \"lu\", \"ma\", \"mi\", \"ju\", \"vi\", \"sá\"],\n ),\n de: makeLocale(\n [\n \"Januar\",\n \"Februar\",\n \"März\",\n \"April\",\n \"Mai\",\n \"Juni\",\n \"Juli\",\n \"August\",\n \"September\",\n \"Oktober\",\n \"November\",\n \"Dezember\",\n ],\n [\"So\", \"Mo\", \"Di\", \"Mi\", \"Do\", \"Fr\", \"Sa\"],\n ),\n fr: makeLocale(\n [\n \"janvier\",\n \"février\",\n \"mars\",\n \"avril\",\n \"mai\",\n \"juin\",\n \"juillet\",\n \"août\",\n \"septembre\",\n \"octobre\",\n \"novembre\",\n \"décembre\",\n ],\n [\"dim.\", \"lun.\", \"mar.\", \"mer.\", \"jeu.\", \"ven.\", \"sam.\"],\n [\"di\", \"lu\", \"ma\", \"me\", \"je\", \"ve\", \"sa\"],\n ),\n ko: makeLocale(\n [\n \"1월\",\n \"2월\",\n \"3월\",\n \"4월\",\n \"5월\",\n \"6월\",\n \"7월\",\n \"8월\",\n \"9월\",\n \"10월\",\n \"11월\",\n \"12월\",\n ],\n [\"일\", \"월\", \"화\", \"수\", \"목\", \"금\", \"토\"],\n ),\n zh: makeLocale(\n [\n \"一月\",\n \"二月\",\n \"三月\",\n \"四月\",\n \"五月\",\n \"六月\",\n \"七月\",\n \"八月\",\n \"九月\",\n \"十月\",\n \"十一月\",\n \"十二月\",\n ],\n [\"日\", \"一\", \"二\", \"三\", \"四\", \"五\", \"六\"],\n ),\n};\n\n/** ロケールデータを取得する(未知のロケールは RangeError) */\nexport function getLocaleData(locale: Locale): LocaleData {\n const data = LOCALES[locale];\n if (data === undefined) {\n throw new RangeError(`Invalid locale: \"${locale}\"`);\n }\n return data;\n}\n\nexport function getWeekdayHeaders(\n locale: Locale,\n weekStart: WeekStart,\n): readonly string[] {\n assertValidWeekStart(weekStart);\n const data = getLocaleData(locale);\n return weekStart === \"monday\" ? data.weekdaysMonday : data.weekdays;\n}\n\nexport function getMonthName(locale: Locale, month: number): string {\n assertValidMonth(month);\n const data = getLocaleData(locale);\n return data.months[month - 1]!;\n}\n","import { getLocaleData, getMonthName } from \"./locale.ts\";\nimport type { Locale, WeekStart } from \"./types.ts\";\nimport {\n assertValidDate,\n assertValidWeekStart,\n createDate,\n} from \"./validation.ts\";\n\n// ─── 日付演算 ─────────────────────────────────────────────\n\n/** 加算量が整数であることを検証する */\nfunction assertAmount(amount: number): void {\n if (!Number.isInteger(amount)) {\n throw new RangeError(`Invalid amount: ${amount} (expected an integer)`);\n }\n}\n\n/**\n * 日付に日数を加算する(負数で減算)。時刻は保持される。\n * `setDate` ベースの暦日加算のため DST 遷移でも 1 日ずれない。\n * 入力 Date は変更されず、新しい Date を返す。\n */\nexport function addDays(date: Date, amount: number): Date {\n assertValidDate(date);\n assertAmount(amount);\n const result = new Date(date.getTime());\n result.setDate(result.getDate() + amount);\n return result;\n}\n\n/** 日付に週数を加算する(負数で減算)。addDays(date, amount * 7) と同じ */\nexport function addWeeks(date: Date, amount: number): Date {\n return addDays(date, amount * 7);\n}\n\n/**\n * 日付に月数を加算する(負数で減算)。時刻は保持される。\n * 月中旬の日付は月を跨いでも同じ日付を保つ(例: 1月15日 + 1ヶ月 = 2月15日)。\n * 移動先の月に存在しない日付は月末に丸められる\n * (例: 1月31日 + 1ヶ月 = 2月28日/29日、1月30日 + 1ヶ月 = 2月28日/29日)。\n */\nexport function addMonths(date: Date, amount: number): Date {\n assertValidDate(date);\n assertAmount(amount);\n const result = startOfDay(date);\n const targetDay = date.getDate();\n result.setDate(1);\n result.setMonth(result.getMonth() + amount);\n const lastDay = daysInMonthOf(result);\n result.setDate(Math.min(targetDay, lastDay));\n // 時刻を復元する\n result.setHours(\n date.getHours(),\n date.getMinutes(),\n date.getSeconds(),\n date.getMilliseconds(),\n );\n return result;\n}\n\n/** 日付に年数を加算する(負数で減算)。うるう日の 2月29日は 2月28日に丸められる */\nexport function addYears(date: Date, amount: number): Date {\n return addMonths(date, amount * 12);\n}\n\n/** 月の日数(1-31)を返す(Date 基準) */\nfunction daysInMonthOf(date: Date): number {\n return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();\n}\n\n// ─── 期間の境界 ───────────────────────────────────────────\n\n/** その日の 00:00:00.000 を返す */\nexport function startOfDay(date: Date): Date {\n assertValidDate(date);\n return createDate(date.getFullYear(), date.getMonth(), date.getDate());\n}\n\n/** その日の 23:59:59.999 を返す */\nexport function endOfDay(date: Date): Date {\n const result = startOfDay(date);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/** 日付を含む週の初日(weekStart 曜日)の 00:00:00.000 を返す */\nexport function startOfWeek(date: Date, weekStart: WeekStart = \"sunday\"): Date {\n assertValidDate(date);\n assertValidWeekStart(weekStart);\n const day = startOfDay(date);\n const offset = weekStart === \"sunday\" ? day.getDay() : (day.getDay() + 6) % 7;\n return addDays(day, -offset);\n}\n\n/** 日付を含む週の最終日の 23:59:59.999 を返す(週末を含む範囲に便利) */\nexport function endOfWeek(date: Date, weekStart: WeekStart = \"sunday\"): Date {\n return endOfDay(addDays(startOfWeek(date, weekStart), 6));\n}\n\n/** 日付を含む月の初日の 00:00:00.000 を返す */\nexport function startOfMonth(date: Date): Date {\n assertValidDate(date);\n return createDate(date.getFullYear(), date.getMonth(), 1);\n}\n\n/** 日付を含む月の最終日の 23:59:59.999 を返す */\nexport function endOfMonth(date: Date): Date {\n return endOfDay(createDate(date.getFullYear(), date.getMonth() + 1, 0));\n}\n\n/** 日付を含む年の初日(1月1日)の 00:00:00.000 を返す */\nexport function startOfYear(date: Date): Date {\n assertValidDate(date);\n return createDate(date.getFullYear(), 0, 1);\n}\n\n/** 日付を含む年の最終日の 23:59:59.999 を返す */\nexport function endOfYear(date: Date): Date {\n return endOfDay(createDate(date.getFullYear(), 12, 0));\n}\n\n// ─── 差(暦日ベース) ─────────────────────────────────────\n\n/** 日付の「日」部分を UTC 日数(epoch からの日数)に正規化する。DST や year 0-99 の影響を受けない */\nfunction toUtcDays(date: Date): number {\n const utc = new Date(0);\n utc.setUTCFullYear(date.getFullYear(), date.getMonth(), date.getDate());\n return utc.getTime() / 86_400_000;\n}\n\n/**\n * 2つの日付の間の暦日数(to - from)を返す。マイナスもありうる。\n * 時刻は無視され、DST 遷移があっても 1 日単位の差は正しい。\n */\nexport function diffInCalendarDays(from: Date, to: Date): number {\n assertValidDate(from);\n assertValidDate(to);\n return toUtcDays(to) - toUtcDays(from);\n}\n\n/** 2つの日付の間の暦月数(to - from)を返す。マイナスもありうる */\nexport function diffInCalendarMonths(from: Date, to: Date): number {\n assertValidDate(from);\n assertValidDate(to);\n return (\n (to.getFullYear() - from.getFullYear()) * 12 +\n (to.getMonth() - from.getMonth())\n );\n}\n\n/** 2つの日付の間の暦年数(to - from)を返す。マイナスもありうる */\nexport function diffInCalendarYears(from: Date, to: Date): number {\n assertValidDate(from);\n assertValidDate(to);\n return to.getFullYear() - from.getFullYear();\n}\n\n// ─── 比較 ─────────────────────────────────────────────────\n\n/** date が other より前(厳密に時刻が小さい)か */\nexport function isBefore(date: Date, other: Date): boolean {\n assertValidDate(date);\n assertValidDate(other);\n return date.getTime() < other.getTime();\n}\n\n/** date が other より後(厳密に時刻が大きい)か */\nexport function isAfter(date: Date, other: Date): boolean {\n assertValidDate(date);\n assertValidDate(other);\n return date.getTime() > other.getTime();\n}\n\n/** 同じ年月か(日は無視) */\nexport function isSameMonth(a: Date, b: Date): boolean {\n assertValidDate(a);\n assertValidDate(b);\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();\n}\n\n/** 同じ年か(月・日は無視) */\nexport function isSameYear(a: Date, b: Date): boolean {\n assertValidDate(a);\n assertValidDate(b);\n return a.getFullYear() === b.getFullYear();\n}\n\n/** 週末(土曜 or 日曜)か */\nexport function isWeekend(date: Date): boolean {\n assertValidDate(date);\n const day = date.getDay();\n return day === 0 || day === 6;\n}\n\n// ─── 年・週の位置 ─────────────────────────────────────────\n\n/** ISO 8601 の週番号(1-53)を返す。第1週は最初の木曜日を含む週 */\nexport function getISOWeek(date: Date): number {\n assertValidDate(date);\n const day = startOfDay(date);\n const weekday = (day.getDay() + 6) % 7; // 月=0 ... 日=6\n // 今週の木曜日(ISO 年の基準。月曜〜日曜のどれを取っても同じ週番号になる)\n const thursday = addDays(day, 3 - weekday);\n const jan1 = createDate(thursday.getFullYear(), 0, 1);\n const jan1Weekday = (jan1.getDay() + 6) % 7;\n const firstThursday = addDays(jan1, (3 - jan1Weekday + 7) % 7);\n return Math.floor(diffInCalendarDays(firstThursday, thursday) / 7) + 1;\n}\n\n/**\n * 年始(1月1日)を含む週を第1週とする週番号を返す。\n * 1月1日より前の週の末日(例: 日曜始まりの 1月1日が土曜のとき)は前年の第52週/第53週になる。\n * 北米のカレンダー(cal -w 相当)で使われる規則。\n */\nexport function getWeekOfYear(\n date: Date,\n weekStart: WeekStart = \"sunday\",\n): number {\n assertValidDate(date);\n assertValidWeekStart(weekStart);\n const day = startOfDay(date);\n const jan1 = createDate(day.getFullYear(), 0, 1);\n const weekStartJan1 = startOfWeek(jan1, weekStart);\n return Math.floor(diffInCalendarDays(weekStartJan1, day) / 7) + 1;\n}\n\n/** 年の通算日(1月1日 = 1、12月31日 = 365/366)を返す */\nexport function getDayOfYear(date: Date): number {\n assertValidDate(date);\n return diffInCalendarDays(startOfYear(date), date) + 1;\n}\n\n// ─── その他 ───────────────────────────────────────────────\n\n/** 日付を [min, max] の範囲内にクランプする。範囲外なら min/max を、範囲内なら元の Date を返す */\nexport function clampDate(date: Date, min: Date, max: Date): Date {\n assertValidDate(date);\n assertValidDate(min);\n assertValidDate(max);\n if (date.getTime() < min.getTime()) return min;\n if (date.getTime() > max.getTime()) return max;\n return date;\n}\n\nconst TOKEN_RE = /yyyy|MMMM|EEE|yy|MM|dd|M|d/g;\n\n/**\n * パターン文字列に従って日付をフォーマットする。\n *\n * 対応トークン:\n * - `yyyy` — 4桁の年(例: 2026)\n * - `yy` — 西暦下2桁(例: 26)\n * - `MMMM` — フル月名(ロケール依存。例: September / 9月)\n * - `MM` — 0埋め2桁の月(01-12)\n * - `M` — 月(1-12)\n * - `dd` — 0埋め2桁の日(01-31)\n * - `d` — 日(1-31)\n * - `EEE` — 曜日の短縮名(ロケール依存。例: Fri / 金)\n *\n * トークン以外の文字はそのまま出力される。\n *\n * ```ts\n * formatDate(date, \"yyyy-MM-dd\"); // \"2026-09-25\"\n * formatDate(date, \"MMMM d, yyyy (EEE)\"); // \"September 25, 2026 (Fri)\"\n * formatDate(date, \"yyyy年M月d日\", \"ja\"); // \"2026年9月25日\"\n * ```\n */\nexport function formatDate(\n date: Date,\n pattern: string,\n locale: Locale = \"en\",\n): string {\n assertValidDate(date);\n const data = getLocaleData(locale);\n return pattern.replace(TOKEN_RE, (token) => {\n switch (token) {\n case \"yyyy\":\n return String(date.getFullYear()).padStart(4, \"0\");\n case \"yy\":\n return String(date.getFullYear() % 100).padStart(2, \"0\");\n case \"MMMM\":\n return getMonthName(locale, date.getMonth() + 1);\n case \"MM\":\n return String(date.getMonth() + 1).padStart(2, \"0\");\n case \"M\":\n return String(date.getMonth() + 1);\n case \"dd\":\n return String(date.getDate()).padStart(2, \"0\");\n case \"d\":\n return String(date.getDate());\n case \"EEE\":\n return data.weekdays[date.getDay()]!;\n default:\n return token;\n }\n });\n}\n","import { addDays } from \"./date-math.ts\";\nimport type { Locale } from \"./types.ts\";\nimport { assertValidDate, createDate } from \"./validation.ts\";\n\n// ─── 日本の祝日 ───────────────────────────────────────────\n\n/**\n * 祝日データを持つロケール。現在は日本語(日本の国民の祝日)のみ。\n * その他のロケールは「祝日なし」として扱われる(isHoliday は常に false)。\n */\nexport type HolidayLocale = \"ja\";\n\n/** 祝日が計算できる年範囲(1949 年以降の現行祝日法制に基づく) */\nexport const HOLIDAY_MIN_YEAR = 1949;\n\n/** 祝日が計算できる年範囲(秋分の日の近似式が有効な上限。実用上十分) */\nexport const HOLIDAY_MAX_YEAR = 2100;\n\n/** 1回限りの祝日(皇室行事・改元に伴う法律で定められたもの): \"YYYY-MM-DD\" → 名称 */\nconst ONE_OFF_HOLIDAYS: Readonly<Record<string, string>> = {\n \"1959-04-10\": \"皇太子明仁親王の結婚の儀\",\n \"1989-02-24\": \"昭和天皇の大喪の礼\",\n \"1990-11-12\": \"即位の礼正殿の儀\",\n \"1993-06-09\": \"皇太子徳仁親王の結婚の儀\",\n \"2019-05-01\": \"天皇即位の日\",\n \"2019-10-22\": \"即位礼正殿の儀\",\n};\n\n/** 年の祝日一覧の1件(month は 1-12) */\ninterface YearHoliday {\n month: number;\n day: number;\n name: string;\n}\n\n/** 年月日の曜日(0=日曜) */\nfunction dayOfWeek(year: number, month: number, day: number): number {\n return createDate(year, month - 1, day).getDay();\n}\n\n/**\n * 国民の祝日に関する法律と特例法に基づき、年の祝日一覧を規則から算出する。\n *\n * 計算順: ①祝日(固定日・第n月曜・春分/秋分)→ ②特例の祝日 → ③振替休日\n * (1973年以降、祝日が日曜のとき直後の祝日でない日)→ ④国民の休日\n * (1986年以降、祝日(日曜を除く)に挟まれた平日)。\n * 祝日の名称は「元日」「成人の日」など法律上の名称、「振替休日」「国民の休日」はそのままの名称。\n * 2020/2021 年の東京五輪特例(海の日・山の日・スポーツの日の振替)も反映する。\n * 秋分の日・春分の日は天文学的な秋分/春分の日付(1851〜2099 年で有効な近似式)で算出する。\n */\nfunction holidaysOfYear(year: number): YearHoliday[] {\n const list: YearHoliday[] = [];\n const add = (month: number, day: number, name: string): void => {\n list.push({ month, day, name });\n };\n /** 指定月の第 n 曜日(weekday: 0=日曜 .. 6=土曜)の日付を返す */\n const nthWeekday = (weekday: number, n: number, month: number): number => {\n const first = createDate(year, month - 1, 1);\n const offset = (weekday - first.getDay() + 7) % 7;\n return 1 + offset + (n - 1) * 7;\n };\n const monday = (n: number, month: number): number => nthWeekday(1, n, month);\n const springEquinox = Math.floor(\n 20.8431 + 0.242194 * (year - 1980) - Math.floor((year - 1980) / 4),\n );\n const fallEquinox = Math.floor(\n 23.2488 + 0.242194 * (year - 1980) - Math.floor((year - 1980) / 4),\n );\n\n // ── ① 国民の祝日(法律上の規則) ──\n if (year >= 1949) add(1, 1, \"元日\");\n if (year >= 1949) {\n // 成人の日: 2000年以降は1月第2月曜\n add(1, year <= 1999 ? 15 : monday(2, 1), \"成人の日\");\n }\n if (year >= 1967) add(2, 11, \"建国記念の日\");\n if (year >= 1949) {\n // 天皇誕生日: 4/29(〜1988)→ 12/23(1989-2018)→ 2019年はなし → 2/23(2020-)\n if (year <= 1988) {\n add(4, 29, \"天皇誕生日\");\n } else if (year <= 2018) {\n add(12, 23, \"天皇誕生日\");\n } else if (year >= 2020) {\n add(2, 23, \"天皇誕生日\");\n }\n }\n if (year >= 2007) add(4, 29, \"昭和の日\");\n if (year >= 1949) add(5, 3, \"憲法記念日\");\n if (year >= 1989 && year <= 2006) add(4, 29, \"みどりの日\");\n if (year >= 2007) add(5, 4, \"みどりの日\");\n if (year >= 1949) add(5, 5, \"こどもの日\");\n if (year >= 1996) {\n // 海の日: 7/20(〜2002)→ 第3月曜(2003-)。2020/2021 は東京五輪特例\n if (year <= 2002) {\n add(7, 20, \"海の日\");\n } else if (year === 2020) {\n add(7, 23, \"海の日\");\n } else if (year === 2021) {\n add(7, 22, \"海の日\");\n } else {\n add(7, monday(3, 7), \"海の日\");\n }\n }\n if (year >= 2016) {\n // 山の日: 8/11。2020/2021 は東京五輪特例\n if (year === 2020) {\n add(8, 10, \"山の日\");\n } else if (year === 2021) {\n add(8, 8, \"山の日\");\n } else {\n add(8, 11, \"山の日\");\n }\n }\n if (year >= 1949) add(3, springEquinox, \"春分の日\");\n if (year >= 1966) {\n // 敬老の日: 9/15(〜2002)→ 第3月曜(2003-)\n add(9, year <= 2002 ? 15 : monday(3, 9), \"敬老の日\");\n }\n if (year >= 1949) add(9, fallEquinox, \"秋分の日\");\n if (year >= 1966) {\n // 体育の日: 10/10(〜1999)→ 第2月曜(2000-2019)→ スポーツの日(2020-)。\n // 2020/2021 は東京五輪特例\n if (year <= 1999) {\n add(10, 10, \"体育の日\");\n } else if (year <= 2019) {\n add(10, monday(2, 10), \"体育の日\");\n } else if (year === 2020) {\n add(7, 24, \"スポーツの日\");\n } else if (year === 2021) {\n add(7, 23, \"スポーツの日\");\n } else {\n add(10, monday(2, 10), \"スポーツの日\");\n }\n }\n if (year >= 1949) add(11, 3, \"文化の日\");\n if (year >= 1949) add(11, 23, \"勤労感謝の日\");\n\n // ── ② 特例の祝日 ──\n for (const [key, name] of Object.entries(ONE_OFF_HOLIDAYS)) {\n const [y, m, d] = key.split(\"-\").map(Number);\n if (y === year) add(m! as number, d! as number, name);\n }\n\n const inList = (month: number, day: number): boolean =>\n list.some((h) => h.month === month && h.day === day);\n\n // ── ③ 振替休日(1973年の法改正により同年4月12日から施行): 祝日が日曜に当たるとき、直後の祝日でない日を休日とする ──\n if (year >= 1973) {\n const substituteFrom = createDate(1973, 3, 12).getTime(); // 制度施行日 1973-04-12\n for (const h of [...list]) {\n if (dayOfWeek(year, h.month, h.day) !== 0) continue;\n const base = createDate(year, h.month - 1, h.day);\n if (base.getTime() < substituteFrom) continue;\n for (let i = 1; i <= 7; i++) {\n const next = addDays(base, i);\n if (!inList(next.getMonth() + 1, next.getDate())) {\n add(next.getMonth() + 1, next.getDate(), \"振替休日\");\n break;\n }\n }\n }\n }\n\n // ── ④ 国民の休日(1986年の法改正以降): 祝日(日曜を除く)に挟まれた平日を休日とする ──\n if (year >= 1986) {\n for (let month = 1; month <= 12; month++) {\n const days = createDate(year, month, 0).getDate();\n for (let day = 1; day <= days; day++) {\n if (inList(month, day)) continue;\n const date = createDate(year, month - 1, day);\n if (date.getDay() === 0) continue;\n const prev = addDays(date, -1);\n const next = addDays(date, 1);\n if (\n inList(prev.getMonth() + 1, prev.getDate()) &&\n inList(next.getMonth() + 1, next.getDate())\n ) {\n add(month, day, \"国民の休日\");\n }\n }\n }\n }\n\n return list;\n}\n\n/** 年の計算結果キャッシュ(同じ年を何度も参照する描画用途のため) */\nconst holidayCache = new Map<number, YearHoliday[]>();\n\n/** 祝日一覧を取得する。対象外ロケール・範囲外の年は null */\nfunction holidaysOf(date: Date, locale: Locale): YearHoliday[] | null {\n if (locale !== \"ja\") return null;\n const year = date.getFullYear();\n if (year < HOLIDAY_MIN_YEAR || year > HOLIDAY_MAX_YEAR) return null;\n const cached = holidayCache.get(year);\n if (cached !== undefined) return cached;\n const list = holidaysOfYear(year);\n holidayCache.set(year, list);\n return list;\n}\n\n/** 日付が祝日(日本の国民の祝日・振替休日・国民の休日)か判定する。対象外ロケール/年は false */\nexport function isHoliday(date: Date, locale: Locale = \"ja\"): boolean {\n assertValidDate(date);\n const list = holidaysOf(date, locale);\n if (list === null) return false;\n return list.some(\n (h) => h.month === date.getMonth() + 1 && h.day === date.getDate(),\n );\n}\n\n/** 日付の祝日名を返す。祝日でない・対象外ロケール/年は undefined */\nexport function getHolidayName(\n date: Date,\n locale: Locale = \"ja\",\n): string | undefined {\n assertValidDate(date);\n const list = holidaysOf(date, locale);\n if (list === null) return undefined;\n return list.find(\n (h) => h.month === date.getMonth() + 1 && h.day === date.getDate(),\n )?.name;\n}\n\n// ─── 営業日 ───────────────────────────────────────────────\n\n/** 日付が営業日(平日かつ祝日でない)か判定する */\nexport function isBusinessDay(date: Date, locale: Locale = \"ja\"): boolean {\n assertValidDate(date);\n if (date.getDay() === 0 || date.getDay() === 6) return false;\n return !isHoliday(date, locale);\n}\n\n/** 加算する営業日数が整数であることを検証する */\nfunction assertBusinessAmount(amount: number): void {\n if (!Number.isInteger(amount)) {\n throw new RangeError(`Invalid amount: ${amount} (expected an integer)`);\n }\n}\n\n/**\n * 日付に営業日数を加算する(負数で減算)。土日・祝日はスキップされる。\n * 加算の起点は date そのもの(date 自体は数えない)。date === 戻り値になるのは amount = 0 のとき。\n * 例: 2026-05-01(金)に 1 を足すと 2026-05-07(木)になる(5/3-5/6 が祝日)。\n */\nexport function addBusinessDays(\n date: Date,\n amount: number,\n locale: Locale = \"ja\",\n): Date {\n assertValidDate(date);\n assertBusinessAmount(amount);\n const result = new Date(date.getTime());\n const step = amount >= 0 ? 1 : -1;\n let remaining = Math.abs(amount);\n while (remaining > 0) {\n result.setDate(result.getDate() + step);\n if (isBusinessDay(result, locale)) remaining--;\n }\n return result;\n}\n\n/**\n * 2つの日付の間の営業日数(to - from 方向)を返す。マイナスもありうる。\n * from 自身は数えず、to は数える。例: 月曜→火曜 = 1、金曜→翌月曜 = 1(土日はスキップ)。\n * `addBusinessDays(from, n) === to` と `diffInBusinessDays(from, to) === n` は同値。\n */\nexport function diffInBusinessDays(\n from: Date,\n to: Date,\n locale: Locale = \"ja\",\n): number {\n assertValidDate(from);\n assertValidDate(to);\n const sign = to.getTime() >= from.getTime() ? 1 : -1;\n const cursor = new Date(from.getTime());\n let count = 0;\n for (;;) {\n cursor.setDate(cursor.getDate() + sign);\n if (\n sign > 0\n ? cursor.getTime() > to.getTime()\n : cursor.getTime() < to.getTime()\n ) {\n break;\n }\n if (isBusinessDay(cursor, locale)) count++;\n }\n return count * sign;\n}\n","import { isHoliday } from \"./holidays.ts\";\nimport type { Locale, WeekStart } from \"./types.ts\";\nimport {\n assertValidDate,\n assertValidWeekStart,\n assertValidYear,\n assertValidYearMonth,\n createDate,\n} from \"./validation.ts\";\n\nexport interface CalendarCellState {\n isWeekend: boolean;\n /** 祝日かどうか(holidayLocale に従って判定。指定なしは \"ja\") */\n isHoliday: boolean;\n isToday: boolean;\n isHighlight: boolean;\n isInRange: boolean;\n isDisabled: boolean;\n}\n\n/** 日付範囲(from <= to) */\nexport interface DateRange {\n from: Date;\n to: Date;\n}\n\n/**\n * 2つの日付を from <= to の順にソートした範囲を返す。\n * どちらかが null なら undefined(範囲なし)。\n */\nexport function sortRange(\n a: Date | null,\n b: Date | null,\n): DateRange | undefined {\n if (a === null || b === null) return undefined;\n return a.getTime() <= b.getTime() ? { from: a, to: b } : { from: b, to: a };\n}\n\n/** 月初日(1日)を返す */\nexport function firstDayOfMonth(year: number, month: number): Date {\n assertValidYearMonth(year, month);\n return createDate(year, month - 1, 1);\n}\n\n/** 月末日を返す */\nexport function lastDayOfMonth(year: number, month: number): Date {\n assertValidYearMonth(year, month);\n return createDate(year, month, 0);\n}\n\n/**\n * 月の日付グリッドを生成する(6行×7列)\n * 各セルは日数(1-31)または null(空欄)\n */\nexport function buildMonthGrid(\n year: number,\n month: number,\n weekStart: WeekStart,\n): (number | null)[][] {\n assertValidYearMonth(year, month);\n assertValidWeekStart(weekStart);\n\n const daysInMonth = lastDayOfMonth(year, month).getDate();\n let skip = firstDayOfMonth(year, month).getDay();\n if (weekStart === \"monday\") {\n skip = (skip + 6) % 7;\n }\n\n const grid: (number | null)[][] = [];\n let day = 1;\n for (let week = 0; week < 6; week++) {\n const row: (number | null)[] = [];\n for (let col = 0; col < 7; col++) {\n if (day > daysInMonth || (week === 0 && col < skip)) {\n row.push(null);\n } else {\n row.push(day++);\n }\n }\n grid.push(row);\n }\n return grid;\n}\n\n/** 範囲(from/to)が有効であることを検証する */\nfunction assertValidRange(from: Date, to: Date): void {\n assertValidDate(from);\n assertValidDate(to);\n if (from.getTime() > to.getTime()) {\n throw new RangeError(\"Invalid range: from must not be after to\");\n }\n}\n\n/** 日付が範囲内に含まれるか */\nexport function isDateInRange(\n date: Date,\n range?: { from: Date; to: Date },\n): boolean {\n if (!range) return false;\n assertValidRange(range.from, range.to);\n assertValidDate(date);\n const time = date.getTime();\n return time >= range.from.getTime() && time <= range.to.getTime();\n}\n\n/** 日付が今日と一致するか(日付のみ比較) */\nexport function isSameDay(a: Date, b: Date): boolean {\n assertValidDate(a);\n assertValidDate(b);\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** 日付セルの表示状態を計算する */\nexport function getCalendarCellState(\n date: Date,\n options: {\n today?: Date;\n highlight?: Date;\n range?: { from: Date; to: Date };\n isDateDisabled?: (date: Date) => boolean;\n /** 祝日判定に使うロケール。省略時は \"ja\"(isHoliday のデフォルト) */\n holidayLocale?: Locale;\n } = {},\n): CalendarCellState {\n assertValidDate(date);\n return {\n isWeekend: date.getDay() === 0 || date.getDay() === 6,\n isHoliday: isHoliday(date, options.holidayLocale),\n isToday: options.today !== undefined && isSameDay(date, options.today),\n isHighlight:\n options.highlight !== undefined && isSameDay(date, options.highlight),\n isInRange:\n options.range !== undefined && isDateInRange(date, options.range),\n isDisabled: options.isDateDisabled?.(date) ?? false,\n };\n}\n\n/** 日付が範囲の開始年から終了年までの月リストを返す */\nexport function getMonthRange(\n from: Date,\n to: Date,\n): { year: number; month: number }[] {\n assertValidRange(from, to);\n const start = from.getFullYear() * 12 + from.getMonth();\n const end = to.getFullYear() * 12 + to.getMonth();\n return Array.from({ length: end - start + 1 }, (_, i) => {\n const n = start + i;\n return { year: Math.floor(n / 12), month: (n % 12) + 1 };\n });\n}\n\n/** グレゴリオ暦のうるう年判定(4で割れるが100では割れない、または400で割れる) */\nexport function isLeapYear(year: number): boolean {\n assertValidYear(year);\n return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;\n}\n\n/** 月の日数(1-31)を返す */\nexport function daysInMonth(year: number, month: number): number {\n assertValidYearMonth(year, month);\n return lastDayOfMonth(year, month).getDate();\n}\n"],"mappings":";;AAGA,MAAa,WAAW;;AAExB,MAAa,WAAW;;AAGxB,SAAS,qBACP,MACA,OACA,KACA,KACM;CACN,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,OAAO,QAAQ,KACrD,MAAM,IAAI,WACR,WAAW,KAAK,IAAI,MAAM,gCAAgC,IAAI,OAAO,IAAI,EAC3E;AAEJ;;AAGA,SAAgB,gBAAgB,MAAoB;CAClD,qBAAqB,QAAQ,MAAA,GAAgB,QAAQ;AACvD;;AAGA,SAAgB,iBAAiB,OAAqB;CACpD,qBAAqB,SAAS,OAAO,GAAG,EAAE;AAC5C;;AAGA,SAAgB,qBAAqB,MAAc,OAAqB;CACtE,gBAAgB,IAAI;CACpB,iBAAiB,KAAK;AACxB;;AAGA,SAAgB,qBAAqB,WAAyB;CAC5D,IAAI,cAAc,YAAY,cAAc,UAC1C,MAAM,IAAI,WACR,uBAAuB,UAAU,kCACnC;AAEJ;;AAGA,SAAgB,gBAAgB,MAAkB;CAChD,IAAI,EAAE,gBAAgB,SAAS,OAAO,MAAM,KAAK,QAAQ,CAAC,GACxD,MAAM,IAAI,WAAW,iBAAiB,OAAO,IAAI,GAAG;AAExD;;;;;;;;;;;AAYA,SAAgB,WACd,MACA,YACA,KACM;CACN,gBAAgB,IAAI;CACpB,qBAAqB,cAAc,YAAY,GAAG,EAAE;CACpD,qBAAqB,OAAO,KAAK,GAAG,EAAE;CACtC,MAAM,uBAAO,IAAI,KAAK,CAAC;CACvB,KAAK,SAAS,GAAG,GAAG,GAAG,CAAC;CACxB,KAAK,YAAY,MAAM,YAAY,GAAG;CACtC,OAAO;AACT;;;;AC/DA,SAAS,WACP,QACA,UACA,gBAAmC,UACvB;CACZ,MAAM,UAAU,QAA2B,CAAC,GAAG,IAAI,MAAM,CAAC,GAAG,IAAI,EAAG;CACpE,OAAO;EACL;EACA;EACA;EACA,gBAAgB,OAAO,QAAQ;EAC/B,qBAAqB,OAAO,aAAa;CAC3C;AACF;AAEA,MAAa,UAAsC;CACjD,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;CAAK,GAChD;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;CAAI,CAC3C;CACA,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;CAAG,CACpC;CACA,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAO;EAAO;EAAO;EAAO;EAAO;EAAO;CAAK,GAChD;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;CAAI,CAC3C;CACA,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;CAAI,CAC3C;CACA,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;EAAQ;CAAM,GACvD;EAAC;EAAM;EAAM;EAAM;EAAM;EAAM;EAAM;CAAI,CAC3C;CACA,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;CAAG,CACpC;CACA,IAAI,WACF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,GACA;EAAC;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;CAAG,CACpC;AACF;;AAGA,SAAgB,cAAc,QAA4B;CACxD,MAAM,OAAO,QAAQ;CACrB,IAAI,SAAS,KAAA,GACX,MAAM,IAAI,WAAW,oBAAoB,OAAO,EAAE;CAEpD,OAAO;AACT;AAEA,SAAgB,kBACd,QACA,WACmB;CACnB,qBAAqB,SAAS;CAC9B,MAAM,OAAO,cAAc,MAAM;CACjC,OAAO,cAAc,WAAW,KAAK,iBAAiB,KAAK;AAC7D;AAEA,SAAgB,aAAa,QAAgB,OAAuB;CAClE,iBAAiB,KAAK;CAEtB,OADa,cAAc,MACjB,CAAC,CAAC,OAAO,QAAQ;AAC7B;;;;ACnKA,SAAS,aAAa,QAAsB;CAC1C,IAAI,CAAC,OAAO,UAAU,MAAM,GAC1B,MAAM,IAAI,WAAW,mBAAmB,OAAO,uBAAuB;AAE1E;;;;;;AAOA,SAAgB,QAAQ,MAAY,QAAsB;CACxD,gBAAgB,IAAI;CACpB,aAAa,MAAM;CACnB,MAAM,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC;CACtC,OAAO,QAAQ,OAAO,QAAQ,IAAI,MAAM;CACxC,OAAO;AACT;;AAGA,SAAgB,SAAS,MAAY,QAAsB;CACzD,OAAO,QAAQ,MAAM,SAAS,CAAC;AACjC;;;;;;;AAQA,SAAgB,UAAU,MAAY,QAAsB;CAC1D,gBAAgB,IAAI;CACpB,aAAa,MAAM;CACnB,MAAM,SAAS,WAAW,IAAI;CAC9B,MAAM,YAAY,KAAK,QAAQ;CAC/B,OAAO,QAAQ,CAAC;CAChB,OAAO,SAAS,OAAO,SAAS,IAAI,MAAM;CAC1C,MAAM,UAAU,cAAc,MAAM;CACpC,OAAO,QAAQ,KAAK,IAAI,WAAW,OAAO,CAAC;CAE3C,OAAO,SACL,KAAK,SAAS,GACd,KAAK,WAAW,GAChB,KAAK,WAAW,GAChB,KAAK,gBAAgB,CACvB;CACA,OAAO;AACT;;AAGA,SAAgB,SAAS,MAAY,QAAsB;CACzD,OAAO,UAAU,MAAM,SAAS,EAAE;AACpC;;AAGA,SAAS,cAAc,MAAoB;CACzC,OAAO,IAAI,KAAK,KAAK,YAAY,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ;AACtE;;AAKA,SAAgB,WAAW,MAAkB;CAC3C,gBAAgB,IAAI;CACpB,OAAO,WAAW,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,KAAK,QAAQ,CAAC;AACvE;;AAGA,SAAgB,SAAS,MAAkB;CACzC,MAAM,SAAS,WAAW,IAAI;CAC9B,OAAO,SAAS,IAAI,IAAI,IAAI,GAAG;CAC/B,OAAO;AACT;;AAGA,SAAgB,YAAY,MAAY,YAAuB,UAAgB;CAC7E,gBAAgB,IAAI;CACpB,qBAAqB,SAAS;CAC9B,MAAM,MAAM,WAAW,IAAI;CAE3B,OAAO,QAAQ,KAAK,EADL,cAAc,WAAW,IAAI,OAAO,KAAK,IAAI,OAAO,IAAI,KAAK,EACjD;AAC7B;;AAGA,SAAgB,UAAU,MAAY,YAAuB,UAAgB;CAC3E,OAAO,SAAS,QAAQ,YAAY,MAAM,SAAS,GAAG,CAAC,CAAC;AAC1D;;AAGA,SAAgB,aAAa,MAAkB;CAC7C,gBAAgB,IAAI;CACpB,OAAO,WAAW,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,CAAC;AAC1D;;AAGA,SAAgB,WAAW,MAAkB;CAC3C,OAAO,SAAS,WAAW,KAAK,YAAY,GAAG,KAAK,SAAS,IAAI,GAAG,CAAC,CAAC;AACxE;;AAGA,SAAgB,YAAY,MAAkB;CAC5C,gBAAgB,IAAI;CACpB,OAAO,WAAW,KAAK,YAAY,GAAG,GAAG,CAAC;AAC5C;;AAGA,SAAgB,UAAU,MAAkB;CAC1C,OAAO,SAAS,WAAW,KAAK,YAAY,GAAG,IAAI,CAAC,CAAC;AACvD;;AAKA,SAAS,UAAU,MAAoB;CACrC,MAAM,sBAAM,IAAI,KAAK,CAAC;CACtB,IAAI,eAAe,KAAK,YAAY,GAAG,KAAK,SAAS,GAAG,KAAK,QAAQ,CAAC;CACtE,OAAO,IAAI,QAAQ,IAAI;AACzB;;;;;AAMA,SAAgB,mBAAmB,MAAY,IAAkB;CAC/D,gBAAgB,IAAI;CACpB,gBAAgB,EAAE;CAClB,OAAO,UAAU,EAAE,IAAI,UAAU,IAAI;AACvC;;AAGA,SAAgB,qBAAqB,MAAY,IAAkB;CACjE,gBAAgB,IAAI;CACpB,gBAAgB,EAAE;CAClB,QACG,GAAG,YAAY,IAAI,KAAK,YAAY,KAAK,MACzC,GAAG,SAAS,IAAI,KAAK,SAAS;AAEnC;;AAGA,SAAgB,oBAAoB,MAAY,IAAkB;CAChE,gBAAgB,IAAI;CACpB,gBAAgB,EAAE;CAClB,OAAO,GAAG,YAAY,IAAI,KAAK,YAAY;AAC7C;;AAKA,SAAgB,SAAS,MAAY,OAAsB;CACzD,gBAAgB,IAAI;CACpB,gBAAgB,KAAK;CACrB,OAAO,KAAK,QAAQ,IAAI,MAAM,QAAQ;AACxC;;AAGA,SAAgB,QAAQ,MAAY,OAAsB;CACxD,gBAAgB,IAAI;CACpB,gBAAgB,KAAK;CACrB,OAAO,KAAK,QAAQ,IAAI,MAAM,QAAQ;AACxC;;AAGA,SAAgB,YAAY,GAAS,GAAkB;CACrD,gBAAgB,CAAC;CACjB,gBAAgB,CAAC;CACjB,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY,KAAK,EAAE,SAAS,MAAM,EAAE,SAAS;AAC5E;;AAGA,SAAgB,WAAW,GAAS,GAAkB;CACpD,gBAAgB,CAAC;CACjB,gBAAgB,CAAC;CACjB,OAAO,EAAE,YAAY,MAAM,EAAE,YAAY;AAC3C;;AAGA,SAAgB,UAAU,MAAqB;CAC7C,gBAAgB,IAAI;CACpB,MAAM,MAAM,KAAK,OAAO;CACxB,OAAO,QAAQ,KAAK,QAAQ;AAC9B;;AAKA,SAAgB,WAAW,MAAoB;CAC7C,gBAAgB,IAAI;CACpB,MAAM,MAAM,WAAW,IAAI;CAG3B,MAAM,WAAW,QAAQ,KAAK,KAFb,IAAI,OAAO,IAAI,KAAK,CAEI;CACzC,MAAM,OAAO,WAAW,SAAS,YAAY,GAAG,GAAG,CAAC;CAEpD,MAAM,gBAAgB,QAAQ,OAAO,KADhB,KAAK,OAAO,IAAI,KAAK,IACa,KAAK,CAAC;CAC7D,OAAO,KAAK,MAAM,mBAAmB,eAAe,QAAQ,IAAI,CAAC,IAAI;AACvE;;;;;;AAOA,SAAgB,cACd,MACA,YAAuB,UACf;CACR,gBAAgB,IAAI;CACpB,qBAAqB,SAAS;CAC9B,MAAM,MAAM,WAAW,IAAI;CAE3B,MAAM,gBAAgB,YADT,WAAW,IAAI,YAAY,GAAG,GAAG,CACT,GAAG,SAAS;CACjD,OAAO,KAAK,MAAM,mBAAmB,eAAe,GAAG,IAAI,CAAC,IAAI;AAClE;;AAGA,SAAgB,aAAa,MAAoB;CAC/C,gBAAgB,IAAI;CACpB,OAAO,mBAAmB,YAAY,IAAI,GAAG,IAAI,IAAI;AACvD;;AAKA,SAAgB,UAAU,MAAY,KAAW,KAAiB;CAChE,gBAAgB,IAAI;CACpB,gBAAgB,GAAG;CACnB,gBAAgB,GAAG;CACnB,IAAI,KAAK,QAAQ,IAAI,IAAI,QAAQ,GAAG,OAAO;CAC3C,IAAI,KAAK,QAAQ,IAAI,IAAI,QAAQ,GAAG,OAAO;CAC3C,OAAO;AACT;AAEA,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;AAuBjB,SAAgB,WACd,MACA,SACA,SAAiB,MACT;CACR,gBAAgB,IAAI;CACpB,MAAM,OAAO,cAAc,MAAM;CACjC,OAAO,QAAQ,QAAQ,WAAW,UAAU;EAC1C,QAAQ,OAAR;GACE,KAAK,QACH,OAAO,OAAO,KAAK,YAAY,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;GACnD,KAAK,MACH,OAAO,OAAO,KAAK,YAAY,IAAI,GAAG,CAAC,CAAC,SAAS,GAAG,GAAG;GACzD,KAAK,QACH,OAAO,aAAa,QAAQ,KAAK,SAAS,IAAI,CAAC;GACjD,KAAK,MACH,OAAO,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;GACpD,KAAK,KACH,OAAO,OAAO,KAAK,SAAS,IAAI,CAAC;GACnC,KAAK,MACH,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;GAC/C,KAAK,KACH,OAAO,OAAO,KAAK,QAAQ,CAAC;GAC9B,KAAK,OACH,OAAO,KAAK,SAAS,KAAK,OAAO;GACnC,SACE,OAAO;EACX;CACF,CAAC;AACH;;;;AC3RA,MAAa,mBAAmB;;AAGhC,MAAa,mBAAmB;;AAGhC,MAAM,mBAAqD;CACzD,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;CACd,cAAc;AAChB;;AAUA,SAAS,UAAU,MAAc,OAAe,KAAqB;CACnE,OAAO,WAAW,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO;AACjD;;;;;;;;;;;AAYA,SAAS,eAAe,MAA6B;CACnD,MAAM,OAAsB,CAAC;CAC7B,MAAM,OAAO,OAAe,KAAa,SAAuB;EAC9D,KAAK,KAAK;GAAE;GAAO;GAAK;EAAK,CAAC;CAChC;;CAEA,MAAM,cAAc,SAAiB,GAAW,UAA0B;EAGxE,OAAO,KADS,UADF,WAAW,MAAM,QAAQ,GAAG,CACZ,CAAC,CAAC,OAAO,IAAI,KAAK,KAC3B,IAAI,KAAK;CAChC;CACA,MAAM,UAAU,GAAW,UAA0B,WAAW,GAAG,GAAG,KAAK;CAC3E,MAAM,gBAAgB,KAAK,MACzB,UAAU,WAAY,OAAO,QAAQ,KAAK,OAAO,OAAO,QAAQ,CAAC,CACnE;CACA,MAAM,cAAc,KAAK,MACvB,UAAU,WAAY,OAAO,QAAQ,KAAK,OAAO,OAAO,QAAQ,CAAC,CACnE;CAGA,IAAI,QAAQ,MAAM,IAAI,GAAG,GAAG,IAAI;CAChC,IAAI,QAAQ,MAEV,IAAI,GAAG,QAAQ,OAAO,KAAK,OAAO,GAAG,CAAC,GAAG,MAAM;CAEjD,IAAI,QAAQ,MAAM,IAAI,GAAG,IAAI,QAAQ;CACrC,IAAI,QAAQ,MAAM;EAEhB,IAAI,QAAQ,MACV,IAAI,GAAG,IAAI,OAAO;OACb,IAAI,QAAQ,MACjB,IAAI,IAAI,IAAI,OAAO;OACd,IAAI,QAAQ,MACjB,IAAI,GAAG,IAAI,OAAO;CAEtB;CACA,IAAI,QAAQ,MAAM,IAAI,GAAG,IAAI,MAAM;CACnC,IAAI,QAAQ,MAAM,IAAI,GAAG,GAAG,OAAO;CACnC,IAAI,QAAQ,QAAQ,QAAQ,MAAM,IAAI,GAAG,IAAI,OAAO;CACpD,IAAI,QAAQ,MAAM,IAAI,GAAG,GAAG,OAAO;CACnC,IAAI,QAAQ,MAAM,IAAI,GAAG,GAAG,OAAO;CACnC,IAAI,QAAQ,MAAM;EAEhB,IAAI,QAAQ,MACV,IAAI,GAAG,IAAI,KAAK;OACX,IAAI,SAAS,MAClB,IAAI,GAAG,IAAI,KAAK;OACX,IAAI,SAAS,MAClB,IAAI,GAAG,IAAI,KAAK;OAEhB,IAAI,GAAG,OAAO,GAAG,CAAC,GAAG,KAAK;CAE9B;CACA,IAAI,QAAQ,MAAM;EAEhB,IAAI,SAAS,MACX,IAAI,GAAG,IAAI,KAAK;OACX,IAAI,SAAS,MAClB,IAAI,GAAG,GAAG,KAAK;OAEf,IAAI,GAAG,IAAI,KAAK;CAEpB;CACA,IAAI,QAAQ,MAAM,IAAI,GAAG,eAAe,MAAM;CAC9C,IAAI,QAAQ,MAEV,IAAI,GAAG,QAAQ,OAAO,KAAK,OAAO,GAAG,CAAC,GAAG,MAAM;CAEjD,IAAI,QAAQ,MAAM,IAAI,GAAG,aAAa,MAAM;CAC5C,IAAI,QAAQ,MAAM;EAGhB,IAAI,QAAQ,MACV,IAAI,IAAI,IAAI,MAAM;OACb,IAAI,QAAQ,MACjB,IAAI,IAAI,OAAO,GAAG,EAAE,GAAG,MAAM;OACxB,IAAI,SAAS,MAClB,IAAI,GAAG,IAAI,QAAQ;OACd,IAAI,SAAS,MAClB,IAAI,GAAG,IAAI,QAAQ;OAEnB,IAAI,IAAI,OAAO,GAAG,EAAE,GAAG,QAAQ;CAEnC;CACA,IAAI,QAAQ,MAAM,IAAI,IAAI,GAAG,MAAM;CACnC,IAAI,QAAQ,MAAM,IAAI,IAAI,IAAI,QAAQ;CAGtC,KAAK,MAAM,CAAC,KAAK,SAAS,OAAO,QAAQ,gBAAgB,GAAG;EAC1D,MAAM,CAAC,GAAG,GAAG,KAAK,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;EAC3C,IAAI,MAAM,MAAM,IAAI,GAAc,GAAc,IAAI;CACtD;CAEA,MAAM,UAAU,OAAe,QAC7B,KAAK,MAAM,MAAM,EAAE,UAAU,SAAS,EAAE,QAAQ,GAAG;CAGrD,IAAI,QAAQ,MAAM;EAChB,MAAM,iBAAiB,WAAW,MAAM,GAAG,EAAE,CAAC,CAAC,QAAQ;EACvD,KAAK,MAAM,KAAK,CAAC,GAAG,IAAI,GAAG;GACzB,IAAI,UAAU,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG;GAC3C,MAAM,OAAO,WAAW,MAAM,EAAE,QAAQ,GAAG,EAAE,GAAG;GAChD,IAAI,KAAK,QAAQ,IAAI,gBAAgB;GACrC,KAAK,IAAI,IAAI,GAAG,KAAK,GAAG,KAAK;IAC3B,MAAM,OAAO,QAAQ,MAAM,CAAC;IAC5B,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,QAAQ,CAAC,GAAG;KAChD,IAAI,KAAK,SAAS,IAAI,GAAG,KAAK,QAAQ,GAAG,MAAM;KAC/C;IACF;GACF;EACF;CACF;CAGA,IAAI,QAAQ,MACV,KAAK,IAAI,QAAQ,GAAG,SAAS,IAAI,SAAS;EACxC,MAAM,OAAO,WAAW,MAAM,OAAO,CAAC,CAAC,CAAC,QAAQ;EAChD,KAAK,IAAI,MAAM,GAAG,OAAO,MAAM,OAAO;GACpC,IAAI,OAAO,OAAO,GAAG,GAAG;GACxB,MAAM,OAAO,WAAW,MAAM,QAAQ,GAAG,GAAG;GAC5C,IAAI,KAAK,OAAO,MAAM,GAAG;GACzB,MAAM,OAAO,QAAQ,MAAM,EAAE;GAC7B,MAAM,OAAO,QAAQ,MAAM,CAAC;GAC5B,IACE,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,QAAQ,CAAC,KAC1C,OAAO,KAAK,SAAS,IAAI,GAAG,KAAK,QAAQ,CAAC,GAE1C,IAAI,OAAO,KAAK,OAAO;EAE3B;CACF;CAGF,OAAO;AACT;;AAGA,MAAM,+BAAe,IAAI,IAA2B;;AAGpD,SAAS,WAAW,MAAY,QAAsC;CACpE,IAAI,WAAW,MAAM,OAAO;CAC5B,MAAM,OAAO,KAAK,YAAY;CAC9B,IAAI,OAAA,QAA2B,OAAA,MAAyB,OAAO;CAC/D,MAAM,SAAS,aAAa,IAAI,IAAI;CACpC,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,MAAM,OAAO,eAAe,IAAI;CAChC,aAAa,IAAI,MAAM,IAAI;CAC3B,OAAO;AACT;;AAGA,SAAgB,UAAU,MAAY,SAAiB,MAAe;CACpE,gBAAgB,IAAI;CACpB,MAAM,OAAO,WAAW,MAAM,MAAM;CACpC,IAAI,SAAS,MAAM,OAAO;CAC1B,OAAO,KAAK,MACT,MAAM,EAAE,UAAU,KAAK,SAAS,IAAI,KAAK,EAAE,QAAQ,KAAK,QAAQ,CACnE;AACF;;AAGA,SAAgB,eACd,MACA,SAAiB,MACG;CACpB,gBAAgB,IAAI;CACpB,MAAM,OAAO,WAAW,MAAM,MAAM;CACpC,IAAI,SAAS,MAAM,OAAO,KAAA;CAC1B,OAAO,KAAK,MACT,MAAM,EAAE,UAAU,KAAK,SAAS,IAAI,KAAK,EAAE,QAAQ,KAAK,QAAQ,CACnE,CAAC,EAAE;AACL;;AAKA,SAAgB,cAAc,MAAY,SAAiB,MAAe;CACxE,gBAAgB,IAAI;CACpB,IAAI,KAAK,OAAO,MAAM,KAAK,KAAK,OAAO,MAAM,GAAG,OAAO;CACvD,OAAO,CAAC,UAAU,MAAM,MAAM;AAChC;;AAGA,SAAS,qBAAqB,QAAsB;CAClD,IAAI,CAAC,OAAO,UAAU,MAAM,GAC1B,MAAM,IAAI,WAAW,mBAAmB,OAAO,uBAAuB;AAE1E;;;;;;AAOA,SAAgB,gBACd,MACA,QACA,SAAiB,MACX;CACN,gBAAgB,IAAI;CACpB,qBAAqB,MAAM;CAC3B,MAAM,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC;CACtC,MAAM,OAAO,UAAU,IAAI,IAAI;CAC/B,IAAI,YAAY,KAAK,IAAI,MAAM;CAC/B,OAAO,YAAY,GAAG;EACpB,OAAO,QAAQ,OAAO,QAAQ,IAAI,IAAI;EACtC,IAAI,cAAc,QAAQ,MAAM,GAAG;CACrC;CACA,OAAO;AACT;;;;;;AAOA,SAAgB,mBACd,MACA,IACA,SAAiB,MACT;CACR,gBAAgB,IAAI;CACpB,gBAAgB,EAAE;CAClB,MAAM,OAAO,GAAG,QAAQ,KAAK,KAAK,QAAQ,IAAI,IAAI;CAClD,MAAM,SAAS,IAAI,KAAK,KAAK,QAAQ,CAAC;CACtC,IAAI,QAAQ;CACZ,SAAS;EACP,OAAO,QAAQ,OAAO,QAAQ,IAAI,IAAI;EACtC,IACE,OAAO,IACH,OAAO,QAAQ,IAAI,GAAG,QAAQ,IAC9B,OAAO,QAAQ,IAAI,GAAG,QAAQ,GAElC;EAEF,IAAI,cAAc,QAAQ,MAAM,GAAG;CACrC;CACA,OAAO,QAAQ;AACjB;;;;;;;ACnQA,SAAgB,UACd,GACA,GACuB;CACvB,IAAI,MAAM,QAAQ,MAAM,MAAM,OAAO,KAAA;CACrC,OAAO,EAAE,QAAQ,KAAK,EAAE,QAAQ,IAAI;EAAE,MAAM;EAAG,IAAI;CAAE,IAAI;EAAE,MAAM;EAAG,IAAI;CAAE;AAC5E;;AAGA,SAAgB,gBAAgB,MAAc,OAAqB;CACjE,qBAAqB,MAAM,KAAK;CAChC,OAAO,WAAW,MAAM,QAAQ,GAAG,CAAC;AACtC;;AAGA,SAAgB,eAAe,MAAc,OAAqB;CAChE,qBAAqB,MAAM,KAAK;CAChC,OAAO,WAAW,MAAM,OAAO,CAAC;AAClC;;;;;AAMA,SAAgB,eACd,MACA,OACA,WACqB;CACrB,qBAAqB,MAAM,KAAK;CAChC,qBAAqB,SAAS;CAE9B,MAAM,cAAc,eAAe,MAAM,KAAK,CAAC,CAAC,QAAQ;CACxD,IAAI,OAAO,gBAAgB,MAAM,KAAK,CAAC,CAAC,OAAO;CAC/C,IAAI,cAAc,UAChB,QAAQ,OAAO,KAAK;CAGtB,MAAM,OAA4B,CAAC;CACnC,IAAI,MAAM;CACV,KAAK,IAAI,OAAO,GAAG,OAAO,GAAG,QAAQ;EACnC,MAAM,MAAyB,CAAC;EAChC,KAAK,IAAI,MAAM,GAAG,MAAM,GAAG,OACzB,IAAI,MAAM,eAAgB,SAAS,KAAK,MAAM,MAC5C,IAAI,KAAK,IAAI;OAEb,IAAI,KAAK,KAAK;EAGlB,KAAK,KAAK,GAAG;CACf;CACA,OAAO;AACT;;AAGA,SAAS,iBAAiB,MAAY,IAAgB;CACpD,gBAAgB,IAAI;CACpB,gBAAgB,EAAE;CAClB,IAAI,KAAK,QAAQ,IAAI,GAAG,QAAQ,GAC9B,MAAM,IAAI,WAAW,0CAA0C;AAEnE;;AAGA,SAAgB,cACd,MACA,OACS;CACT,IAAI,CAAC,OAAO,OAAO;CACnB,iBAAiB,MAAM,MAAM,MAAM,EAAE;CACrC,gBAAgB,IAAI;CACpB,MAAM,OAAO,KAAK,QAAQ;CAC1B,OAAO,QAAQ,MAAM,KAAK,QAAQ,KAAK,QAAQ,MAAM,GAAG,QAAQ;AAClE;;AAGA,SAAgB,UAAU,GAAS,GAAkB;CACnD,gBAAgB,CAAC;CACjB,gBAAgB,CAAC;CACjB,OACE,EAAE,YAAY,MAAM,EAAE,YAAY,KAClC,EAAE,SAAS,MAAM,EAAE,SAAS,KAC5B,EAAE,QAAQ,MAAM,EAAE,QAAQ;AAE9B;;AAGA,SAAgB,qBACd,MACA,UAOI,CAAC,GACc;CACnB,gBAAgB,IAAI;CACpB,OAAO;EACL,WAAW,KAAK,OAAO,MAAM,KAAK,KAAK,OAAO,MAAM;EACpD,WAAW,UAAU,MAAM,QAAQ,aAAa;EAChD,SAAS,QAAQ,UAAU,KAAA,KAAa,UAAU,MAAM,QAAQ,KAAK;EACrE,aACE,QAAQ,cAAc,KAAA,KAAa,UAAU,MAAM,QAAQ,SAAS;EACtE,WACE,QAAQ,UAAU,KAAA,KAAa,cAAc,MAAM,QAAQ,KAAK;EAClE,YAAY,QAAQ,iBAAiB,IAAI,KAAK;CAChD;AACF;;AAGA,SAAgB,cACd,MACA,IACmC;CACnC,iBAAiB,MAAM,EAAE;CACzB,MAAM,QAAQ,KAAK,YAAY,IAAI,KAAK,KAAK,SAAS;CACtD,MAAM,MAAM,GAAG,YAAY,IAAI,KAAK,GAAG,SAAS;CAChD,OAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,QAAQ,EAAE,IAAI,GAAG,MAAM;EACvD,MAAM,IAAI,QAAQ;EAClB,OAAO;GAAE,MAAM,KAAK,MAAM,IAAI,EAAE;GAAG,OAAQ,IAAI,KAAM;EAAE;CACzD,CAAC;AACH;;AAGA,SAAgB,WAAW,MAAuB;CAChD,gBAAgB,IAAI;CACpB,OAAQ,OAAO,MAAM,KAAK,OAAO,QAAQ,KAAM,OAAO,QAAQ;AAChE;;AAGA,SAAgB,YAAY,MAAc,OAAuB;CAC/D,qBAAqB,MAAM,KAAK;CAChC,OAAO,eAAe,MAAM,KAAK,CAAC,CAAC,QAAQ;AAC7C"}
|