bizdays-toolkit 0.2.0 → 0.3.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.
@@ -9,86 +9,263 @@
9
9
  *
10
10
  * 営業日カレンダー(createCalendar)は、このスケジュールの真偽値版として作られている。
11
11
  */
12
- import { parseDate, toDayNumber, weekdayOf } from "./date.js";
12
+ import { isValidDate, parseDate, toDayNumber, weekdayOf, } from "./date.js";
13
+ import { checkUnknownKeys, IssueCollector, rangeError } from "./errors.js";
13
14
  import { instantToCivil, toCivil, toDateTimeParts, validateTimeZone, } from "./input.js";
14
15
  // ------------------------------------------------------------
15
- // 内部の共通処理
16
+ // 入力チェック(誤りを集める。作成時は最初の誤りを例外にする)
16
17
  // ------------------------------------------------------------
17
18
  const MONTH_DAY_RE = /^(\d{2})-(\d{2})$/;
18
19
  const MAX_DAYS_IN_MONTH = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
19
- /** "MM-DD" を検証する(2 月 29 日は可) */
20
- export function parseMonthDay(value) {
20
+ const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21
+ /** @internal "MM-DD" として正しいか(2 月 29 日は可) */
22
+ export function isValidMonthDay(value) {
23
+ if (typeof value !== "string")
24
+ return false;
21
25
  const m = MONTH_DAY_RE.exec(value);
22
- const month = Number(m?.[1]);
23
- const day = Number(m?.[2]);
24
- if (!m || month < 1 || month > 12 || day < 1 || day > MAX_DAYS_IN_MONTH[month - 1]) {
25
- throw new RangeError(`Invalid month-day: ${JSON.stringify(value)} (expected MM-DD)`);
26
+ if (!m)
27
+ return false;
28
+ const month = Number(m[1]);
29
+ const day = Number(m[2]);
30
+ return month >= 1 && month <= 12 && day >= 1 && day <= MAX_DAYS_IN_MONTH[month - 1];
31
+ }
32
+ /** @internal 曜日の番号として正しいか */
33
+ export const isWeekday = (value) => Number.isInteger(value) && value >= 0 && value <= 6;
34
+ function checkName(c, path, name) {
35
+ if (name === undefined)
36
+ return undefined;
37
+ if (typeof name !== "string") {
38
+ c.error("invalid-option", `${path}.name`, "name must be a string");
39
+ return undefined;
40
+ }
41
+ return name;
42
+ }
43
+ function checkDate(c, path, value) {
44
+ if (!isValidDate(value)) {
45
+ c.error("invalid-date", path, `Invalid date ${JSON.stringify(value)} (expected YYYY-MM-DD)`);
46
+ return undefined;
47
+ }
48
+ return toDayNumber(value);
49
+ }
50
+ function checkMonthDay(c, path, value) {
51
+ if (!isValidMonthDay(value)) {
52
+ c.error("invalid-month-day", path, `Invalid month-day ${JSON.stringify(value)} (expected MM-DD)`);
53
+ return undefined;
26
54
  }
27
55
  return value;
28
56
  }
29
- function compileDates(entries, label) {
30
- return (entries ?? []).map((e) => {
31
- if ("date" in e) {
32
- const n = toDayNumber(e.date);
33
- return { from: n, to: n, value: e.value, name: e.name };
57
+ /**
58
+ * @internal 個別の日付の規則を検証して取り込む。
59
+ * entry は "YYYY-MM-DD"・{ date }・{ from, to } のいずれか。値は valueOf で決める
60
+ */
61
+ export function compileDateRules(c, path, entries, valueOf, allowString) {
62
+ if (entries === undefined)
63
+ return [];
64
+ if (!Array.isArray(entries)) {
65
+ c.error("invalid-option", path, "must be an array");
66
+ return [];
67
+ }
68
+ const rules = [];
69
+ entries.forEach((raw, i) => {
70
+ const p = `${path}[${i}]`;
71
+ const entry = allowString && typeof raw === "string" ? { date: raw } : raw;
72
+ if (!isObject(entry)) {
73
+ c.error("invalid-option", p, allowString ? "must be a date string or an object" : "must be an object");
74
+ return;
75
+ }
76
+ const value = valueOf(entry, p);
77
+ const name = checkName(c, p, entry["name"]);
78
+ if ("date" in entry) {
79
+ checkUnknownKeys(c, p, entry, ["date", "name", "value"]);
80
+ const n = checkDate(c, `${p}.date`, entry["date"]);
81
+ if (n !== undefined && value !== undefined)
82
+ rules.push({ from: n, to: n, value, name });
83
+ return;
34
84
  }
35
- const from = toDayNumber(e.from);
36
- const to = toDayNumber(e.to);
85
+ if (!("from" in entry) || !("to" in entry)) {
86
+ c.error("invalid-option", p, 'must have "date" or both "from" and "to"');
87
+ return;
88
+ }
89
+ checkUnknownKeys(c, p, entry, ["from", "to", "name", "value"]);
90
+ const from = checkDate(c, `${p}.from`, entry["from"]);
91
+ const to = checkDate(c, `${p}.to`, entry["to"]);
92
+ if (from === undefined || to === undefined)
93
+ return;
37
94
  if (from > to) {
38
- throw new RangeError(`${label}: "from" must not be after "to" (${e.from} > ${e.to})`);
95
+ c.error("invalid-range", p, `"from" must not be after "to" (${String(entry["from"])} > ${String(entry["to"])})`);
96
+ return;
39
97
  }
40
- return { from, to, value: e.value, name: e.name };
98
+ if (value !== undefined)
99
+ rules.push({ from, to, value, name });
41
100
  });
101
+ return rules;
42
102
  }
43
- function compileAnnual(entries) {
44
- return (entries ?? []).map((e) => {
45
- if ("date" in e) {
46
- const md = parseMonthDay(e.date);
47
- return { from: md, to: md, value: e.value, name: e.name };
103
+ /** @internal 毎年の月日の規則を検証して取り込む(entry は "MM-DD"・{ date }・{ from, to }) */
104
+ export function compileAnnualRules(c, path, entries, valueOf, allowString) {
105
+ if (entries === undefined)
106
+ return [];
107
+ if (!Array.isArray(entries)) {
108
+ c.error("invalid-option", path, "must be an array");
109
+ return [];
110
+ }
111
+ const rules = [];
112
+ entries.forEach((raw, i) => {
113
+ const p = `${path}[${i}]`;
114
+ const entry = allowString && typeof raw === "string" ? { date: raw } : raw;
115
+ if (!isObject(entry)) {
116
+ c.error("invalid-option", p, allowString ? "must be a month-day string or an object" : "must be an object");
117
+ return;
118
+ }
119
+ const value = valueOf(entry, p);
120
+ const name = checkName(c, p, entry["name"]);
121
+ if ("date" in entry) {
122
+ checkUnknownKeys(c, p, entry, ["date", "name", "value"]);
123
+ const md = checkMonthDay(c, `${p}.date`, entry["date"]);
124
+ if (md !== undefined && value !== undefined)
125
+ rules.push({ from: md, to: md, value, name });
126
+ return;
127
+ }
128
+ if (!("from" in entry) || !("to" in entry)) {
129
+ c.error("invalid-option", p, 'must have "date" or both "from" and "to"');
130
+ return;
131
+ }
132
+ checkUnknownKeys(c, p, entry, ["from", "to", "name", "value"]);
133
+ const from = checkMonthDay(c, `${p}.from`, entry["from"]);
134
+ const to = checkMonthDay(c, `${p}.to`, entry["to"]);
135
+ if (from !== undefined && to !== undefined && value !== undefined) {
136
+ rules.push({ from, to, value, name });
48
137
  }
49
- return { from: parseMonthDay(e.from), to: parseMonthDay(e.to), value: e.value, name: e.name };
50
138
  });
139
+ return rules;
51
140
  }
52
- function matchAnnual(rule, monthDay) {
53
- return rule.from <= rule.to
54
- ? monthDay >= rule.from && monthDay <= rule.to
55
- : monthDay >= rule.from || monthDay <= rule.to;
141
+ /** 規則の value を取り出す(value が必須の規則用) */
142
+ function requiredValue(c) {
143
+ return (entry, path) => {
144
+ if (!("value" in entry)) {
145
+ c.error("invalid-option", path, 'must have "value"');
146
+ return undefined;
147
+ }
148
+ return entry["value"];
149
+ };
56
150
  }
57
- /** 祝日を調べる機能を作る(スケジュールと営業日カレンダーで共通) */
58
- export function createHolidayLookup(options) {
59
- const providers = Object.freeze(options.holidays === undefined
60
- ? []
61
- : Array.isArray(options.holidays)
62
- ? [...options.holidays]
63
- : [options.holidays]);
64
- let timeZone;
65
- if (options.timeZone !== undefined) {
66
- timeZone = validateTimeZone(options.timeZone);
151
+ function validateProvider(c, path, p) {
152
+ const bad = (message) => {
153
+ c.error("invalid-provider", path, message);
154
+ return false;
155
+ };
156
+ if (!isObject(p))
157
+ return bad("must be a holiday provider object");
158
+ if (typeof p["id"] !== "string" || p["id"] === "")
159
+ return bad('"id" must be a non-empty string');
160
+ if (typeof p["timeZone"] !== "string")
161
+ return bad('"timeZone" must be a string');
162
+ try {
163
+ validateTimeZone(p["timeZone"]);
164
+ }
165
+ catch {
166
+ return bad(`"timeZone" is not a valid IANA time zone: ${JSON.stringify(p["timeZone"])}`);
167
+ }
168
+ const years = p["supportedYears"];
169
+ if (!isObject(years) ||
170
+ !Number.isInteger(years["from"]) ||
171
+ !Number.isInteger(years["to"]) ||
172
+ years["from"] < 1000 ||
173
+ years["to"] > 9999 ||
174
+ years["from"] > years["to"]) {
175
+ return bad('"supportedYears" must be { from, to } with integer years in 1000-9999 and from <= to');
176
+ }
177
+ if (typeof p["dataVersion"] !== "string")
178
+ return bad('"dataVersion" must be a string');
179
+ if (p["confirmedThrough"] !== undefined && !Number.isInteger(p["confirmedThrough"])) {
180
+ return bad('"confirmedThrough" must be an integer year');
181
+ }
182
+ if (typeof p["holidaysInYear"] !== "function")
183
+ return bad('"holidaysInYear" must be a function');
184
+ return true;
185
+ }
186
+ /** @internal 祝日の提供元とタイムゾーンの設定を検証する */
187
+ export function compileHolidaySource(c, options) {
188
+ const raw = options["holidays"];
189
+ const list = raw === undefined ? [] : Array.isArray(raw) ? raw : [raw];
190
+ const providers = [];
191
+ list.forEach((p, i) => {
192
+ const path = Array.isArray(raw) ? `holidays[${i}]` : "holidays";
193
+ if (validateProvider(c, path, p))
194
+ providers.push(p);
195
+ });
196
+ let timeZone = "UTC";
197
+ if (options["timeZone"] !== undefined) {
198
+ try {
199
+ timeZone = validateTimeZone(options["timeZone"]);
200
+ }
201
+ catch {
202
+ c.error("invalid-time-zone", "timeZone", `Invalid time zone: ${JSON.stringify(options["timeZone"])}`);
203
+ }
67
204
  }
68
205
  else {
69
206
  const zones = [...new Set(providers.map((p) => validateTimeZone(p.timeZone)))];
70
207
  if (zones.length > 1) {
71
- throw new RangeError(`Holiday providers use different time zones (${zones.join(", ")}); specify options.timeZone`);
208
+ c.error("time-zone-conflict", "timeZone", `Holiday providers use different time zones (${zones.join(", ")}); specify timeZone`);
72
209
  }
73
210
  timeZone = zones[0] ?? "UTC";
74
211
  }
75
- const outOfRange = options.outOfRange ?? "throw";
76
- const yearCache = new Map();
77
- function providerYears(provider, year) {
78
- const { from, to } = provider.supportedYears;
79
- if (year < from || year > to) {
80
- if (outOfRange === "ignore")
81
- return [];
82
- throw new RangeError(`Holidays for ${provider.id} are supported for ${from}-${to}; requested ${year}`);
83
- }
84
- return provider.holidaysInYear(year);
212
+ const outOfRange = options["outOfRange"] ?? "throw";
213
+ if (outOfRange !== "throw" && outOfRange !== "ignore") {
214
+ c.error("invalid-option", "outOfRange", 'must be "throw" or "ignore"');
85
215
  }
216
+ return {
217
+ providers: Object.freeze(providers),
218
+ timeZone,
219
+ outOfRange: outOfRange === "ignore" ? "ignore" : "throw",
220
+ };
221
+ }
222
+ // ------------------------------------------------------------
223
+ // 祝日を調べる機能
224
+ // ------------------------------------------------------------
225
+ function providerDataError(provider, year, message) {
226
+ return rangeError("invalid-provider-data", `Holiday provider ${provider.id} returned invalid data for ${year}: ${message}`);
227
+ }
228
+ /** 提供元が返したデータを検証し、契約の項目だけを持つ変更できないオブジェクトにする */
229
+ function loadProviderYear(provider, year) {
230
+ const list = provider.holidaysInYear(year);
231
+ if (!Array.isArray(list))
232
+ throw providerDataError(provider, year, "holidaysInYear must return an array");
233
+ const seen = new Set();
234
+ return list.map((h, i) => {
235
+ if (!isObject(h))
236
+ throw providerDataError(provider, year, `item ${i} is not an object`);
237
+ const { date, name, nameEn, kind, confirmed } = h;
238
+ if (!isValidDate(date) || parseDate(date).year !== year) {
239
+ throw providerDataError(provider, year, `item ${i} has an invalid date ${JSON.stringify(date)}`);
240
+ }
241
+ if (seen.has(date))
242
+ throw providerDataError(provider, year, `duplicate date ${date}`);
243
+ seen.add(date);
244
+ if (typeof name !== "string" || typeof nameEn !== "string" || typeof kind !== "string") {
245
+ throw providerDataError(provider, year, `item ${i} (${date}) must have string name, nameEn and kind`);
246
+ }
247
+ if (typeof confirmed !== "boolean") {
248
+ throw providerDataError(provider, year, `item ${i} (${date}) must have boolean confirmed`);
249
+ }
250
+ return Object.freeze({ date, name, nameEn, kind: kind, confirmed });
251
+ });
252
+ }
253
+ /** @internal 祝日を調べる機能を作る(設定は検証済みであること) */
254
+ export function createHolidayLookup(source) {
255
+ const { providers, timeZone, outOfRange } = source;
256
+ const yearCache = new Map();
86
257
  function holidayMap(year) {
87
258
  let map = yearCache.get(year);
88
259
  if (!map) {
89
260
  map = new Map();
90
261
  for (const provider of providers) {
91
- for (const h of providerYears(provider, year)) {
262
+ const { from, to } = provider.supportedYears;
263
+ if (year < from || year > to) {
264
+ if (outOfRange === "ignore")
265
+ continue;
266
+ throw rangeError("out-of-range", `Holidays for ${provider.id} are supported for ${from}-${to}; requested ${year}`);
267
+ }
268
+ for (const h of loadProviderYear(provider, year)) {
92
269
  // 同じ日に複数の提供元の祝日がある場合は、先に渡した提供元を優先する
93
270
  if (!map.has(h.date))
94
271
  map.set(h.date, h);
@@ -106,7 +283,7 @@ export function createHolidayLookup(options) {
106
283
  const dateOf = (input) => toCivil(input, timeZone).date;
107
284
  function holidaysInYear(year) {
108
285
  if (!Number.isInteger(year))
109
- throw new RangeError(`Invalid year: ${year}`);
286
+ throw rangeError("invalid-argument", `Invalid year: ${year}`);
110
287
  return Object.freeze([...holidayMap(year).values()].sort((a, b) => a.date < b.date ? -1 : a.date > b.date ? 1 : 0));
111
288
  }
112
289
  return {
@@ -123,7 +300,7 @@ export function createHolidayLookup(options) {
123
300
  const a = dateOf(from);
124
301
  const b = dateOf(to);
125
302
  if (a > b)
126
- throw new RangeError(`"from" must not be after "to" (${a} > ${b})`);
303
+ throw rangeError("invalid-range", `"from" must not be after "to" (${a} > ${b})`);
127
304
  const result = [];
128
305
  for (let y = parseDate(a).year; y <= parseDate(b).year; y++) {
129
306
  for (const h of holidaysInYear(y))
@@ -134,28 +311,61 @@ export function createHolidayLookup(options) {
134
311
  },
135
312
  };
136
313
  }
137
- // ------------------------------------------------------------
138
- // createSchedule
139
- // ------------------------------------------------------------
140
- /** 曜日・祝日・特定日の規則から、日ごとに値を返すスケジュールを作る */
141
- export function createSchedule(options) {
142
- const weekly = options.weekly;
143
- if (!Array.isArray(weekly) || weekly.length !== 7) {
144
- throw new RangeError("weekly must have 7 values ([Sunday, Monday, ..., Saturday])");
145
- }
146
- const holidayRule = options.holiday ?? "weekday";
147
- if (typeof holidayRule === "object" && "sameAs" in holidayRule) {
148
- const w = holidayRule.sameAs;
149
- if (!Number.isInteger(w) || w < 0 || w > 6)
150
- throw new RangeError(`Invalid weekday: ${w}`);
151
- }
152
- else if (holidayRule !== "weekday" &&
153
- !(typeof holidayRule === "object" && "value" in holidayRule)) {
154
- throw new RangeError(`Invalid holiday rule: ${JSON.stringify(holidayRule)}`);
155
- }
156
- const dates = compileDates(options.dates, "dates");
157
- const annual = compileAnnual(options.annual);
158
- const lookup = createHolidayLookup(options);
314
+ const SCHEDULE_OPTION_KEYS = [
315
+ "holidays",
316
+ "timeZone",
317
+ "outOfRange",
318
+ "weekly",
319
+ "holiday",
320
+ "annual",
321
+ "dates",
322
+ ];
323
+ /** @internal スケジュールの設定を検証して取り込む(誤りは c に集める) */
324
+ export function compileScheduleOptions(c, options) {
325
+ if (!isObject(options)) {
326
+ c.error("invalid-option", "", "options must be an object");
327
+ return undefined;
328
+ }
329
+ checkUnknownKeys(c, "", options, SCHEDULE_OPTION_KEYS);
330
+ const source = compileHolidaySource(c, options);
331
+ const weeklyRaw = options["weekly"];
332
+ let weekly = [];
333
+ if (!Array.isArray(weeklyRaw) || weeklyRaw.length !== 7) {
334
+ c.error("invalid-option", "weekly", "must have 7 values ([Sunday, Monday, ..., Saturday])");
335
+ }
336
+ else {
337
+ weekly = Object.freeze([...weeklyRaw]);
338
+ }
339
+ let holiday = "weekday";
340
+ const holidayRaw = options["holiday"];
341
+ if (holidayRaw === undefined || holidayRaw === "weekday") {
342
+ holiday = "weekday";
343
+ }
344
+ else if (isObject(holidayRaw) && "sameAs" in holidayRaw) {
345
+ if (isWeekday(holidayRaw["sameAs"]))
346
+ holiday = Object.freeze({ sameAs: holidayRaw["sameAs"] });
347
+ else
348
+ c.error("invalid-weekday", "holiday.sameAs", "must be an integer from 0 (Sunday) to 6 (Saturday)");
349
+ }
350
+ else if (isObject(holidayRaw) && "value" in holidayRaw) {
351
+ holiday = Object.freeze({ value: holidayRaw["value"] });
352
+ }
353
+ else {
354
+ c.error("invalid-option", "holiday", 'must be "weekday", { sameAs: weekday } or { value }');
355
+ }
356
+ const annual = compileAnnualRules(c, "annual", options["annual"], requiredValue(c), false);
357
+ const dates = compileDateRules(c, "dates", options["dates"], requiredValue(c), false);
358
+ return { source, weekly, holiday, annual, dates };
359
+ }
360
+ function matchAnnual(rule, monthDay) {
361
+ return rule.from <= rule.to
362
+ ? monthDay >= rule.from && monthDay <= rule.to
363
+ : monthDay >= rule.from || monthDay <= rule.to;
364
+ }
365
+ /** @internal 検証済みの規則からスケジュールを作る */
366
+ export function buildSchedule(compiled) {
367
+ const { weekly, holiday: holidayRule, annual, dates } = compiled;
368
+ const lookup = createHolidayLookup(compiled.source);
159
369
  function info(date) {
160
370
  const dayNumber = toDayNumber(date);
161
371
  const weekday = weekdayOf(date);
@@ -201,3 +411,16 @@ export function createSchedule(options) {
201
411
  };
202
412
  return Object.freeze(schedule);
203
413
  }
414
+ /**
415
+ * 曜日・祝日・特定日の規則から、日ごとに値を返すスケジュールを作る。
416
+ *
417
+ * 設定は作る時点で複製されるので、あとから元の配列やオブジェクトを書き換えても結果は変わらない
418
+ * (値 T そのものは複製しない)。設定の誤りは code 付きの RangeError になる。
419
+ * @public
420
+ */
421
+ export function createSchedule(options) {
422
+ const c = new IssueCollector();
423
+ const compiled = compileScheduleOptions(c, options);
424
+ c.throwFirstError();
425
+ return buildSchedule(compiled);
426
+ }
@@ -1,5 +1,8 @@
1
1
  import type { DateString } from "./date.ts";
2
- /** 祝日・休日 1 日分 */
2
+ /**
3
+ * 祝日・休日 1 日分。
4
+ * @public
5
+ */
3
6
  export interface Holiday<Kind extends string = string> {
4
7
  /** 暦日 "YYYY-MM-DD" */
5
8
  readonly date: DateString;
@@ -18,7 +21,12 @@ export interface Holiday<Kind extends string = string> {
18
21
  /**
19
22
  * 祝日の提供元(国・地域ごとのルール)。
20
23
  *
21
- * 他の国・地域の祝日はこのインターフェースを実装して createCalendar に渡す。
24
+ * 他の国・地域の祝日はこのインターフェースを実装して createCalendar・createSchedule に渡す。
25
+ * holidaysInYear が返すデータは、年ごとに初めて使うときに検証され、誤りがあれば
26
+ * code: "invalid-provider-data" の例外になる。
27
+ *
28
+ * 互換性の約束: このインターフェースに項目を足すときは、メジャー版以外では省略可能な項目に限る。
29
+ * @public
22
30
  */
23
31
  export interface HolidayProvider<Kind extends string = string> {
24
32
  /** ISO 3166-1 alpha-2(国)または ISO 3166-2(地域)のコード。例: "JP", "US-CA" */
@@ -34,6 +42,6 @@ export interface HolidayProvider<Kind extends string = string> {
34
42
  readonly dataVersion: string;
35
43
  /** この年までは公式に確定している(それより後は confirmed: false を含みうる) */
36
44
  readonly confirmedThrough?: number;
37
- /** その年の祝日・休日を日付順に返す */
45
+ /** その年の祝日・休日(同じ日付は 1 件まで。順序は問わない) */
38
46
  holidaysInYear(year: number): readonly Holiday<Kind>[];
39
47
  }
package/dist/index.d.ts CHANGED
@@ -3,9 +3,13 @@
3
3
  * 曜日・祝日・特定日で変わる値(営業時間など)のスケジュール(createSchedule)。
4
4
  *
5
5
  * 国ごとの祝日はサブパスから読み込む(例: "bizdays-toolkit/jp")。
6
+ *
7
+ * 公開する API には TSDoc の @public(安定)または @beta(マイナー版で変わりうる)を付ける。
8
+ * 公開する名前と型は etc/api.md に記録し、pnpm check で意図しない変化を検出する。
6
9
  */
7
- export { createCalendar, type AnnualDate, type AnnualRange, type BusinessDayOptions, type Calendar, type CalendarOptions, type DateRange, type DayInfo, type DayReason, type HolidayPolicy, type SpecificDate, } from "./core/calendar.ts";
8
- export { createSchedule, type AnnualRangeValue, type AnnualValue, type CivilDateTimeParts, type DateRangeValue, type DateValue, type HolidayLookup, type HolidayRule, type HolidaySourceOptions, type Schedule, type ScheduleDay, type ScheduleOptions, type ScheduleReason, } from "./core/schedule.ts";
9
- export { addDays, diffDays, isValidDate, nthWeekdayOfMonth, weekdayOf, type DateString, type Weekday, } from "./core/date.ts";
10
+ export { createCalendar, type AnnualDate, type AnnualRange, type BusinessDayOptions, type Calendar, type CalendarOptions, type DateRange, type DayInfo, type DayReason, type HolidayPolicy, type KnownDayReason, type SpecificDate, } from "./core/calendar.ts";
11
+ export { createSchedule, type AnnualRangeValue, type AnnualValue, type CivilDateTimeParts, type DateRangeValue, type DateValue, type HolidayLookup, type HolidayRule, type HolidaySourceOptions, type KnownScheduleReason, type Schedule, type ScheduleDay, type ScheduleOptions, type ScheduleReason, } from "./core/schedule.ts";
12
+ export { addDays, diffDays, isValidDate, weekdayOf, type DateString, type Weekday, } from "./core/date.ts";
13
+ export { isBizdaysError, type ErrorCode, type KnownErrorCode } from "./core/errors.ts";
10
14
  export { toDateString, toDateTimeParts, type DateInput, type TemporalDateLike, type TemporalInstantLike, } from "./core/input.ts";
11
15
  export type { Holiday, HolidayProvider } from "./core/types.ts";
package/dist/index.js CHANGED
@@ -3,8 +3,12 @@
3
3
  * 曜日・祝日・特定日で変わる値(営業時間など)のスケジュール(createSchedule)。
4
4
  *
5
5
  * 国ごとの祝日はサブパスから読み込む(例: "bizdays-toolkit/jp")。
6
+ *
7
+ * 公開する API には TSDoc の @public(安定)または @beta(マイナー版で変わりうる)を付ける。
8
+ * 公開する名前と型は etc/api.md に記録し、pnpm check で意図しない変化を検出する。
6
9
  */
7
10
  export { createCalendar, } from "./core/calendar.js";
8
11
  export { createSchedule, } from "./core/schedule.js";
9
- export { addDays, diffDays, isValidDate, nthWeekdayOfMonth, weekdayOf, } from "./core/date.js";
12
+ export { addDays, diffDays, isValidDate, weekdayOf, } from "./core/date.js";
13
+ export { isBizdaysError } from "./core/errors.js";
10
14
  export { toDateString, toDateTimeParts, } from "./core/input.js";
@@ -14,28 +14,57 @@ import type { DateString } from "../core/date.ts";
14
14
  import type { DateInput } from "../core/input.ts";
15
15
  import type { Holiday, HolidayProvider } from "../core/types.ts";
16
16
  import { type JapaneseHolidayKind } from "./rules.ts";
17
- export type { JapaneseHolidayKind } from "./rules.ts";
18
- export { autumnalEquinoxDay, vernalEquinoxDay } from "./rules.ts";
19
- /** 日本の祝日 */
17
+ export type { JapaneseHolidayKind, KnownJapaneseHolidayKind } from "./rules.ts";
18
+ /**
19
+ * 日本の祝日。
20
+ * @public
21
+ */
20
22
  export type JapaneseHoliday = Holiday<JapaneseHolidayKind>;
21
- /** 日本の祝日の提供元(createCalendar の holidays に渡す) */
23
+ /**
24
+ * 日本の祝日の提供元(createCalendar・createSchedule の holidays に渡す)。
25
+ * @public
26
+ */
22
27
  export declare const jp: HolidayProvider<JapaneseHolidayKind>;
23
- /** 祝日か(Date などの瞬間は日本時間で判定する) */
28
+ /**
29
+ * 祝日か(Date などの瞬間は日本時間で判定する)。
30
+ * @public
31
+ */
24
32
  export declare function isHoliday(input: DateInput): boolean;
25
- /** 祝日なら祝日を返す */
33
+ /**
34
+ * 祝日なら祝日を返す。
35
+ * @public
36
+ */
26
37
  export declare function getHoliday(input: DateInput): JapaneseHoliday | undefined;
27
- /** その年の祝日 */
38
+ /**
39
+ * その年の祝日。
40
+ * @public
41
+ */
28
42
  export declare function holidaysInYear(year: number): readonly JapaneseHoliday[];
29
- /** 期間内の祝日(両端を含む) */
43
+ /**
44
+ * 期間内の祝日(両端を含む)。
45
+ * @public
46
+ */
30
47
  export declare function holidaysBetween(from: DateInput, to: DateInput): readonly JapaneseHoliday[];
31
- /** 日本時間での暦日 */
48
+ /**
49
+ * 日本時間での暦日。
50
+ * @public
51
+ */
32
52
  export declare function toJapanDate(input: DateInput): DateString;
33
- /** 日本時間での暦日と時刻("HH:mm"。暦日だけの入力では null) */
53
+ /**
54
+ * 日本時間での暦日と時刻("HH:mm"。暦日だけの入力では null)。
55
+ * @public
56
+ */
34
57
  export declare function toJapanDateTime(input: DateInput): {
35
58
  readonly date: DateString;
36
59
  readonly time: string | null;
37
60
  };
38
- /** 反映済みの官報の年月 */
61
+ /**
62
+ * 反映済みの官報の年月。
63
+ * @public
64
+ */
39
65
  export declare const dataVersion: string;
40
- /** 春分・秋分の日が官報で確定している最後の年 */
66
+ /**
67
+ * 春分・秋分の日が官報で確定している最後の年。
68
+ * @public
69
+ */
41
70
  export declare const confirmedThrough: number;
package/dist/jp/index.js CHANGED
@@ -12,9 +12,11 @@
12
12
  */
13
13
  import { createCalendar } from "../core/calendar.js";
14
14
  import { CONFIRMED_THROUGH, DATA_VERSION, SUPPORTED_YEARS, computeJapaneseHolidays, } from "./rules.js";
15
- export { autumnalEquinoxDay, vernalEquinoxDay } from "./rules.js";
16
15
  const cache = new Map();
17
- /** 日本の祝日の提供元(createCalendar の holidays に渡す) */
16
+ /**
17
+ * 日本の祝日の提供元(createCalendar・createSchedule の holidays に渡す)。
18
+ * @public
19
+ */
18
20
  export const jp = Object.freeze({
19
21
  id: "JP",
20
22
  timeZone: "Asia/Tokyo",
@@ -32,31 +34,55 @@ export const jp = Object.freeze({
32
34
  });
33
35
  let defaultCalendar;
34
36
  const calendar = () => (defaultCalendar ??= createCalendar({ holidays: jp }));
35
- /** 祝日か(Date などの瞬間は日本時間で判定する) */
37
+ /**
38
+ * 祝日か(Date などの瞬間は日本時間で判定する)。
39
+ * @public
40
+ */
36
41
  export function isHoliday(input) {
37
42
  return calendar().isHoliday(input);
38
43
  }
39
- /** 祝日なら祝日を返す */
44
+ /**
45
+ * 祝日なら祝日を返す。
46
+ * @public
47
+ */
40
48
  export function getHoliday(input) {
41
49
  return calendar().getHoliday(input);
42
50
  }
43
- /** その年の祝日 */
51
+ /**
52
+ * その年の祝日。
53
+ * @public
54
+ */
44
55
  export function holidaysInYear(year) {
45
56
  return calendar().holidaysInYear(year);
46
57
  }
47
- /** 期間内の祝日(両端を含む) */
58
+ /**
59
+ * 期間内の祝日(両端を含む)。
60
+ * @public
61
+ */
48
62
  export function holidaysBetween(from, to) {
49
63
  return calendar().holidaysBetween(from, to);
50
64
  }
51
- /** 日本時間での暦日 */
65
+ /**
66
+ * 日本時間での暦日。
67
+ * @public
68
+ */
52
69
  export function toJapanDate(input) {
53
70
  return calendar().dateOf(input);
54
71
  }
55
- /** 日本時間での暦日と時刻("HH:mm"。暦日だけの入力では null) */
72
+ /**
73
+ * 日本時間での暦日と時刻("HH:mm"。暦日だけの入力では null)。
74
+ * @public
75
+ */
56
76
  export function toJapanDateTime(input) {
57
77
  return calendar().civilOf(input);
58
78
  }
59
- /** 反映済みの官報の年月 */
79
+ /**
80
+ * 反映済みの官報の年月。
81
+ * @public
82
+ */
60
83
  export const dataVersion = DATA_VERSION;
61
- /** 春分・秋分の日が官報で確定している最後の年 */
84
+ /**
85
+ * 春分・秋分の日が官報で確定している最後の年。
86
+ * @public
87
+ */
62
88
  export const confirmedThrough = CONFIRMED_THROUGH;