bizdays-toolkit 0.12.0 → 0.14.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 +46 -1
- package/README.md +59 -2
- package/dist/core/calendar.d.ts +19 -2
- package/dist/core/calendar.js +85 -43
- package/dist/core/input.d.ts +16 -0
- package/dist/core/input.js +50 -0
- package/dist/core/month.d.ts +17 -0
- package/dist/core/month.js +34 -4
- package/dist/core/schedule.d.ts +7 -0
- package/dist/core/schedule.js +15 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,49 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.14.0] - 2026-10-20
|
|
8
|
+
|
|
9
|
+
### 移行
|
|
10
|
+
|
|
11
|
+
動作は変わりません(締め期間と、暦日と時刻から瞬間への変換の追加)。版ごとの作業は [docs/migration.md](./docs/migration.md) にまとめた。
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- 締め日で区切ったときのその月分の期間を返す `closingPeriod(monthOrDate, closingDay)` を追加した(`closingMonth` の逆算。前月の締め日の翌日からその月の締め日まで、両端を含む `{ from, to }`)。暦日を渡すとその日が属する期間。返す値は変更できず、日付の期間の形なので `closedDates` や `listBusinessDays` にそのまま渡せる。締め日がその月にない場合の読み替え(末日)は `closingMonth` と同じ内部関数で行い、期間が隙間も重なりもなく敷き詰められることをテストで確かめた
|
|
16
|
+
- 暦日と時刻(`"HH:mm"` または `"HH:mm:ss"`)からタイムゾーンでの瞬間を `Date` で返す `toInstant(date, time, timeZone)` と、営業日カレンダー・スケジュールのメソッド `instantOf(date, time)`(そのタイムゾーンで。合成したカレンダー・スケジュールからのカレンダーにも付く)を追加した。`civilOf`・`toDateTimeParts` の逆向き。夏時間で存在しない時刻は飛んだ分だけ後ろに、2 回ある時刻は先のほう(Temporal の `disambiguation: "compatible"` と同じ)。`Intl` だけで実装し、オフセットの差が 1 時間でないゾーン(`Australia/Lord_Howe`)と南半球(`Pacific/Auckland`)でも確かめた。受け付けるのは暦日の文字列だけ(`invalid-date`)。時刻の誤りは `invalid-time`
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- README: 月のヘルパーの節に `closingPeriod` を、日付・日時の入力の節に `toInstant`・`instantOf` と夏時間の例を足した。サイズの値を測り直した(ルート約 9.3KB・日本の祝日込み約 11.1KB・英国の祝日込み約 11.1KB。0.14.0 で測定)
|
|
21
|
+
- `docs/decisions.md`: 39 番(`closingPeriod`)・40 番(`toInstant`)を追加した
|
|
22
|
+
- `src/core/schedule.ts` の `buildSchedule` の注釈の位置を直した(0.13.0 で関数の間に別の定義が入り、注釈が離れていた。動作は変わらない)
|
|
23
|
+
|
|
24
|
+
### 開発
|
|
25
|
+
|
|
26
|
+
- サイズの上限(`size-limits.json`)を上げた。ルート 20,500 → 22,000、`./jp` 23,500 → 25,000、`./diagnose` 27,000 → 28,500(gzip)。`closingPeriod` と瞬間への変換(ルートで約 1.3KB)のため。0.13.0 で上限に近づいていた分も含めて余裕を持たせた
|
|
27
|
+
|
|
28
|
+
## [0.13.0] - 2026-10-20
|
|
29
|
+
|
|
30
|
+
### 移行
|
|
31
|
+
|
|
32
|
+
動作は変わりません(スケジュールから営業日カレンダーを作る関数と、判定の理由の新しい値の追加。CI の改善)。版ごとの作業は [docs/migration.md](./docs/migration.md) にまとめた。
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- スケジュールから営業日カレンダーを作る `createCalendarFromSchedule(schedule, isOpen, { maxSearchDays? })` を追加した。`isOpen` が true を返す値の日を営業日とする通常のカレンダーを返し、営業時間表のようなスケジュールが唯一の設定になる。`isOpen` は値だけを受け取る純粋な関数(日付は渡さない。営業日の探索で繰り返し呼ぶため)。`dayInfo` の `reason` はスケジュールの `reason` と営業・休業の組み合わせで、`name`・`holiday`・`shiftedFrom`・`rulePath` はスケジュールの結果を引き継ぐ。祝日・タイムゾーン・`outOfRange` はスケジュールのもの。`createSchedule` が作ったスケジュール以外は `invalid-argument`
|
|
37
|
+
- 判定の理由(`DayReason`)に `"open-annual"`(スケジュールの `annual` の値で営業になる日)を追加した。`createCalendar` の結果には現れない(`closedAnnual` は休業だけ)
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- README: 「スケジュールから営業日カレンダーを作る」の節を足した。型定義の確認の範囲を、CI で確かめる TypeScript の版(5.0・5.5・5.8・6.0・7.0)に改めた。サイズの値を測り直した(ルート約 9.0KB・日本の祝日込み約 10.8KB・英国の祝日込み約 10.7KB。0.13.0 で測定)
|
|
42
|
+
- `docs/decisions.md`: 38 番(スケジュールからのカレンダー・`"open-annual"`・利用者の TypeScript の版の確認)を追加した
|
|
43
|
+
|
|
44
|
+
### 開発
|
|
45
|
+
|
|
46
|
+
- CI に `consumer-types` ジョブを追加した。公開する型定義を、利用者が使う TypeScript 5.0.4・5.5.4・5.8.3・7.0.2(ネイティブ版)のそれぞれで、`moduleResolution` の `node16`・`nodenext`・`bundler` で型チェックする。各版は依存にせず、版と SHA-256 を固定した tgz から一時ディレクトリに入れる(7.0 はランナー向けの実行形式 `@typescript/typescript-linux-x64` も固定)。`pnpm check` は従来どおり開発用の 6.0.3 で確かめる
|
|
47
|
+
- `scripts/check-consumer-types.ts` に `--tsc <path>` を足し、わざと型エラーを含む `test/consumer/consumer-error.mts` が各版で失敗として報告されること(検査が機能していること)も確かめるようにした。リポジトリの `@types` を読まないよう `--typeRoots` を空にした(古い TypeScript では `@types/node` の新しい構文を読めないため)。`--ignoreConfig` は 6.0 以降でだけ渡す
|
|
48
|
+
- `createCalendar` の `dayInfo` の変換を `createCalendarFromSchedule` と共用の内部関数に切り出した。結果は変わらない
|
|
49
|
+
|
|
7
50
|
## [0.12.0] - 2026-10-20
|
|
8
51
|
|
|
9
52
|
### 移行
|
|
@@ -330,7 +373,9 @@
|
|
|
330
373
|
|
|
331
374
|
- 日本: 2026 年 2 月の官報(2027 年の春分・秋分の日)まで反映(`dataVersion: "2026-02"`、`confirmedThrough: 2027`)
|
|
332
375
|
|
|
333
|
-
[Unreleased]: https://github.com/inohan77/bizdays-toolkit/compare/v0.
|
|
376
|
+
[Unreleased]: https://github.com/inohan77/bizdays-toolkit/compare/v0.14.0...HEAD
|
|
377
|
+
[0.14.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.13.0...v0.14.0
|
|
378
|
+
[0.13.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.12.0...v0.13.0
|
|
334
379
|
[0.12.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.11.1...v0.12.0
|
|
335
380
|
[0.11.1]: https://github.com/inohan77/bizdays-toolkit/compare/v0.11.0...v0.11.1
|
|
336
381
|
[0.11.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.10.0...v0.11.0
|
package/README.md
CHANGED
|
@@ -25,9 +25,10 @@ A business-day calendar that combines public holidays with company or store clos
|
|
|
25
25
|
- **Option diagnostics**: `diagnoseCalendar` and `diagnoseSchedule` from `bizdays-toolkit/diagnose` list rules that can never apply, rules whose removal changes nothing, typos in option keys, calendars with no business day and more, without throwing. One line in your tests keeps a change of settings from silently killing a rule
|
|
26
26
|
- **Month-based due dates**: due dates such as "closing on the 20th, paid on the 10th of the next month (next business day if closed)" or "the last business day of the month", with the four adjustment conventions used in finance (following, preceding, modified-following, modified-preceding)
|
|
27
27
|
- **Business-day arithmetic**: closed weekdays, annual closures (including ranges across the new year), specific closed and open dates, and holidays combine to compute "N business days later", "next business day after the cut-off time" and more
|
|
28
|
+
- **A calendar from a schedule**: `createCalendarFromSchedule` turns a schedule such as store hours into a business-day calendar, so the hours are the only configuration and business-day arithmetic works on them
|
|
28
29
|
- **Combining calendars**: `combineCalendars` builds a calendar of the days on which all of several calendars are open (your company and the bank, for a remittance date) or any of them is open (any of several stores), and business-day arithmetic works on the result
|
|
29
30
|
- **Time-zone safe**: instants such as `Date` are converted to a civil date in the calendar's time zone (`Asia/Tokyo` for Japan, `Europe/London` for the UK) before evaluation. A server running in UTC does not treat 00:00–09:00 in Japan as the previous day
|
|
30
|
-
- **Lightweight, zero dependencies**: about
|
|
31
|
+
- **Lightweight, zero dependencies**: about 9.3 KB min+gzip (about 11.1 KB with Japanese holidays, about 11.1 KB with UK bank holidays; measured for 0.14.0). ESM, bundled types and `sideEffects: false`. Written in standard JavaScript and `Intl` only (no Node.js APIs); CI tests it on Node.js, Chrome, Firefox, Edge, Safari and Cloudflare Workers
|
|
31
32
|
- **Extensible to other countries and regions**: holiday providers are pluggable through the `HolidayProvider` interface. Japan and the UK are bundled
|
|
32
33
|
|
|
33
34
|
## Installation
|
|
@@ -38,7 +39,7 @@ pnpm add bizdays-toolkit
|
|
|
38
39
|
|
|
39
40
|
Node.js 22.12 or later. CI tests the package on Node.js 22.12, 22, 24 and 26, on Chrome, Firefox and Edge (Linux) and Safari (macOS), and on Cloudflare Workers (workerd, the open-source Workers runtime). The browsers are the latest stable versions on GitHub's runners, so their versions change as the runners are updated. The package is ESM only, but it can also be loaded from CommonJS with `require()` (tested in CI as well; on Node.js 22.12, `require()` prints an experimental-feature warning, which does not affect the result, and Node.js 26 prints none). Other runtimes (such as Deno, Bun or Vercel's Edge Runtime) are not tested.
|
|
40
41
|
|
|
41
|
-
Types are bundled. CI type-checks a consumer project against the published type definitions with `moduleResolution` set to `node16`, `nodenext` and `bundler
|
|
42
|
+
Types are bundled. CI type-checks a consumer project against the published type definitions with TypeScript 5.0, 5.5, 5.8, 6.0 and 7.0 (the native compiler), each with `moduleResolution` set to `node16`, `nodenext` and `bundler`, and confirms that an intentional type error is reported by each.
|
|
42
43
|
|
|
43
44
|
## Usage
|
|
44
45
|
|
|
@@ -396,6 +397,15 @@ calendar.dueDate(monthOf(billing, 1), { day: -1, convention: "preceding" }); //
|
|
|
396
397
|
|
|
397
398
|
- `monthOf(dateOrMonth, offset?)`: the month `offset` months after (before, if negative) a date or a month `"YYYY-MM"`
|
|
398
399
|
- `closingMonth(date, closingDay)`: the month a date belongs to when months are closed on `closingDay` (1–31, `-1` for the last day). The day after a closing day up to the next closing day belongs to the month of the next closing day. If the closing day does not exist in a month (closing on the 31st in February), the last day is used
|
|
400
|
+
- `closingPeriod(monthOrDate, closingDay)`: the inverse: the period `{ from, to }` (both ends included) that belongs to the month, from the day after the previous closing day to the closing day. Given a date, the period that date belongs to. The result has the shape of a date range, so it can go straight into `closedDates` or `listBusinessDays`
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
closingPeriod("2026-11", 20); // { from: "2026-10-21", to: "2026-11-20" }
|
|
404
|
+
closingPeriod("2026-03", 31); // { from: "2026-03-01", to: "2026-03-31" } (closing on the 31st in February means the 28th)
|
|
405
|
+
const { from, to } = closingPeriod("2026-11-05", 20); // the period "2026-11-05" belongs to
|
|
406
|
+
const office = createCalendar({ holidays: jp, closedWeekdays: [0, 6] });
|
|
407
|
+
office.listBusinessDays(from, to).length; // 22 (business days in the November period)
|
|
408
|
+
```
|
|
399
409
|
|
|
400
410
|
The `day` of `dueDate` also maps days that do not exist in a month (such as February 30) to the last day. Recurring rules (`{ day: 31 }`) do not match such days, so the two behave differently: a due date must always fall somewhere in its month.
|
|
401
411
|
|
|
@@ -450,6 +460,33 @@ const isOpen = hours !== null && hours.open <= time && time < hours.close;
|
|
|
450
460
|
|
|
451
461
|
For inputs that always carry a time (a `Date`, Temporal `Instant`, `ZonedDateTime` or `PlainDateTime`, or a `"YYYY-MM-DDTHH:mm"` string), `time` is typed as `string`, so no `null` check is needed. `civilOf` is also on business-day calendars, and the same conversion is available as `toDateTimeParts(input, timeZone)` in the root. (`toJapanDate` and `toJapanDateTime` in `bizdays-toolkit/jp` are deprecated since 0.10.0: use `toDateString(input, "Asia/Tokyo")`, `toDateTimeParts(input, "Asia/Tokyo")` or `dateOf`/`civilOf` on a calendar instead. They will be removed in 1.0.0.)
|
|
452
462
|
|
|
463
|
+
### A business-day calendar from a schedule
|
|
464
|
+
|
|
465
|
+
When the schedule (store hours, say) is the real configuration, `createCalendarFromSchedule(schedule, isOpen)` makes a business-day calendar from it, so you do not write a second set of rules to keep in sync. `isOpen` decides from the value alone whether the day is a business day; the result is an ordinary calendar with business-day arithmetic, `adjust`, `dueDate` and the rest.
|
|
466
|
+
|
|
467
|
+
```ts
|
|
468
|
+
import { createCalendarFromSchedule } from "bizdays-toolkit";
|
|
469
|
+
|
|
470
|
+
const hours = createSchedule<Hours>({
|
|
471
|
+
holidays: jp,
|
|
472
|
+
weekly: [null, weekday, weekday, weekday, weekday, weekday, { open: "10:00", close: "17:00" }],
|
|
473
|
+
holiday: { sameAs: 0 },
|
|
474
|
+
annual: [
|
|
475
|
+
{ from: "12-31", to: "01-03", value: null, name: "New Year closure" },
|
|
476
|
+
{ date: "01-04", value: { open: "11:00", close: "15:00" }, name: "First day of the year" },
|
|
477
|
+
],
|
|
478
|
+
});
|
|
479
|
+
const calendar = createCalendarFromSchedule(hours, (v) => v !== null);
|
|
480
|
+
calendar.addBusinessDays("2026-12-29", 2); // "2027-01-04"
|
|
481
|
+
calendar.dayInfo("2027-01-04");
|
|
482
|
+
// { date: "2027-01-04", weekday: 1, businessDay: true, reason: "open-annual", name: "First day of the year", rulePath: "annual[1]" }
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
- `isOpen` must be a pure function of the value: it receives only the value (not the date), and the calendar calls it repeatedly while searching for business days, so a function that looks at the date, the time or outside state would make the result change from call to call. Conditions that depend on the date belong in the schedule's rules (`dates`, `recurring`)
|
|
486
|
+
- `dayInfo` maps the schedule's `reason` to the calendar's: `date` → `"open-date"`/`"closed-date"`, `annual` → `"open-annual"`/`"closed-annual"`, `recurring` → `"open-recurring"`/`"closed-recurring"`, `holiday` → `"holiday"` (whether or not the holiday is open, as with `holidayPolicy: "open"`), `weekday` → `"regular"`/`"closed-weekday"`. `"open-annual"` exists only for calendars made this way, since `closedAnnual` of `createCalendar` only closes. `name`, `holiday`, `shiftedFrom` and `rulePath` are the schedule's
|
|
487
|
+
- Holidays, the time zone and `outOfRange` are the schedule's. Diagnose the schedule's options with `diagnoseSchedule`; the calendar itself is not diagnosed. `maxSearchDays` (default 3660) can be given in the options
|
|
488
|
+
- Use `createCalendar` when you only need open or closed; use this when you have a schedule anyway
|
|
489
|
+
|
|
453
490
|
### Date and date-time inputs
|
|
454
491
|
|
|
455
492
|
Every method accepts the following inputs. Returned dates are always `"YYYY-MM-DD"` strings.
|
|
@@ -467,6 +504,26 @@ Temporal objects are recognized by their shape, so the package does not depend o
|
|
|
467
504
|
|
|
468
505
|
The calendar's time zone is the `timeZone` option, then the holiday provider's time zone, then `"UTC"`.
|
|
469
506
|
|
|
507
|
+
**From a civil date and time to an instant**: `toInstant(date, time, timeZone)` and `calendar.instantOf(date, time)` (also on schedules) go the other way from `civilOf`: a civil date `"YYYY-MM-DD"` and a time `"HH:mm"` or `"HH:mm:ss"` become a `Date` for that instant in the time zone. Only a date string is accepted (a `Date` would be ambiguous about which day it means), and the result is a `Date`, which is one line from an ISO string (`.toISOString()`) or a Temporal `Instant` (`Temporal.Instant.fromEpochMilliseconds(d.getTime())`).
|
|
508
|
+
|
|
509
|
+
```ts
|
|
510
|
+
import { toInstant } from "bizdays-toolkit";
|
|
511
|
+
|
|
512
|
+
toInstant("2026-11-23", "10:00", "Asia/Tokyo").toISOString(); // "2026-11-23T01:00:00.000Z"
|
|
513
|
+
const store = createCalendar({ holidays: jp, closedWeekdays: [0] });
|
|
514
|
+
const opensAt = store.instantOf(store.nextBusinessDay(store.today()), "10:00"); // the next opening, as a Date
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Around daylight-saving changes the result follows Temporal's default (`disambiguation: "compatible"`): a time that does not exist (in the UK, 01:00–01:59 on the last Sunday of March) is moved forward by the length of the gap, and a time that occurs twice (01:00–01:59 on the last Sunday of October) is the earlier one.
|
|
518
|
+
|
|
519
|
+
```ts
|
|
520
|
+
import { england } from "bizdays-toolkit/uk";
|
|
521
|
+
|
|
522
|
+
const uk = createCalendar({ holidays: england });
|
|
523
|
+
uk.instantOf("2026-03-29", "01:30").toISOString(); // "2026-03-29T01:30:00.000Z" (02:30 BST; 01:30 does not exist)
|
|
524
|
+
uk.instantOf("2026-10-25", "01:30").toISOString(); // "2026-10-25T00:30:00.000Z" (the first 01:30, still BST)
|
|
525
|
+
```
|
|
526
|
+
|
|
470
527
|
### Holidays of other countries and regions
|
|
471
528
|
|
|
472
529
|
Implement `HolidayProvider` and pass it as `holidays`. Several providers give their union (if their time zones differ, `timeZone` is required).
|
package/dist/core/calendar.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ import { type DateString, type Weekday } from "./date.ts";
|
|
|
22
22
|
import { IssueCollector } from "./errors.ts";
|
|
23
23
|
import { type DateInput } from "./input.ts";
|
|
24
24
|
import { type MonthString } from "./month.ts";
|
|
25
|
-
import { type CompiledSchedule, type DayOfMonth, type EffectivePeriod, type RecurringRule, type HolidayLookup, type HolidaySourceOptions } from "./schedule.ts";
|
|
25
|
+
import { type CompiledSchedule, type DayOfMonth, type EffectivePeriod, type RecurringRule, type HolidayLookup, type HolidaySourceOptions, type Schedule } from "./schedule.ts";
|
|
26
26
|
import type { Holiday } from "./types.ts";
|
|
27
27
|
/**
|
|
28
28
|
* 祝日の扱い。
|
|
@@ -93,7 +93,7 @@ export interface CalendarOptions<Kind extends string = string> extends HolidaySo
|
|
|
93
93
|
* 営業・休業を決めた規則(既知の値)。
|
|
94
94
|
* @public
|
|
95
95
|
*/
|
|
96
|
-
export type KnownDayReason = "open-date" | "closed-date" | "closed-annual" | "open-recurring" | "closed-recurring" | "holiday" | "closed-weekday" | "regular";
|
|
96
|
+
export type KnownDayReason = "open-date" | "closed-date" | "open-annual" | "closed-annual" | "open-recurring" | "closed-recurring" | "holiday" | "closed-weekday" | "regular";
|
|
97
97
|
/**
|
|
98
98
|
* 営業・休業を決めた規則。将来の版で値が増えることがあるので、分岐では「その他」も扱うこと。
|
|
99
99
|
* すべての値を網羅したことを型で確かめたい場合は KnownDayReason を使う。
|
|
@@ -255,4 +255,21 @@ export declare function createCalendar<Kind extends string = string>(options?: C
|
|
|
255
255
|
* @public
|
|
256
256
|
*/
|
|
257
257
|
export declare function combineCalendars<Kind extends string = string>(calendars: readonly Calendar<Kind>[], options: CombineOptions): Calendar<Kind>;
|
|
258
|
+
/**
|
|
259
|
+
* スケジュール(createSchedule)から営業日カレンダーを作る。isOpen が true を返す値の日を営業日とする。
|
|
260
|
+
* 営業時間表のような日ごとの値が設定の正本で、営業日の計算(加減算・調整・期日など)も同じ設定から行いたいときに使う
|
|
261
|
+
* (営業日カレンダーを別に書いて同期を取る必要がなくなる)。
|
|
262
|
+
*
|
|
263
|
+
* - isOpen は値だけで決まる純粋な関数であること(日付・時刻・外部の状態を見ない)。営業日の探索は isOpen を繰り返し呼ぶので、
|
|
264
|
+
* 結果が変わる関数を渡すと探索の結果が呼ぶたびに変わる。日付で変えたい条件は、スケジュールの規則(dates・recurring)に書く
|
|
265
|
+
* - dayInfo の reason は、スケジュールの reason と営業・休業の組み合わせ(date → "open-date"/"closed-date"、annual → "open-annual"/
|
|
266
|
+
* "closed-annual"、recurring → "open-recurring"/"closed-recurring"、holiday → "holiday"、weekday → "regular"/"closed-weekday")。
|
|
267
|
+
* name・holiday・shiftedFrom・rulePath はスケジュールの結果をそのまま引き継ぐ
|
|
268
|
+
* - 祝日の判定(holiday・isHoliday など)・タイムゾーン・outOfRange はスケジュールのもの。規則の診断(diagnoseSchedule)は
|
|
269
|
+
* スケジュールの設定に対して行う
|
|
270
|
+
* @public
|
|
271
|
+
*/
|
|
272
|
+
export declare function createCalendarFromSchedule<T, Kind extends string = string>(schedule: Schedule<T, Kind>, isOpen: (value: T) => boolean, options?: {
|
|
273
|
+
readonly maxSearchDays?: number;
|
|
274
|
+
}): Calendar<Kind>;
|
|
258
275
|
export {};
|
package/dist/core/calendar.js
CHANGED
|
@@ -22,7 +22,7 @@ import { addDays, daysInMonth, formatDate, fromDayNumber, toDayNumber, } from ".
|
|
|
22
22
|
import { checkUnknownKeys, IssueCollector, plainError, rangeError } from "./errors.js";
|
|
23
23
|
import { parseTimeOfDay, toCivil } from "./input.js";
|
|
24
24
|
import { dayOfMonth, isDayOfMonthValue, parseMonth } from "./month.js";
|
|
25
|
-
import { buildSchedule, compileAnnualRules, compileDateRules, compileHolidaySource, compileRecurringRules, createHolidayLookup, isWeekday, mapDays, RECURRING_RULE_KEYS, } from "./schedule.js";
|
|
25
|
+
import { buildSchedule, compileAnnualRules, compileDateRules, compileHolidaySource, compileRecurringRules, createHolidayLookup, infoOfSchedule, isWeekday, mapDays, RECURRING_RULE_KEYS, } from "./schedule.js";
|
|
26
26
|
const CALENDAR_OPTION_KEYS = [
|
|
27
27
|
"holidays",
|
|
28
28
|
"timeZone",
|
|
@@ -100,54 +100,62 @@ export function createCalendar(options = {}) {
|
|
|
100
100
|
c.throwFirstError();
|
|
101
101
|
const { maxSearchDays } = compiled;
|
|
102
102
|
const schedule = buildSchedule(compiled.schedule);
|
|
103
|
-
|
|
103
|
+
const info = (date) => {
|
|
104
104
|
const day = schedule.dayInfo(date);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
105
|
+
return dayInfoFromScheduleDay(day, day.value);
|
|
106
|
+
};
|
|
107
|
+
return buildCalendar(schedule, (date) => schedule.valueOn(date), info, maxSearchDays);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* @internal スケジュールの 1 日分の結果(値は営業日か)を、営業日カレンダーの結果に変換する。
|
|
111
|
+
* reason はスケジュールの reason と営業・休業の組み合わせで決まる。createCalendar の closedAnnual は休業だけなので
|
|
112
|
+
* "open-annual" にはならないが、createCalendarFromSchedule では annual の値で営業になる日がある
|
|
113
|
+
*/
|
|
114
|
+
function dayInfoFromScheduleDay(day, open) {
|
|
115
|
+
let reason;
|
|
116
|
+
// 内部では閉じた型(KnownScheduleReason)で分岐し、値が増えたときの対応漏れを型で検出する
|
|
117
|
+
const known = day.reason;
|
|
118
|
+
switch (known) {
|
|
119
|
+
case "date":
|
|
120
|
+
reason = open ? "open-date" : "closed-date";
|
|
121
|
+
break;
|
|
122
|
+
case "annual":
|
|
123
|
+
reason = open ? "open-annual" : "closed-annual";
|
|
124
|
+
break;
|
|
125
|
+
case "recurring":
|
|
126
|
+
reason = open ? "open-recurring" : "closed-recurring";
|
|
127
|
+
break;
|
|
128
|
+
case "holiday":
|
|
129
|
+
reason = "holiday";
|
|
130
|
+
break;
|
|
131
|
+
case "weekday":
|
|
132
|
+
reason = open ? "regular" : "closed-weekday";
|
|
133
|
+
break;
|
|
134
|
+
default: {
|
|
135
|
+
const unreachable = known;
|
|
136
|
+
throw plainError("invalid-option", `Unexpected schedule reason: ${String(unreachable)}`);
|
|
128
137
|
}
|
|
129
|
-
const result = {
|
|
130
|
-
date: day.date,
|
|
131
|
-
weekday: day.weekday,
|
|
132
|
-
businessDay: day.value,
|
|
133
|
-
reason,
|
|
134
|
-
};
|
|
135
|
-
// 曜日で決まった日には名前を付けない(祝日名は holiday で分かる)
|
|
136
|
-
if (day.name !== undefined && day.reason !== "weekday")
|
|
137
|
-
result.name = day.name;
|
|
138
|
-
if (day.holiday !== undefined)
|
|
139
|
-
result.holiday = day.holiday;
|
|
140
|
-
if (day.shiftedFrom !== undefined)
|
|
141
|
-
result.shiftedFrom = day.shiftedFrom;
|
|
142
|
-
if (day.rulePath !== undefined)
|
|
143
|
-
result.rulePath = day.rulePath;
|
|
144
|
-
return Object.freeze(result);
|
|
145
138
|
}
|
|
146
|
-
|
|
139
|
+
const result = {
|
|
140
|
+
date: day.date,
|
|
141
|
+
weekday: day.weekday,
|
|
142
|
+
businessDay: open,
|
|
143
|
+
reason,
|
|
144
|
+
};
|
|
145
|
+
// 曜日で決まった日には名前を付けない(祝日名は holiday で分かる)
|
|
146
|
+
if (day.name !== undefined && day.reason !== "weekday")
|
|
147
|
+
result.name = day.name;
|
|
148
|
+
if (day.holiday !== undefined)
|
|
149
|
+
result.holiday = day.holiday;
|
|
150
|
+
if (day.shiftedFrom !== undefined)
|
|
151
|
+
result.shiftedFrom = day.shiftedFrom;
|
|
152
|
+
if (day.rulePath !== undefined)
|
|
153
|
+
result.rulePath = day.rulePath;
|
|
154
|
+
return Object.freeze(result);
|
|
147
155
|
}
|
|
148
156
|
/**
|
|
149
157
|
* @internal 祝日を調べる機能と 1 日分の判定から、営業日カレンダーの機能(探索・調整・月の営業日・期日)を組み立てる。
|
|
150
|
-
* createCalendar
|
|
158
|
+
* createCalendar・combineCalendars・createCalendarFromSchedule で共用する
|
|
151
159
|
*/
|
|
152
160
|
function buildCalendar(lookup, isOpen, info, maxSearchDays) {
|
|
153
161
|
const dateOf = lookup.dateOf;
|
|
@@ -241,6 +249,7 @@ function buildCalendar(lookup, isOpen, info, maxSearchDays) {
|
|
|
241
249
|
outOfRange: lookup.outOfRange,
|
|
242
250
|
dateOf,
|
|
243
251
|
civilOf: lookup.civilOf,
|
|
252
|
+
instantOf: lookup.instantOf,
|
|
244
253
|
today: lookup.today,
|
|
245
254
|
isHoliday: lookup.isHoliday,
|
|
246
255
|
getHoliday: lookup.getHoliday,
|
|
@@ -386,3 +395,36 @@ export function combineCalendars(calendars, options) {
|
|
|
386
395
|
}
|
|
387
396
|
return buildCalendar(lookup, isOpen, info, maxSearchDays);
|
|
388
397
|
}
|
|
398
|
+
/**
|
|
399
|
+
* スケジュール(createSchedule)から営業日カレンダーを作る。isOpen が true を返す値の日を営業日とする。
|
|
400
|
+
* 営業時間表のような日ごとの値が設定の正本で、営業日の計算(加減算・調整・期日など)も同じ設定から行いたいときに使う
|
|
401
|
+
* (営業日カレンダーを別に書いて同期を取る必要がなくなる)。
|
|
402
|
+
*
|
|
403
|
+
* - isOpen は値だけで決まる純粋な関数であること(日付・時刻・外部の状態を見ない)。営業日の探索は isOpen を繰り返し呼ぶので、
|
|
404
|
+
* 結果が変わる関数を渡すと探索の結果が呼ぶたびに変わる。日付で変えたい条件は、スケジュールの規則(dates・recurring)に書く
|
|
405
|
+
* - dayInfo の reason は、スケジュールの reason と営業・休業の組み合わせ(date → "open-date"/"closed-date"、annual → "open-annual"/
|
|
406
|
+
* "closed-annual"、recurring → "open-recurring"/"closed-recurring"、holiday → "holiday"、weekday → "regular"/"closed-weekday")。
|
|
407
|
+
* name・holiday・shiftedFrom・rulePath はスケジュールの結果をそのまま引き継ぐ
|
|
408
|
+
* - 祝日の判定(holiday・isHoliday など)・タイムゾーン・outOfRange はスケジュールのもの。規則の診断(diagnoseSchedule)は
|
|
409
|
+
* スケジュールの設定に対して行う
|
|
410
|
+
* @public
|
|
411
|
+
*/
|
|
412
|
+
export function createCalendarFromSchedule(schedule, isOpen, options) {
|
|
413
|
+
const info = infoOfSchedule(schedule);
|
|
414
|
+
if (info === undefined) {
|
|
415
|
+
throw rangeError("invalid-argument", "schedule must be a schedule created by createSchedule");
|
|
416
|
+
}
|
|
417
|
+
if (typeof isOpen !== "function") {
|
|
418
|
+
throw rangeError("invalid-argument", "isOpen must be a function (value) => boolean");
|
|
419
|
+
}
|
|
420
|
+
const maxSearchDays = options?.maxSearchDays ?? 3660;
|
|
421
|
+
if (!Number.isInteger(maxSearchDays) || maxSearchDays < 1) {
|
|
422
|
+
throw rangeError("invalid-option", "maxSearchDays must be a positive integer");
|
|
423
|
+
}
|
|
424
|
+
const open = (date) => isOpen(info(date).value) === true;
|
|
425
|
+
return buildCalendar(schedule, open, (date) => {
|
|
426
|
+
const day = info(date);
|
|
427
|
+
// 変換は value を読まない(営業・休業は open で渡す)ので、boolean の値として扱ってよい
|
|
428
|
+
return dayInfoFromScheduleDay(day, isOpen(day.value) === true);
|
|
429
|
+
}, maxSearchDays);
|
|
430
|
+
}
|
package/dist/core/input.d.ts
CHANGED
|
@@ -82,5 +82,21 @@ export declare function toDateString(input: DateInput, timeZone: string): DateSt
|
|
|
82
82
|
export declare function toDateTimeParts(input: DateTimeInput, timeZone: string): CivilDateTimeParts<string>;
|
|
83
83
|
/** @public */
|
|
84
84
|
export declare function toDateTimeParts(input: DateInput, timeZone: string): CivilDateTimeParts;
|
|
85
|
+
/**
|
|
86
|
+
* @internal 暦日と時刻(0 時からの経過分)から、タイムゾーンでの瞬間(エポックミリ秒)を求める。
|
|
87
|
+
* 夏時間の扱いは Temporal の disambiguation: "compatible" と同じ。存在しない時刻(飛び)は切り替え前のオフセットで解釈して
|
|
88
|
+
* 飛んだ分だけ後ろの瞬間に、2 回ある時刻(重複)は先のほう(切り替え前のオフセット)にする。
|
|
89
|
+
* Intl 以外に依存しないよう、前後 1 日のオフセットを候補にして、暦日に戻して一致するものを選ぶ
|
|
90
|
+
*/
|
|
91
|
+
export declare function civilToInstant(date: DateString, minutes: number, timeZone: string): number;
|
|
92
|
+
/** @internal "HH:mm" または "HH:mm:ss" を 0 時からの経過秒に変換する(00:00:00〜23:59:59) */
|
|
93
|
+
export declare function parseTimeOfDaySeconds(value: unknown): number;
|
|
94
|
+
/**
|
|
95
|
+
* 暦日と時刻("HH:mm" または "HH:mm:ss")から、タイムゾーンでのその瞬間を Date で返す(civilOf・toDateTimeParts の逆向き)。
|
|
96
|
+
* 夏時間の切り替えで存在しない時刻は、飛んだ分だけ後ろの瞬間(英国の 3 月の 01:30 は 02:30 BST)、2 回ある時刻は先のほう
|
|
97
|
+
* (Temporal の disambiguation: "compatible" と同じ)。
|
|
98
|
+
* @public
|
|
99
|
+
*/
|
|
100
|
+
export declare function toInstant(date: DateString, time: string, timeZone: string): Date;
|
|
85
101
|
/** @internal "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
|
|
86
102
|
export declare function parseTimeOfDay(value: string): number;
|
package/dist/core/input.js
CHANGED
|
@@ -143,6 +143,56 @@ export function toDateTimeParts(input, timeZone) {
|
|
|
143
143
|
: `${String(Math.floor(civil.minutes / 60)).padStart(2, "0")}:${String(civil.minutes % 60).padStart(2, "0")}`;
|
|
144
144
|
return Object.freeze({ date: civil.date, time });
|
|
145
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* @internal 暦日と時刻(0 時からの経過分)から、タイムゾーンでの瞬間(エポックミリ秒)を求める。
|
|
148
|
+
* 夏時間の扱いは Temporal の disambiguation: "compatible" と同じ。存在しない時刻(飛び)は切り替え前のオフセットで解釈して
|
|
149
|
+
* 飛んだ分だけ後ろの瞬間に、2 回ある時刻(重複)は先のほう(切り替え前のオフセット)にする。
|
|
150
|
+
* Intl 以外に依存しないよう、前後 1 日のオフセットを候補にして、暦日に戻して一致するものを選ぶ
|
|
151
|
+
*/
|
|
152
|
+
export function civilToInstant(date, minutes, timeZone) {
|
|
153
|
+
const { year, month, day } = parseDate(date);
|
|
154
|
+
const wall = Date.UTC(year, month - 1, day, Math.floor(minutes / 60), minutes % 60);
|
|
155
|
+
const offsetAt = (ms) => {
|
|
156
|
+
const civil = instantToCivil(ms, timeZone);
|
|
157
|
+
const c = parseDate(civil.date);
|
|
158
|
+
const m = civil.minutes ?? 0; // 瞬間からの変換では時刻が必ずある
|
|
159
|
+
return Date.UTC(c.year, c.month - 1, c.day, Math.floor(m / 60), m % 60) - ms;
|
|
160
|
+
};
|
|
161
|
+
const before = offsetAt(wall - 86_400_000);
|
|
162
|
+
const after = offsetAt(wall + 86_400_000);
|
|
163
|
+
const valid = (ms) => {
|
|
164
|
+
const civil = instantToCivil(ms, timeZone);
|
|
165
|
+
return civil.date === date && civil.minutes === minutes;
|
|
166
|
+
};
|
|
167
|
+
const candidates = before === after ? [wall - before] : [wall - before, wall - after];
|
|
168
|
+
const found = candidates.filter(valid);
|
|
169
|
+
// 重複なら先のほう(小さいミリ秒)、飛びなら切り替え前のオフセット(before)で解釈した瞬間
|
|
170
|
+
return found.length > 0 ? Math.min(...found) : wall - before;
|
|
171
|
+
}
|
|
172
|
+
/** @internal "HH:mm" または "HH:mm:ss" を 0 時からの経過秒に変換する(00:00:00〜23:59:59) */
|
|
173
|
+
export function parseTimeOfDaySeconds(value) {
|
|
174
|
+
const m = typeof value === "string" ? /^(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(value) : null;
|
|
175
|
+
const seconds = m ? Number(m[3] ?? "0") : NaN;
|
|
176
|
+
if (!m || seconds > 59) {
|
|
177
|
+
throw rangeError("invalid-time", `Invalid time of day: ${JSON.stringify(value)} (expected HH:mm or HH:mm:ss)`);
|
|
178
|
+
}
|
|
179
|
+
return wallTime(Number(m[1]), Number(m[2])) * 60 + seconds;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 暦日と時刻("HH:mm" または "HH:mm:ss")から、タイムゾーンでのその瞬間を Date で返す(civilOf・toDateTimeParts の逆向き)。
|
|
183
|
+
* 夏時間の切り替えで存在しない時刻は、飛んだ分だけ後ろの瞬間(英国の 3 月の 01:30 は 02:30 BST)、2 回ある時刻は先のほう
|
|
184
|
+
* (Temporal の disambiguation: "compatible" と同じ)。
|
|
185
|
+
* @public
|
|
186
|
+
*/
|
|
187
|
+
export function toInstant(date, time, timeZone) {
|
|
188
|
+
const zone = validateTimeZone(timeZone);
|
|
189
|
+
if (typeof date !== "string")
|
|
190
|
+
throw typeError("invalid-date", `Invalid date: ${String(date)}`);
|
|
191
|
+
parseDate(date);
|
|
192
|
+
const seconds = parseTimeOfDaySeconds(time);
|
|
193
|
+
const ms = civilToInstant(date, Math.floor(seconds / 60), zone) + (seconds % 60) * 1000;
|
|
194
|
+
return new Date(ms);
|
|
195
|
+
}
|
|
146
196
|
/** @internal "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
|
|
147
197
|
export function parseTimeOfDay(value) {
|
|
148
198
|
const m = /^(\d{2}):(\d{2})$/.exec(value);
|
package/dist/core/month.d.ts
CHANGED
|
@@ -48,3 +48,20 @@ export declare function monthOf(input: DateString | MonthString, offset?: number
|
|
|
48
48
|
* @public
|
|
49
49
|
*/
|
|
50
50
|
export declare function closingMonth(date: DateString, closingDay: number): MonthString;
|
|
51
|
+
/**
|
|
52
|
+
* 締め日で区切ったときの、その月分の期間(両端を含む暦日)。closingMonth の逆算。
|
|
53
|
+
*
|
|
54
|
+
* 前月の締め日の翌日から、その月の締め日まで。月の代わりに暦日を渡すと、その日が属する期間(closingMonth で求めた月の期間)。
|
|
55
|
+
* 返す値は変更できず、name は付かない(DateRange の形なので、そのまま closedDates などに渡せる)。
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* closingPeriod("2026-11", 20); // { from: "2026-10-21", to: "2026-11-20" }
|
|
59
|
+
* closingPeriod("2026-10", -1); // { from: "2026-10-01", to: "2026-10-31" }(末日締め)
|
|
60
|
+
* closingPeriod("2026-03", 31); // { from: "2026-03-01", to: "2026-03-31" }(2 月の 31 日締めは 2 月末日)
|
|
61
|
+
* closingPeriod("2026-11-05", 20); // { from: "2026-10-21", to: "2026-11-20" }(この日が属する期間)
|
|
62
|
+
* @public
|
|
63
|
+
*/
|
|
64
|
+
export declare function closingPeriod(input: DateString | MonthString, closingDay: number): {
|
|
65
|
+
readonly from: DateString;
|
|
66
|
+
readonly to: DateString;
|
|
67
|
+
};
|
package/dist/core/month.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 営業日を考えない暦の計算なので、カレンダーなしで使える。営業日を考えた月の計算
|
|
5
5
|
* (月の第 n 営業日・期日)は営業日カレンダーのメソッドにある。
|
|
6
6
|
*/
|
|
7
|
-
import { daysInMonth, formatDate, parseDate } from "./date.js";
|
|
7
|
+
import { addDays, daysInMonth, formatDate, parseDate } from "./date.js";
|
|
8
8
|
import { rangeError } from "./errors.js";
|
|
9
9
|
const MONTH_RE = /^(\d{4})-(\d{2})$/;
|
|
10
10
|
/** @internal "YYYY-MM" を年と月に分解する(誤りは code: "invalid-month") */
|
|
@@ -67,10 +67,40 @@ export function monthOf(input, offset = 0) {
|
|
|
67
67
|
* @public
|
|
68
68
|
*/
|
|
69
69
|
export function closingMonth(date, closingDay) {
|
|
70
|
+
checkClosingDay(closingDay);
|
|
71
|
+
const { year, month, day } = parseDate(date);
|
|
72
|
+
return formatMonth(shiftMonth({ year, month }, day <= closingDayIn({ year, month }, closingDay) ? 0 : 1));
|
|
73
|
+
}
|
|
74
|
+
function checkClosingDay(closingDay) {
|
|
70
75
|
if (!isDayOfMonthValue(closingDay)) {
|
|
71
76
|
throw rangeError("invalid-argument", `closingDay must be an integer from 1 to 31, or -1 (last day): ${closingDay}`);
|
|
72
77
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
}
|
|
79
|
+
/** その月の締め日(日)。-1 またはその月の日数以上なら末日(decisions.md の 16 番の読み替えはここだけで行う) */
|
|
80
|
+
function closingDayIn({ year, month }, closingDay) {
|
|
81
|
+
const last = daysInMonth(year, month);
|
|
82
|
+
return closingDay === -1 || closingDay > last ? last : closingDay;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 締め日で区切ったときの、その月分の期間(両端を含む暦日)。closingMonth の逆算。
|
|
86
|
+
*
|
|
87
|
+
* 前月の締め日の翌日から、その月の締め日まで。月の代わりに暦日を渡すと、その日が属する期間(closingMonth で求めた月の期間)。
|
|
88
|
+
* 返す値は変更できず、name は付かない(DateRange の形なので、そのまま closedDates などに渡せる)。
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* closingPeriod("2026-11", 20); // { from: "2026-10-21", to: "2026-11-20" }
|
|
92
|
+
* closingPeriod("2026-10", -1); // { from: "2026-10-01", to: "2026-10-31" }(末日締め)
|
|
93
|
+
* closingPeriod("2026-03", 31); // { from: "2026-03-01", to: "2026-03-31" }(2 月の 31 日締めは 2 月末日)
|
|
94
|
+
* closingPeriod("2026-11-05", 20); // { from: "2026-10-21", to: "2026-11-20" }(この日が属する期間)
|
|
95
|
+
* @public
|
|
96
|
+
*/
|
|
97
|
+
export function closingPeriod(input, closingDay) {
|
|
98
|
+
checkClosingDay(closingDay);
|
|
99
|
+
const month = typeof input === "string" && input.split("-").length === 3
|
|
100
|
+
? parseMonth(closingMonth(input, closingDay))
|
|
101
|
+
: parseMonth(input);
|
|
102
|
+
const previous = shiftMonth(month, -1);
|
|
103
|
+
const to = dayOfMonth(month, closingDayIn(month, closingDay));
|
|
104
|
+
const from = addDays(dayOfMonth(previous, closingDayIn(previous, closingDay)), 1);
|
|
105
|
+
return Object.freeze({ from, to });
|
|
76
106
|
}
|
package/dist/core/schedule.d.ts
CHANGED
|
@@ -283,6 +283,11 @@ export interface HolidayLookup<Kind extends string = string> {
|
|
|
283
283
|
/** 入力をタイムゾーンでの暦日と時刻に変換する */
|
|
284
284
|
civilOf(input: DateTimeInput): CivilDateTimeParts<string>;
|
|
285
285
|
civilOf(input: DateInput): CivilDateTimeParts;
|
|
286
|
+
/**
|
|
287
|
+
* 暦日 "YYYY-MM-DD" と時刻("HH:mm" または "HH:mm:ss")から、タイムゾーンでのその瞬間を Date で返す(civilOf の逆向き)。
|
|
288
|
+
* 夏時間で存在しない時刻は飛んだ分だけ後ろに、2 回ある時刻は先のほう(toInstant と同じ)
|
|
289
|
+
*/
|
|
290
|
+
instantOf(date: DateString, time: string): Date;
|
|
286
291
|
/** 今日の日付(タイムゾーン) */
|
|
287
292
|
today(now?: Date): DateString;
|
|
288
293
|
/** 祝日か */
|
|
@@ -429,6 +434,8 @@ type HolidayLookupInternal<Kind extends string> = ReturnType<typeof createHolida
|
|
|
429
434
|
export declare function createResolver<T, Kind extends string>(compiled: CompiledSchedule<T, Kind>, lookup: HolidayLookupInternal<Kind>): (date: DateString) => Resolution<T, Kind>;
|
|
430
435
|
/** @internal 期間(両端を含む)の日ごとに fn を呼んだ結果。from が to より後ならエラー */
|
|
431
436
|
export declare function mapDays<R>(from: DateString, to: DateString, fn: (date: DateString) => R): readonly R[];
|
|
437
|
+
/** @internal スケジュールの内部の判定を取り出す(createSchedule が作ったものでなければ undefined) */
|
|
438
|
+
export declare function infoOfSchedule<T, Kind extends string>(schedule: Schedule<T, Kind>): ((date: DateString) => ScheduleDay<T, Kind>) | undefined;
|
|
432
439
|
/** @internal 検証済みの規則からスケジュールを作る */
|
|
433
440
|
export declare function buildSchedule<T, Kind extends string>(compiled: CompiledSchedule<T, Kind>): Schedule<T, Kind>;
|
|
434
441
|
/**
|
package/dist/core/schedule.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { addDays, daysInMonth, fromDayNumber, isValidDate, parseDate, toDayNumber, weekdayOf, } from "./date.js";
|
|
21
21
|
import { checkUnknownKeys, IssueCollector, rangeError } from "./errors.js";
|
|
22
|
-
import { instantToCivil, toCivil, toDateTimeParts, validateTimeZone, } from "./input.js";
|
|
22
|
+
import { instantToCivil, toCivil, toDateTimeParts, toInstant, validateTimeZone, } from "./input.js";
|
|
23
23
|
// ------------------------------------------------------------
|
|
24
24
|
// 入力チェック(誤りを集める。作成時は最初の誤りを例外にする)
|
|
25
25
|
// ------------------------------------------------------------
|
|
@@ -577,6 +577,7 @@ export function createHolidayLookup(source) {
|
|
|
577
577
|
holidayOn,
|
|
578
578
|
dateOf,
|
|
579
579
|
civilOf: ((input) => toDateTimeParts(input, timeZone)),
|
|
580
|
+
instantOf: (date, time) => toInstant(date, time, timeZone),
|
|
580
581
|
today: (now = new Date()) => instantToCivil(now.getTime(), timeZone).date,
|
|
581
582
|
isHoliday: (input) => holidayOn(dateOf(input)) !== undefined,
|
|
582
583
|
getHoliday: (input) => holidayOn(dateOf(input)),
|
|
@@ -773,6 +774,15 @@ export function mapDays(from, to, fn) {
|
|
|
773
774
|
result.push(fn(fromDayNumber(n)));
|
|
774
775
|
return Object.freeze(result);
|
|
775
776
|
}
|
|
777
|
+
/**
|
|
778
|
+
* @internal スケジュールの内部の判定(暦日の文字列を直接受け取る)。createCalendarFromSchedule が、公開メソッド
|
|
779
|
+
* (入力の解析を通る)を経由せずに 1 日分の結果を得るために使う。スケジュールごとに作成時に登録する
|
|
780
|
+
*/
|
|
781
|
+
const internalInfo = new WeakMap();
|
|
782
|
+
/** @internal スケジュールの内部の判定を取り出す(createSchedule が作ったものでなければ undefined) */
|
|
783
|
+
export function infoOfSchedule(schedule) {
|
|
784
|
+
return internalInfo.get(schedule);
|
|
785
|
+
}
|
|
776
786
|
/** @internal 検証済みの規則からスケジュールを作る */
|
|
777
787
|
export function buildSchedule(compiled) {
|
|
778
788
|
const lookup = createHolidayLookup(compiled.source);
|
|
@@ -784,6 +794,7 @@ export function buildSchedule(compiled) {
|
|
|
784
794
|
outOfRange: lookup.outOfRange,
|
|
785
795
|
dateOf: lookup.dateOf,
|
|
786
796
|
civilOf: lookup.civilOf,
|
|
797
|
+
instantOf: lookup.instantOf,
|
|
787
798
|
today: lookup.today,
|
|
788
799
|
isHoliday: lookup.isHoliday,
|
|
789
800
|
getHoliday: lookup.getHoliday,
|
|
@@ -793,7 +804,9 @@ export function buildSchedule(compiled) {
|
|
|
793
804
|
dayInfo: (input) => info(lookup.dateOf(input)),
|
|
794
805
|
daysBetween: (from, to) => mapDays(lookup.dateOf(from), lookup.dateOf(to), info),
|
|
795
806
|
};
|
|
796
|
-
|
|
807
|
+
Object.freeze(schedule);
|
|
808
|
+
internalInfo.set(schedule, info);
|
|
809
|
+
return schedule;
|
|
797
810
|
}
|
|
798
811
|
/**
|
|
799
812
|
* 曜日・祝日・特定日の規則から、日ごとに値を返すスケジュールを作る。
|
package/dist/index.d.ts
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* 公開する API には TSDoc の @public(安定)または @beta(マイナー版で変わりうる)を付ける。
|
|
8
8
|
* 公開する名前と型は etc/api.md に記録し、pnpm check で意図しない変化を検出する。
|
|
9
9
|
*/
|
|
10
|
-
export { combineCalendars, createCalendar, type AdjustOptions, type AnnualDate, type AnnualRange, type BusinessDayConvention, type BusinessDayOptions, type Calendar, type CalendarOptions, type CombineOptions, type DateRange, type DayInfo, type DayReason, type DueDateOptions, type HolidayPolicy, type KnownDayReason, type SpecificDate, } from "./core/calendar.ts";
|
|
10
|
+
export { combineCalendars, createCalendar, createCalendarFromSchedule, type AdjustOptions, type AnnualDate, type AnnualRange, type BusinessDayConvention, type BusinessDayOptions, type Calendar, type CalendarOptions, type CombineOptions, type DateRange, type DayInfo, type DayReason, type DueDateOptions, type HolidayPolicy, type KnownDayReason, type SpecificDate, } from "./core/calendar.ts";
|
|
11
11
|
export { createSchedule, type AnnualRangeValue, type AnnualValue, type CivilDateTimeParts, type DateRangeValue, type DateValue, type DayAfterHolidayPattern, type DayOfMonth, type DayOfMonthPattern, type EffectivePeriod, type HolidayLookup, type HolidayPattern, type HolidaySourceOptions, type HolidayValueRule, type KnownScheduleReason, type Month, type Nth, type OnHolidayRule, type RecurringPattern, type RecurringRule, type RecurringValue, type Schedule, type ScheduleDay, type ScheduleOptions, type ScheduleReason, type WeekdayPattern, } from "./core/schedule.ts";
|
|
12
12
|
export { addDays, diffDays, isValidDate, weekdayOf, type DateString, type Weekday, } from "./core/date.ts";
|
|
13
|
-
export { closingMonth, monthOf, type MonthString } from "./core/month.ts";
|
|
13
|
+
export { closingMonth, closingPeriod, monthOf, type MonthString } from "./core/month.ts";
|
|
14
14
|
export { isBizdaysError, type ErrorCode, type KnownErrorCode } from "./core/errors.ts";
|
|
15
|
-
export { toDateString, toDateTimeParts, type DateInput, type DateTimeInput, type TemporalDateLike, type TemporalDateTimeLike, type TemporalInstantLike, } from "./core/input.ts";
|
|
15
|
+
export { toDateString, toDateTimeParts, toInstant, type DateInput, type DateTimeInput, type TemporalDateLike, type TemporalDateTimeLike, type TemporalInstantLike, } from "./core/input.ts";
|
|
16
16
|
export type { Holiday, HolidayProvider } from "./core/types.ts";
|
package/dist/index.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* 公開する API には TSDoc の @public(安定)または @beta(マイナー版で変わりうる)を付ける。
|
|
8
8
|
* 公開する名前と型は etc/api.md に記録し、pnpm check で意図しない変化を検出する。
|
|
9
9
|
*/
|
|
10
|
-
export { combineCalendars, createCalendar, } from "./core/calendar.js";
|
|
10
|
+
export { combineCalendars, createCalendar, createCalendarFromSchedule, } from "./core/calendar.js";
|
|
11
11
|
export { createSchedule, } from "./core/schedule.js";
|
|
12
12
|
export { addDays, diffDays, isValidDate, weekdayOf, } from "./core/date.js";
|
|
13
|
-
export { closingMonth, monthOf } from "./core/month.js";
|
|
13
|
+
export { closingMonth, closingPeriod, monthOf } from "./core/month.js";
|
|
14
14
|
export { isBizdaysError } from "./core/errors.js";
|
|
15
|
-
export { toDateString, toDateTimeParts, } from "./core/input.js";
|
|
15
|
+
export { toDateString, toDateTimeParts, toInstant, } from "./core/input.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bizdays-toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Holidays, business-day calendar and day-by-day schedules (e.g. store hours) with time zone support. Japanese holidays verified against the Cabinet Office data; UK bank holidays (England, Wales, Scotland, Northern Ireland) verified against GOV.UK. / 祝日と営業日のカレンダー(タイムゾーン対応・日本の祝日は内閣府、英国の bank holidays は GOV.UK のデータで全日照合)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bank-holidays",
|