bizdays-toolkit 0.8.1 → 0.9.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 CHANGED
@@ -4,6 +4,32 @@
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.9.0] - 2026-10-18
8
+
9
+ ### 移行
10
+
11
+ 作業は要りません(機能の追加だけ)。時刻を必ず持つ入力では `time` の型が `string` になったので、これまでの `null` の分岐は不要になります(残しても動く)。版ごとの作業は [docs/migration.md](./docs/migration.md) にまとめた。
12
+
13
+ ### Added
14
+
15
+ - 期間の日ごとの情報を返す `daysBetween(from, to)` を、スケジュール(`ScheduleDay` の配列)と営業日カレンダー(`DayInfo` の配列)に追加した。両端を含み、`from` が `to` より後なら `invalid-range`。祝日だけ欲しい場合は、結果を `holiday` で絞る
16
+ - 時刻を必ず持つ入力の型 `DateTimeInput`(`Date`・Temporal の `Instant`・`ZonedDateTime`・`PlainDateTime`・`"YYYY-MM-DDTHH:mm…"` の文字列)と、`TemporalDateTimeLike` を追加した
17
+
18
+ ### Changed
19
+
20
+ - `civilOf`(スケジュール・営業日カレンダー)・`toDateTimeParts`・`toJapanDateTime` に、`DateTimeInput` を渡したときは `time` の型を `string` とするオーバーロードを加えた。返す値は変わらない。`CivilDateTimeParts` は時刻の型を引数に取る型になった(省略時はこれまでと同じ `string | null`)
21
+ - README: スケジュールの `dayInfo` の `reason` は値を決めた規則であり、祝日の規則が `"weekday"` のときは祝日でも `"weekday"` になること(祝日かどうかは `holiday` で判定する)を書いた。`createCalendar`・`createSchedule` は作ったものを使い回すのが基本で、作成のコストも小さいこと(測った値)を書いた。1 つの配列で持つ特別営業日を `dates` と `annual` に振り分ける例と、型定義の確認の範囲(CI で `moduleResolution` の `node16`・`nodenext`・`bundler`、0.9.0 の時点で TypeScript 5.0・5.5・5.8 も手作業で確認)を足した
22
+ - README のサイズの値を測り直した(ルート約 7.5KB・日本の祝日込み約 9.0KB・英国の祝日込み約 9.1KB。0.9.0 で測定)
23
+ - 移行ガイド(`docs/migration.md`)を追加した。0.2.0〜0.9.0 で利用者がする作業を版ごとにまとめた
24
+
25
+ ### 開発
26
+
27
+ - `pnpm check` で、利用者のプロジェクトとして公開する型定義を型チェックするようにした(`scripts/check-consumer-types.ts`・`test/consumer/consumer.mts`。`moduleResolution` を `node16`・`nodenext`・`bundler` に変え、ライブラリの型定義の検査も有効にする)
28
+
29
+ - GOV.UK の監視を、外部の cron サービス(cron-job.org)から `workflow_dispatch` で起動する形にした(毎日 06:00 日本時間)。GitHub の schedule は予備として 18:17(日本時間)に移した。GitHub の schedule は高負荷時に遅れたり落ちたりし、追加してから一度も動かなかったため。動いたことは最後に healthchecks.io へ知らせ(Secrets の `HEALTHCHECKS_PING_URL`。照合そのものが失敗したときは `/fail`)、知らせが途絶えたらメールが届くようにした(RELEASING.md の「監視の起動とハートビート」。cron-job.org からの呼び出しは REST API の 2026-03-10 版を指定する)
30
+
31
+ - ブラウザーでの確認(`scripts/test-browsers.ts`)に時間制限を設けた。ページの読み込みとスクリプトの実行は 30 秒(WebDriver の `timeouts`)、ドライバーへの各呼び出しは 60 秒。結果を受け取る前の段階(ドライバーの起動・セッションの作成・ページの読み込み・結果の待ち)で止まったときだけ、ドライバーを起動し直して 1 回だけやり直し、その理由を `RETRY` としてログに出す。確認の結果が FAIL のときはやり直さない。CI でランナー上の Edge がページの読み込みで応答しなくなり、既定の 300 秒を待って失敗したため
32
+
7
33
  ## [0.8.1] - 2026-10-18
8
34
 
9
35
  ライブラリのコードは 0.8.0 と同じ。動作を確かめる環境を広げ、README の記述を確かめた範囲に合わせた。
@@ -200,7 +226,8 @@
200
226
 
201
227
  - 日本: 2026 年 2 月の官報(2027 年の春分・秋分の日)まで反映(`dataVersion: "2026-02"`、`confirmedThrough: 2027`)
202
228
 
203
- [Unreleased]: https://github.com/inohan77/bizdays-toolkit/compare/v0.8.1...HEAD
229
+ [Unreleased]: https://github.com/inohan77/bizdays-toolkit/compare/v0.9.0...HEAD
230
+ [0.9.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.8.1...v0.9.0
204
231
  [0.8.1]: https://github.com/inohan77/bizdays-toolkit/compare/v0.8.0...v0.8.1
205
232
  [0.8.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.7.0...v0.8.0
206
233
  [0.7.0]: https://github.com/inohan77/bizdays-toolkit/compare/v0.6.0...v0.7.0
package/README.md CHANGED
@@ -26,7 +26,7 @@ A business-day calendar that combines public holidays with company or store clos
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
28
  - **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
29
- - **Lightweight, zero dependencies**: about 7.4 KB min+gzip (about 9.0 KB with Japanese holidays, about 9.0 KB with UK bank holidays; measured for 0.8.1). 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
29
+ - **Lightweight, zero dependencies**: about 7.5 KB min+gzip (about 9.0 KB with Japanese holidays, about 9.1 KB with UK bank holidays; measured for 0.9.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
30
30
  - **Extensible to other countries and regions**: holiday providers are pluggable through the `HolidayProvider` interface. Japan and the UK are bundled
31
31
 
32
32
  ## Installation
@@ -37,6 +37,8 @@ pnpm add bizdays-toolkit
37
37
 
38
38
  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.
39
39
 
40
+ Types are bundled. CI type-checks a consumer project against the published type definitions with `moduleResolution` set to `node16`, `nodenext` and `bundler` (TypeScript 6); TypeScript 5.0, 5.5 and 5.8 were also checked by hand for 0.9.0.
41
+
40
42
  ## Usage
41
43
 
42
44
  ### Looking up holidays
@@ -112,8 +114,13 @@ shipping.businessDayOnOrAfter(new Date(), { cutoff: "14:00" });
112
114
 
113
115
  shipping.dayInfo("2027-01-01");
114
116
  // { date: "2027-01-01", weekday: 5, businessDay: false, reason: "closed-annual", name: "New Year closure", holiday: {…} }
117
+ shipping.daysBetween("2026-12-30", "2027-01-04").map((d) => d.businessDay); // [true, false, false, false, false, true]
115
118
  ```
116
119
 
120
+ `daysBetween(from, to)` returns `dayInfo` for every day in the period (both ends included).
121
+
122
+ `createCalendar` and `createSchedule` validate the options when they are created, so the usual pattern is to create them once and reuse them. Creating is also cheap (about 0.02 ms for a calendar and 0.04 ms for a schedule with Japanese holidays on a development machine), so creating one per request is not a problem in practice.
123
+
117
124
  Each day is decided in this order of priority (top first).
118
125
 
119
126
  1. `openDates` (specific open days)
@@ -203,6 +210,23 @@ const jpWithoutCitizensHoliday = {
203
210
  const calendar = createCalendar({ holidays: jpWithoutCitizensHoliday, closedWeekdays: [0, 6] });
204
211
  ```
205
212
 
213
+ **Split special days kept in one list**: dates (`"YYYY-MM-DD"`) go in `dates` and annual dates (`"MM-DD"`) in `annual`.
214
+
215
+ ```ts
216
+ type Hours = { open: string; close: string } | null;
217
+ const regular: Hours = { open: "10:00", close: "19:00" };
218
+ const specialDays: { date: string; value: Hours }[] = [
219
+ { date: "2026-12-31", value: { open: "10:00", close: "17:00" } },
220
+ { date: "01-01", value: null },
221
+ ];
222
+ const special = createSchedule<Hours>({
223
+ weekly: [regular, regular, regular, regular, regular, regular, regular],
224
+ dates: specialDays.filter((d) => d.date.length === 10),
225
+ annual: specialDays.filter((d) => d.date.length === 5),
226
+ });
227
+ special.valueOn("2027-01-01"); // null
228
+ ```
229
+
206
230
  **Build per-store calendars from shared options**: options are plain objects, so spread and extend them.
207
231
 
208
232
  ```ts
@@ -288,8 +312,16 @@ const storeHours = createSchedule<Hours>({
288
312
  storeHours.valueOn("2026-11-03"); // { open: "10:00", close: "19:00" } (Culture Day uses Sunday hours)
289
313
  storeHours.dayInfo("2027-01-01");
290
314
  // { date: "2027-01-01", weekday: 5, value: null, reason: "annual", name: "Closed on New Year's Day", holiday: {…} }
315
+
316
+ // Holidays in a period with their hours: filter daysBetween by holiday
317
+ storeHours
318
+ .daysBetween("2026-11-01", "2026-11-30")
319
+ .filter((d) => d.holiday)
320
+ .map((d) => d.date); // ["2026-11-03", "2026-11-23"]
291
321
  ```
292
322
 
323
+ `reason` is the rule that decided the value. With `holiday: "weekday"` (holidays follow the weekday rule), a holiday has `reason: "weekday"`, so check `holiday` to know whether a day is a holiday.
324
+
293
325
  Values are decided in the order `dates` (specific dates and ranges) → `annual` (annual dates and ranges) → `recurring` (`"over-holiday"`) → holidays → `recurring` (default and moved days) → `weekly` (weekday). When rules of the same type overlap, the one written first in the array wins.
294
326
 
295
327
  Checking a time range such as "is the store open now" is left to your app, because industries differ (opening past midnight, breaks and so on). With `civilOf` you get the civil date and time in the time zone, and the check is short.
@@ -297,10 +329,10 @@ Checking a time range such as "is the store open now" is left to your app, becau
297
329
  ```ts
298
330
  const { date, time } = storeHours.civilOf(new Date()); // { date: "2026-10-14", time: "16:30" } (Japan time)
299
331
  const hours = storeHours.valueOn(date);
300
- const isOpen = hours !== null && time !== null && hours.open <= time && time < hours.close;
332
+ const isOpen = hours !== null && hours.open <= time && time < hours.close;
301
333
  ```
302
334
 
303
- `civilOf` is also on business-day calendars, and the same conversion is available as `toJapanDateTime` in `bizdays-toolkit/jp` and `toDateTimeParts(input, timeZone)` in the root.
335
+ 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 `toJapanDateTime` in `bizdays-toolkit/jp` and `toDateTimeParts(input, timeZone)` in the root.
304
336
 
305
337
  ### Date and date-time inputs
306
338
 
@@ -448,7 +480,7 @@ Versioning follows [Semantic Versioning](https://semver.org/). See [RELEASING.md
448
480
  - Official data updates (new, moved and one-off holidays by law, Official Gazette or proclamation, fixed equinox days in Japan, newly published years on GOV.UK) are released as **patch** versions, so they reach you with either `^` or `~`
449
481
  - To freeze the holiday results, pin an exact version (for example `"bizdays-toolkit": "0.8.0"`)
450
482
  - The reflected data is shown by `dataVersion` (for Japan, the year and month of the Official Gazette reflected; for the UK, the year and month of the GOV.UK data reflected) and `confirmedThrough`
451
- - Before 1.0.0, minor versions may contain breaking changes
483
+ - Before 1.0.0, minor versions may contain breaking changes. What you need to change when moving between minor versions is listed in the [migration guide](https://github.com/inohan77/bizdays-toolkit/blob/main/docs/migration.md) (Japanese) and in the "移行" (migration) part of each version in the CHANGELOG
452
484
  - Decision reasons (`reason`), holiday kinds (`kind`), error codes and diagnostic codes may gain values in future versions. When you branch on their values, handle "anything else" too (see "互換性の約束", the compatibility promise, in [RELEASING.md](https://github.com/inohan77/bizdays-toolkit/blob/main/RELEASING.md))
453
485
 
454
486
  ## Data sources
@@ -161,6 +161,8 @@ export interface DueDateOptions {
161
161
  export interface Calendar<Kind extends string = string> extends HolidayLookup<Kind> {
162
162
  /** 1 日分の判定結果(営業日か・理由・祝日) */
163
163
  dayInfo(input: DateInput): DayInfo<Kind>;
164
+ /** 期間内の日ごとの判定結果(両端を含む。from が to より後ならエラー) */
165
+ daysBetween(from: DateInput, to: DateInput): readonly DayInfo<Kind>[];
164
166
  /** 営業日か */
165
167
  isBusinessDay(input: DateInput): boolean;
166
168
  /** 翌営業日(入力日の翌日以降) */
@@ -20,7 +20,7 @@ import { addDays, daysInMonth, formatDate, fromDayNumber, toDayNumber, } from ".
20
20
  import { checkUnknownKeys, IssueCollector, plainError, rangeError } from "./errors.js";
21
21
  import { parseTimeOfDay, toCivil } from "./input.js";
22
22
  import { dayOfMonth, isDayOfMonthValue, parseMonth } from "./month.js";
23
- import { buildSchedule, compileAnnualRules, compileDateRules, compileHolidaySource, compileRecurringRules, isWeekday, RECURRING_RULE_KEYS, } from "./schedule.js";
23
+ import { buildSchedule, compileAnnualRules, compileDateRules, compileHolidaySource, compileRecurringRules, isWeekday, mapDays, RECURRING_RULE_KEYS, } from "./schedule.js";
24
24
  const CALENDAR_OPTION_KEYS = [
25
25
  "holidays",
26
26
  "timeZone",
@@ -236,6 +236,7 @@ export function createCalendar(options = {}) {
236
236
  holidaysInYear: schedule.holidaysInYear,
237
237
  holidaysBetween: schedule.holidaysBetween,
238
238
  dayInfo: (input) => info(dateOf(input)),
239
+ daysBetween: (from, to) => mapDays(dateOf(from), dateOf(to), info),
239
240
  isBusinessDay: (input) => isOpen(dateOf(input)),
240
241
  nextBusinessDay: (input) => search(addDays(dateOf(input), 1), 1),
241
242
  previousBusinessDay: (input) => search(addDays(dateOf(input), -1), -1),
@@ -33,6 +33,30 @@ export interface TemporalInstantLike {
33
33
  * @public
34
34
  */
35
35
  export type DateInput = string | Date | TemporalDateLike | TemporalInstantLike;
36
+ /**
37
+ * Temporal.PlainDateTime として受け付ける最小限の形(時刻を持つ TemporalDateLike)。
38
+ * @public
39
+ */
40
+ export interface TemporalDateTimeLike extends TemporalDateLike {
41
+ readonly hour: number;
42
+ readonly minute: number;
43
+ }
44
+ /**
45
+ * 時刻を必ず持つ入力(瞬間、Temporal.PlainDateTime、"YYYY-MM-DDTHH:mm…" の文字列)。
46
+ * これを渡すと、civilOf・toDateTimeParts・toJapanDateTime の time の型が string になる。
47
+ * @public
48
+ */
49
+ export type DateTimeInput = Date | TemporalInstantLike | TemporalDateTimeLike | `${number}-${number}-${number}T${string}`;
50
+ /**
51
+ * タイムゾーンでの暦日と時刻。Time は時刻の型で、時刻を必ず持つ入力(DateTimeInput)では string になる。
52
+ * @public
53
+ */
54
+ export interface CivilDateTimeParts<Time extends string | null = string | null> {
55
+ /** 暦日 "YYYY-MM-DD" */
56
+ readonly date: DateString;
57
+ /** 時刻 "HH:mm"。暦日だけの入力では null */
58
+ readonly time: Time;
59
+ }
36
60
  /** @internal 暦日と、分かる場合は 0 時からの経過分 */
37
61
  export interface CivilDateTime {
38
62
  readonly date: DateString;
@@ -52,11 +76,11 @@ export declare function toCivil(input: DateInput, timeZone: string): CivilDateTi
52
76
  export declare function toDateString(input: DateInput, timeZone: string): DateString;
53
77
  /**
54
78
  * 入力を、タイムゾーンでの暦日と時刻("HH:mm"。暦日だけの入力では null)に変換する。
79
+ * 時刻を必ず持つ入力(DateTimeInput)では、time の型が string になる。
55
80
  * @public
56
81
  */
57
- export declare function toDateTimeParts(input: DateInput, timeZone: string): {
58
- readonly date: DateString;
59
- readonly time: string | null;
60
- };
82
+ export declare function toDateTimeParts(input: DateTimeInput, timeZone: string): CivilDateTimeParts<string>;
83
+ /** @public */
84
+ export declare function toDateTimeParts(input: DateInput, timeZone: string): CivilDateTimeParts;
61
85
  /** @internal "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
62
86
  export declare function parseTimeOfDay(value: string): number;
@@ -136,10 +136,6 @@ export function toCivil(input, timeZone) {
136
136
  export function toDateString(input, timeZone) {
137
137
  return toCivil(input, validateTimeZone(timeZone)).date;
138
138
  }
139
- /**
140
- * 入力を、タイムゾーンでの暦日と時刻("HH:mm"。暦日だけの入力では null)に変換する。
141
- * @public
142
- */
143
139
  export function toDateTimeParts(input, timeZone) {
144
140
  const civil = toCivil(input, validateTimeZone(timeZone));
145
141
  const time = civil.minutes === null
@@ -16,7 +16,8 @@
16
16
  */
17
17
  import { type DateString, type Weekday } from "./date.ts";
18
18
  import { IssueCollector } from "./errors.ts";
19
- import { type DateInput } from "./input.ts";
19
+ import { type CivilDateTimeParts, type DateInput, type DateTimeInput } from "./input.ts";
20
+ export type { CivilDateTimeParts } from "./input.ts";
20
21
  import type { Holiday, HolidayProvider } from "./types.ts";
21
22
  /**
22
23
  * 祝日の値の決め方(スケジュールの holiday の設定)。
@@ -198,16 +199,6 @@ export interface ScheduleDay<T, Kind extends string = string> {
198
199
  /** 祝日から振り替えられた規則で値が決まった場合、振替の元の日付(onHolidayRule: "after-holiday") */
199
200
  readonly shiftedFrom?: DateString;
200
201
  }
201
- /**
202
- * 暦日と時刻(カレンダーのタイムゾーン)。
203
- * @public
204
- */
205
- export interface CivilDateTimeParts {
206
- /** 暦日 "YYYY-MM-DD" */
207
- readonly date: DateString;
208
- /** 時刻 "HH:mm"。暦日だけの入力では null */
209
- readonly time: string | null;
210
- }
211
202
  /**
212
203
  * 祝日を調べる機能(スケジュールと営業日カレンダーで共通)。
213
204
  * @public
@@ -220,6 +211,7 @@ export interface HolidayLookup<Kind extends string = string> {
220
211
  /** 入力をタイムゾーンでの暦日に変換する */
221
212
  dateOf(input: DateInput): DateString;
222
213
  /** 入力をタイムゾーンでの暦日と時刻に変換する */
214
+ civilOf(input: DateTimeInput): CivilDateTimeParts<string>;
223
215
  civilOf(input: DateInput): CivilDateTimeParts;
224
216
  /** 今日の日付(タイムゾーン) */
225
217
  today(now?: Date): DateString;
@@ -239,8 +231,13 @@ export interface HolidayLookup<Kind extends string = string> {
239
231
  export interface Schedule<T, Kind extends string = string> extends HolidayLookup<Kind> {
240
232
  /** その日の値 */
241
233
  valueOn(input: DateInput): T;
242
- /** その日の値と、値を決めた規則 */
234
+ /**
235
+ * その日の値と、値を決めた規則(reason)。reason は値を決めた規則なので、祝日の規則が "weekday"(曜日どおり)の
236
+ * ときは、祝日でも "weekday" になる。祝日かどうかは holiday の有無で判定する
237
+ */
243
238
  dayInfo(input: DateInput): ScheduleDay<T, Kind>;
239
+ /** 期間内の日ごとの値と規則(両端を含む。from が to より後ならエラー) */
240
+ daysBetween(from: DateInput, to: DateInput): readonly ScheduleDay<T, Kind>[];
244
241
  }
245
242
  /** @internal "MM-DD" として正しいか(2 月 29 日は可) */
246
243
  export declare function isValidMonthDay(value: unknown): value is string;
@@ -331,6 +328,8 @@ type HolidayLookupInternal<Kind extends string> = ReturnType<typeof createHolida
331
328
  * 規則の診断では、規則を取り除いた版と結果を比べるために、祝日を調べる機能(lookup)を共有して作る
332
329
  */
333
330
  export declare function createResolver<T, Kind extends string>(compiled: CompiledSchedule<T, Kind>, lookup: HolidayLookupInternal<Kind>): (date: DateString) => Resolution<T, Kind>;
331
+ /** @internal 期間(両端を含む)の日ごとに fn を呼んだ結果。from が to より後ならエラー */
332
+ export declare function mapDays<R>(from: DateString, to: DateString, fn: (date: DateString) => R): readonly R[];
334
333
  /** @internal 検証済みの規則からスケジュールを作る */
335
334
  export declare function buildSchedule<T, Kind extends string>(compiled: CompiledSchedule<T, Kind>): Schedule<T, Kind>;
336
335
  /**
@@ -341,4 +340,3 @@ export declare function buildSchedule<T, Kind extends string>(compiled: Compiled
341
340
  * @public
342
341
  */
343
342
  export declare function createSchedule<T, Kind extends string = string>(options: ScheduleOptions<T, Kind>): Schedule<T, Kind>;
344
- export {};
@@ -14,7 +14,7 @@
14
14
  *
15
15
  * 営業日カレンダー(createCalendar)は、このスケジュールの真偽値版として作られている。
16
16
  */
17
- import { addDays, daysInMonth, isValidDate, parseDate, toDayNumber, weekdayOf, } from "./date.js";
17
+ import { addDays, daysInMonth, fromDayNumber, isValidDate, parseDate, toDayNumber, weekdayOf, } from "./date.js";
18
18
  import { checkUnknownKeys, IssueCollector, rangeError } from "./errors.js";
19
19
  import { instantToCivil, toCivil, toDateTimeParts, validateTimeZone, } from "./input.js";
20
20
  // ------------------------------------------------------------
@@ -454,7 +454,7 @@ export function createHolidayLookup(source) {
454
454
  providers,
455
455
  holidayOn,
456
456
  dateOf,
457
- civilOf: (input) => toDateTimeParts(input, timeZone),
457
+ civilOf: ((input) => toDateTimeParts(input, timeZone)),
458
458
  today: (now = new Date()) => instantToCivil(now.getTime(), timeZone).date,
459
459
  isHoliday: (input) => holidayOn(dateOf(input)) !== undefined,
460
460
  getHoliday: (input) => holidayOn(dateOf(input)),
@@ -620,6 +620,16 @@ export function createResolver(compiled, lookup) {
620
620
  return make(weekly[weekday], "weekday", null, undefined);
621
621
  };
622
622
  }
623
+ /** @internal 期間(両端を含む)の日ごとに fn を呼んだ結果。from が to より後ならエラー */
624
+ export function mapDays(from, to, fn) {
625
+ if (from > to) {
626
+ throw rangeError("invalid-range", `"from" must not be after "to" (${from} > ${to})`);
627
+ }
628
+ const result = [];
629
+ for (let n = toDayNumber(from); n <= toDayNumber(to); n++)
630
+ result.push(fn(fromDayNumber(n)));
631
+ return Object.freeze(result);
632
+ }
623
633
  /** @internal 検証済みの規則からスケジュールを作る */
624
634
  export function buildSchedule(compiled) {
625
635
  const lookup = createHolidayLookup(compiled.source);
@@ -637,6 +647,7 @@ export function buildSchedule(compiled) {
637
647
  holidaysBetween: lookup.holidaysBetween,
638
648
  valueOn: (input) => info(lookup.dateOf(input)).value,
639
649
  dayInfo: (input) => info(lookup.dateOf(input)),
650
+ daysBetween: (from, to) => mapDays(lookup.dateOf(from), lookup.dateOf(to), info),
640
651
  };
641
652
  return Object.freeze(schedule);
642
653
  }
package/dist/index.d.ts CHANGED
@@ -12,5 +12,5 @@ export { createSchedule, type AnnualRangeValue, type AnnualValue, type CivilDate
12
12
  export { addDays, diffDays, isValidDate, weekdayOf, type DateString, type Weekday, } from "./core/date.ts";
13
13
  export { closingMonth, 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 TemporalDateLike, type TemporalInstantLike, } from "./core/input.ts";
15
+ export { toDateString, toDateTimeParts, type DateInput, type DateTimeInput, type TemporalDateLike, type TemporalDateTimeLike, type TemporalInstantLike, } from "./core/input.ts";
16
16
  export type { Holiday, HolidayProvider } from "./core/types.ts";
@@ -11,7 +11,7 @@
11
11
  * isHoliday("2026-11-23"); // true
12
12
  */
13
13
  import type { DateString } from "../core/date.ts";
14
- import type { DateInput } from "../core/input.ts";
14
+ import type { CivilDateTimeParts, DateInput, DateTimeInput } from "../core/input.ts";
15
15
  import type { Holiday, HolidayProvider } from "../core/types.ts";
16
16
  import { type JapaneseHolidayKind } from "./rules.ts";
17
17
  export type { JapaneseHolidayKind, KnownJapaneseHolidayKind } from "./rules.ts";
@@ -52,12 +52,12 @@ export declare function holidaysBetween(from: DateInput, to: DateInput): readonl
52
52
  export declare function toJapanDate(input: DateInput): DateString;
53
53
  /**
54
54
  * 日本時間での暦日と時刻("HH:mm"。暦日だけの入力では null)。
55
+ * 時刻を必ず持つ入力(Date・Temporal.Instant など)では、time の型が string になる。
55
56
  * @public
56
57
  */
57
- export declare function toJapanDateTime(input: DateInput): {
58
- readonly date: DateString;
59
- readonly time: string | null;
60
- };
58
+ export declare function toJapanDateTime(input: DateTimeInput): CivilDateTimeParts<string>;
59
+ /** @public */
60
+ export declare function toJapanDateTime(input: DateInput): CivilDateTimeParts;
61
61
  /**
62
62
  * 反映済みの官報の年月。
63
63
  * @public
package/dist/jp/index.js CHANGED
@@ -69,10 +69,6 @@ export function holidaysBetween(from, to) {
69
69
  export function toJapanDate(input) {
70
70
  return calendar().dateOf(input);
71
71
  }
72
- /**
73
- * 日本時間での暦日と時刻("HH:mm"。暦日だけの入力では null)。
74
- * @public
75
- */
76
72
  export function toJapanDateTime(input) {
77
73
  return calendar().civilOf(input);
78
74
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bizdays-toolkit",
3
- "version": "0.8.1",
3
+ "version": "0.9.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",
@@ -62,7 +62,7 @@
62
62
  "bench": "node scripts/bench.ts",
63
63
  "fmt": "oxfmt",
64
64
  "fmt:check": "oxfmt --check",
65
- "check": "node scripts/check-toolchain.ts && pnpm typecheck && pnpm lint && pnpm test && pnpm test:tz && pnpm build && pnpm test:compat && node scripts/check-readme-examples.ts && pnpm fmt:check && node scripts/check-package.ts && node scripts/check-size.ts && node scripts/api-report.ts",
65
+ "check": "node scripts/check-toolchain.ts && pnpm typecheck && pnpm lint && pnpm test && pnpm test:tz && pnpm build && pnpm test:compat && node scripts/check-readme-examples.ts && node scripts/check-consumer-types.ts && pnpm fmt:check && node scripts/check-package.ts && node scripts/check-size.ts && node scripts/api-report.ts",
66
66
  "verify:cao": "node scripts/verify-cao.ts",
67
67
  "verify:govuk": "node scripts/verify-govuk.ts",
68
68
  "prepublishOnly": "pnpm check",