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.
- package/CHANGELOG.md +31 -1
- package/README.md +21 -2
- package/dist/core/calendar.d.ts +63 -10
- package/dist/core/calendar.js +77 -46
- package/dist/core/date.d.ts +24 -6
- package/dist/core/date.js +24 -10
- package/dist/core/errors.d.ts +78 -0
- package/dist/core/errors.js +61 -0
- package/dist/core/input.d.ts +25 -10
- package/dist/core/input.js +41 -21
- package/dist/core/schedule.d.ts +110 -16
- package/dist/core/schedule.js +296 -73
- package/dist/core/types.d.ts +11 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +5 -1
- package/dist/jp/index.d.ts +41 -12
- package/dist/jp/index.js +36 -10
- package/dist/jp/rules.d.ts +17 -8
- package/dist/jp/rules.js +8 -7
- package/package.json +5 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* コード付きの例外と、入力の問題を集める仕組み。
|
|
3
|
+
*
|
|
4
|
+
* 例外は従来どおり RangeError・TypeError・Error のインスタンスで、code プロパティを持つ。
|
|
5
|
+
* 例外のメッセージの文言は API に含まない(変わることがある)。判定には code を使う。
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* このライブラリが投げた例外(code 付き)か。
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
export function isBizdaysError(error) {
|
|
12
|
+
return (error instanceof Error &&
|
|
13
|
+
typeof error.code === "string" &&
|
|
14
|
+
error.bizdays === true);
|
|
15
|
+
}
|
|
16
|
+
function withCode(error, code) {
|
|
17
|
+
Object.defineProperty(error, "code", { value: code, enumerable: true });
|
|
18
|
+
Object.defineProperty(error, "bizdays", { value: true, enumerable: false });
|
|
19
|
+
return error;
|
|
20
|
+
}
|
|
21
|
+
/** @internal */
|
|
22
|
+
export function rangeError(code, message) {
|
|
23
|
+
return withCode(new RangeError(message), code);
|
|
24
|
+
}
|
|
25
|
+
/** @internal */
|
|
26
|
+
export function typeError(code, message) {
|
|
27
|
+
return withCode(new TypeError(message), code);
|
|
28
|
+
}
|
|
29
|
+
/** @internal */
|
|
30
|
+
export function plainError(code, message) {
|
|
31
|
+
return withCode(new Error(message), code);
|
|
32
|
+
}
|
|
33
|
+
/** @internal 問題を集める */
|
|
34
|
+
export class IssueCollector {
|
|
35
|
+
issues = [];
|
|
36
|
+
error(code, path, message) {
|
|
37
|
+
this.issues.push({ severity: "error", code, path, message });
|
|
38
|
+
}
|
|
39
|
+
warning(code, path, message) {
|
|
40
|
+
this.issues.push({ severity: "warning", code, path, message });
|
|
41
|
+
}
|
|
42
|
+
get hasErrors() {
|
|
43
|
+
return this.issues.some((i) => i.severity === "error");
|
|
44
|
+
}
|
|
45
|
+
/** 最初の誤りを例外にして投げる(誤りがなければ何もしない) */
|
|
46
|
+
throwFirstError() {
|
|
47
|
+
const first = this.issues.find((i) => i.severity === "error");
|
|
48
|
+
if (!first)
|
|
49
|
+
return;
|
|
50
|
+
const message = first.path ? `${first.path}: ${first.message}` : first.message;
|
|
51
|
+
throw rangeError(first.code, message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/** @internal 設定のオブジェクトに、知らないキーがあれば警告として記録する */
|
|
55
|
+
export function checkUnknownKeys(collector, path, value, known) {
|
|
56
|
+
for (const key of Object.keys(value)) {
|
|
57
|
+
if (!known.includes(key)) {
|
|
58
|
+
collector.warning("unknown-option", path ? `${path}.${key}` : key, `Unknown option ${JSON.stringify(key)} is ignored`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/dist/core/input.d.ts
CHANGED
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
* - Temporal.PlainDate / PlainDateTime / ZonedDateTime / Instant(ダックタイピング。依存は持たない)
|
|
10
10
|
*/
|
|
11
11
|
import { type DateString } from "./date.ts";
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Temporal.PlainDate / PlainDateTime として受け付ける最小限の形。
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
13
16
|
export interface TemporalDateLike {
|
|
14
17
|
readonly year: number;
|
|
15
18
|
readonly month: number;
|
|
@@ -18,30 +21,42 @@ export interface TemporalDateLike {
|
|
|
18
21
|
readonly minute?: number;
|
|
19
22
|
readonly calendarId?: string;
|
|
20
23
|
}
|
|
21
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Temporal.Instant / ZonedDateTime として受け付ける最小限の形。
|
|
26
|
+
* @public
|
|
27
|
+
*/
|
|
22
28
|
export interface TemporalInstantLike {
|
|
23
29
|
readonly epochMilliseconds: number;
|
|
24
30
|
}
|
|
25
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* 日付・日時として受け付ける入力。
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
26
35
|
export type DateInput = string | Date | TemporalDateLike | TemporalInstantLike;
|
|
27
|
-
/** 暦日と、分かる場合は 0 時からの経過分 */
|
|
36
|
+
/** @internal 暦日と、分かる場合は 0 時からの経過分 */
|
|
28
37
|
export interface CivilDateTime {
|
|
29
38
|
readonly date: DateString;
|
|
30
39
|
/** 0 時からの経過分。暦日だけの入力では null */
|
|
31
40
|
readonly minutes: number | null;
|
|
32
41
|
}
|
|
33
|
-
/** IANA タイムゾーン名として使えるか確かめ、正規化した名前を返す(不正なら RangeError) */
|
|
42
|
+
/** @internal IANA タイムゾーン名として使えるか確かめ、正規化した名前を返す(不正なら RangeError) */
|
|
34
43
|
export declare function validateTimeZone(timeZone: string): string;
|
|
35
|
-
/** 瞬間(エポックミリ秒)を、タイムゾーンでの暦日と時刻に変換する */
|
|
44
|
+
/** @internal 瞬間(エポックミリ秒)を、タイムゾーンでの暦日と時刻に変換する */
|
|
36
45
|
export declare function instantToCivil(epochMilliseconds: number, timeZone: string): CivilDateTime;
|
|
37
|
-
/** 入力をタイムゾーンでの暦日と時刻に変換する */
|
|
46
|
+
/** @internal 入力をタイムゾーンでの暦日と時刻に変換する */
|
|
38
47
|
export declare function toCivil(input: DateInput, timeZone: string): CivilDateTime;
|
|
39
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* 入力を、タイムゾーンでの暦日 "YYYY-MM-DD" に変換する。
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
40
52
|
export declare function toDateString(input: DateInput, timeZone: string): DateString;
|
|
41
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* 入力を、タイムゾーンでの暦日と時刻("HH:mm"。暦日だけの入力では null)に変換する。
|
|
55
|
+
* @public
|
|
56
|
+
*/
|
|
42
57
|
export declare function toDateTimeParts(input: DateInput, timeZone: string): {
|
|
43
58
|
readonly date: DateString;
|
|
44
59
|
readonly time: string | null;
|
|
45
60
|
};
|
|
46
|
-
/** "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
|
|
61
|
+
/** @internal "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
|
|
47
62
|
export declare function parseTimeOfDay(value: string): number;
|
package/dist/core/input.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* - Temporal.PlainDate / PlainDateTime / ZonedDateTime / Instant(ダックタイピング。依存は持たない)
|
|
10
10
|
*/
|
|
11
11
|
import { formatDate, parseDate } from "./date.js";
|
|
12
|
+
import { rangeError, typeError } from "./errors.js";
|
|
12
13
|
const DATETIME_RE = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d{1,9})?)?(Z|[+-]\d{2}:?\d{2})?$/i;
|
|
13
14
|
const formatters = new Map();
|
|
14
15
|
function formatterFor(timeZone) {
|
|
@@ -27,24 +28,27 @@ function formatterFor(timeZone) {
|
|
|
27
28
|
}
|
|
28
29
|
return f;
|
|
29
30
|
}
|
|
30
|
-
/** IANA タイムゾーン名として使えるか確かめ、正規化した名前を返す(不正なら RangeError) */
|
|
31
|
+
/** @internal IANA タイムゾーン名として使えるか確かめ、正規化した名前を返す(不正なら RangeError) */
|
|
31
32
|
export function validateTimeZone(timeZone) {
|
|
33
|
+
if (typeof timeZone !== "string" || timeZone === "") {
|
|
34
|
+
throw rangeError("invalid-time-zone", `Invalid time zone: ${JSON.stringify(timeZone)}`);
|
|
35
|
+
}
|
|
32
36
|
try {
|
|
33
37
|
return formatterFor(timeZone).resolvedOptions().timeZone;
|
|
34
38
|
}
|
|
35
39
|
catch {
|
|
36
|
-
throw
|
|
40
|
+
throw rangeError("invalid-time-zone", `Invalid time zone: ${JSON.stringify(timeZone)}`);
|
|
37
41
|
}
|
|
38
42
|
}
|
|
39
|
-
/** 瞬間(エポックミリ秒)を、タイムゾーンでの暦日と時刻に変換する */
|
|
43
|
+
/** @internal 瞬間(エポックミリ秒)を、タイムゾーンでの暦日と時刻に変換する */
|
|
40
44
|
export function instantToCivil(epochMilliseconds, timeZone) {
|
|
41
45
|
if (!Number.isFinite(epochMilliseconds))
|
|
42
|
-
throw
|
|
46
|
+
throw rangeError("invalid-time", "Invalid instant");
|
|
43
47
|
const parts = formatterFor(timeZone).formatToParts(new Date(epochMilliseconds));
|
|
44
48
|
const get = (type) => {
|
|
45
49
|
const p = parts.find((x) => x.type === type);
|
|
46
50
|
if (!p)
|
|
47
|
-
throw
|
|
51
|
+
throw rangeError("invalid-time-zone", `Cannot resolve ${type} in ${timeZone}`);
|
|
48
52
|
return Number(p.value);
|
|
49
53
|
};
|
|
50
54
|
return {
|
|
@@ -59,7 +63,7 @@ function wallTime(hour, minute) {
|
|
|
59
63
|
!Number.isInteger(minute) ||
|
|
60
64
|
minute < 0 ||
|
|
61
65
|
minute > 59) {
|
|
62
|
-
throw
|
|
66
|
+
throw rangeError("invalid-time", `Invalid time: ${hour}:${minute}`);
|
|
63
67
|
}
|
|
64
68
|
return hour * 60 + minute;
|
|
65
69
|
}
|
|
@@ -70,21 +74,23 @@ function parseString(value, timeZone) {
|
|
|
70
74
|
}
|
|
71
75
|
const m = DATETIME_RE.exec(value);
|
|
72
76
|
if (!m) {
|
|
73
|
-
throw
|
|
77
|
+
throw rangeError(/^\d{4}-\d{2}-\d{2}[T ]/i.test(value) ? "invalid-time" : "invalid-date", `Invalid date/time string: ${JSON.stringify(value)} (expected YYYY-MM-DD or ISO 8601 date-time)`);
|
|
74
78
|
}
|
|
75
79
|
const date = m[1];
|
|
76
80
|
parseDate(date);
|
|
77
81
|
const minutes = wallTime(Number(m[2]), Number(m[3]));
|
|
78
|
-
if (m[4] !== undefined && Number(m[4]) > 59)
|
|
79
|
-
throw
|
|
82
|
+
if (m[4] !== undefined && Number(m[4]) > 59) {
|
|
83
|
+
throw rangeError("invalid-time", `Invalid time: ${value}`);
|
|
84
|
+
}
|
|
80
85
|
const offset = m[5];
|
|
81
86
|
if (offset === undefined)
|
|
82
87
|
return { date, minutes };
|
|
83
88
|
// オフセット付きは瞬間として扱う
|
|
84
89
|
const normalized = value.replace(" ", "T").replace(/([+-]\d{2})(\d{2})$/, "$1:$2");
|
|
85
90
|
const ms = Date.parse(normalized);
|
|
86
|
-
if (Number.isNaN(ms))
|
|
87
|
-
throw
|
|
91
|
+
if (Number.isNaN(ms)) {
|
|
92
|
+
throw rangeError("invalid-time", `Invalid date/time string: ${JSON.stringify(value)}`);
|
|
93
|
+
}
|
|
88
94
|
return instantToCivil(ms, timeZone);
|
|
89
95
|
}
|
|
90
96
|
function isInstantLike(value) {
|
|
@@ -94,18 +100,25 @@ function isDateLike(value) {
|
|
|
94
100
|
const v = value;
|
|
95
101
|
return typeof v.year === "number" && typeof v.month === "number" && typeof v.day === "number";
|
|
96
102
|
}
|
|
97
|
-
/**
|
|
103
|
+
/**
|
|
104
|
+
* Date か(iframe や vm など、別の実行環境で作られた Date も含む)。
|
|
105
|
+
* instanceof は実行環境ごとに Date が別物になるため使わない
|
|
106
|
+
*/
|
|
107
|
+
function isDate(value) {
|
|
108
|
+
return Object.prototype.toString.call(value) === "[object Date]";
|
|
109
|
+
}
|
|
110
|
+
/** @internal 入力をタイムゾーンでの暦日と時刻に変換する */
|
|
98
111
|
export function toCivil(input, timeZone) {
|
|
99
112
|
if (typeof input === "string")
|
|
100
113
|
return parseString(input, timeZone);
|
|
101
|
-
if (input
|
|
102
|
-
return instantToCivil(
|
|
114
|
+
if (isDate(input))
|
|
115
|
+
return instantToCivil(Date.prototype.getTime.call(input), timeZone);
|
|
103
116
|
if (typeof input === "object" && input !== null) {
|
|
104
117
|
if (isInstantLike(input))
|
|
105
118
|
return instantToCivil(input.epochMilliseconds, timeZone);
|
|
106
119
|
if (isDateLike(input)) {
|
|
107
120
|
if (input.calendarId !== undefined && input.calendarId !== "iso8601") {
|
|
108
|
-
throw
|
|
121
|
+
throw rangeError("invalid-input", `Unsupported calendar: ${input.calendarId} (only iso8601)`);
|
|
109
122
|
}
|
|
110
123
|
const date = formatDate(input.year, input.month, input.day);
|
|
111
124
|
if (typeof input.hour === "number") {
|
|
@@ -114,13 +127,19 @@ export function toCivil(input, timeZone) {
|
|
|
114
127
|
return { date, minutes: null };
|
|
115
128
|
}
|
|
116
129
|
}
|
|
117
|
-
throw
|
|
130
|
+
throw typeError("invalid-input", "Unsupported date input: expected a YYYY-MM-DD string, an ISO 8601 date-time string, a Date, or a Temporal object");
|
|
118
131
|
}
|
|
119
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* 入力を、タイムゾーンでの暦日 "YYYY-MM-DD" に変換する。
|
|
134
|
+
* @public
|
|
135
|
+
*/
|
|
120
136
|
export function toDateString(input, timeZone) {
|
|
121
137
|
return toCivil(input, validateTimeZone(timeZone)).date;
|
|
122
138
|
}
|
|
123
|
-
/**
|
|
139
|
+
/**
|
|
140
|
+
* 入力を、タイムゾーンでの暦日と時刻("HH:mm"。暦日だけの入力では null)に変換する。
|
|
141
|
+
* @public
|
|
142
|
+
*/
|
|
124
143
|
export function toDateTimeParts(input, timeZone) {
|
|
125
144
|
const civil = toCivil(input, validateTimeZone(timeZone));
|
|
126
145
|
const time = civil.minutes === null
|
|
@@ -128,10 +147,11 @@ export function toDateTimeParts(input, timeZone) {
|
|
|
128
147
|
: `${String(Math.floor(civil.minutes / 60)).padStart(2, "0")}:${String(civil.minutes % 60).padStart(2, "0")}`;
|
|
129
148
|
return Object.freeze({ date: civil.date, time });
|
|
130
149
|
}
|
|
131
|
-
/** "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
|
|
150
|
+
/** @internal "HH:mm" を 0 時からの経過分に変換する(00:00〜23:59) */
|
|
132
151
|
export function parseTimeOfDay(value) {
|
|
133
152
|
const m = /^(\d{2}):(\d{2})$/.exec(value);
|
|
134
|
-
if (!m)
|
|
135
|
-
throw
|
|
153
|
+
if (!m) {
|
|
154
|
+
throw rangeError("invalid-time", `Invalid time of day: ${JSON.stringify(value)} (expected HH:mm)`);
|
|
155
|
+
}
|
|
136
156
|
return wallTime(Number(m[1]), Number(m[2]));
|
|
137
157
|
}
|
package/dist/core/schedule.d.ts
CHANGED
|
@@ -10,9 +10,13 @@
|
|
|
10
10
|
* 営業日カレンダー(createCalendar)は、このスケジュールの真偽値版として作られている。
|
|
11
11
|
*/
|
|
12
12
|
import { type DateString, type Weekday } from "./date.ts";
|
|
13
|
+
import { IssueCollector } from "./errors.ts";
|
|
13
14
|
import { type DateInput } from "./input.ts";
|
|
14
15
|
import type { Holiday, HolidayProvider } from "./types.ts";
|
|
15
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* 祝日の値の決め方。
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
16
20
|
export type HolidayRule<T> =
|
|
17
21
|
/** 祝日を考えず、曜日の値を使う */
|
|
18
22
|
"weekday"
|
|
@@ -24,33 +28,48 @@ export type HolidayRule<T> =
|
|
|
24
28
|
| {
|
|
25
29
|
readonly value: T;
|
|
26
30
|
};
|
|
27
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* 毎年の月日 "MM-DD" の値。
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
28
35
|
export interface AnnualValue<T> {
|
|
29
36
|
readonly date: string;
|
|
30
37
|
readonly value: T;
|
|
31
38
|
readonly name?: string;
|
|
32
39
|
}
|
|
33
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* 毎年の月日の期間の値(from > to なら年をまたぐ。例: 12-31 〜 01-03)。
|
|
42
|
+
* @public
|
|
43
|
+
*/
|
|
34
44
|
export interface AnnualRangeValue<T> {
|
|
35
45
|
readonly from: string;
|
|
36
46
|
readonly to: string;
|
|
37
47
|
readonly value: T;
|
|
38
48
|
readonly name?: string;
|
|
39
49
|
}
|
|
40
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* 個別の日付 "YYYY-MM-DD" の値。
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
41
54
|
export interface DateValue<T> {
|
|
42
55
|
readonly date: DateString;
|
|
43
56
|
readonly value: T;
|
|
44
57
|
readonly name?: string;
|
|
45
58
|
}
|
|
46
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* 個別の日付の期間(両端を含む)の値。
|
|
61
|
+
* @public
|
|
62
|
+
*/
|
|
47
63
|
export interface DateRangeValue<T> {
|
|
48
64
|
readonly from: DateString;
|
|
49
65
|
readonly to: DateString;
|
|
50
66
|
readonly value: T;
|
|
51
67
|
readonly name?: string;
|
|
52
68
|
}
|
|
53
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* 祝日の提供元とタイムゾーンの設定(スケジュールと営業日カレンダーで共通)。
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
54
73
|
export interface HolidaySourceOptions<Kind extends string = string> {
|
|
55
74
|
/** 祝日の提供元(例: jp)。複数渡すと和集合になる */
|
|
56
75
|
readonly holidays?: HolidayProvider<Kind> | readonly HolidayProvider<Kind>[];
|
|
@@ -62,6 +81,10 @@ export interface HolidaySourceOptions<Kind extends string = string> {
|
|
|
62
81
|
/** 祝日の提供元の対応年を外れた日付の扱い(既定: "throw") */
|
|
63
82
|
readonly outOfRange?: "throw" | "ignore";
|
|
64
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* スケジュールの設定。
|
|
86
|
+
* @public
|
|
87
|
+
*/
|
|
65
88
|
export interface ScheduleOptions<T, Kind extends string = string> extends HolidaySourceOptions<Kind> {
|
|
66
89
|
/** 曜日ごとの値([日, 月, 火, 水, 木, 金, 土]) */
|
|
67
90
|
readonly weekly: readonly [T, T, T, T, T, T, T];
|
|
@@ -72,9 +95,21 @@ export interface ScheduleOptions<T, Kind extends string = string> extends Holida
|
|
|
72
95
|
/** 個別の日付の値 */
|
|
73
96
|
readonly dates?: readonly (DateValue<T> | DateRangeValue<T>)[];
|
|
74
97
|
}
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
|
|
98
|
+
/**
|
|
99
|
+
* 値を決めた規則(既知の値)。
|
|
100
|
+
* @public
|
|
101
|
+
*/
|
|
102
|
+
export type KnownScheduleReason = "date" | "annual" | "holiday" | "weekday";
|
|
103
|
+
/**
|
|
104
|
+
* 値を決めた規則。将来の版で値が増えることがあるので、分岐では「その他」も扱うこと。
|
|
105
|
+
* すべての値を網羅したことを型で確かめたい場合は KnownScheduleReason を使う。
|
|
106
|
+
* @public
|
|
107
|
+
*/
|
|
108
|
+
export type ScheduleReason = KnownScheduleReason | (string & {});
|
|
109
|
+
/**
|
|
110
|
+
* 1 日分の値と、その値を決めた規則。
|
|
111
|
+
* @public
|
|
112
|
+
*/
|
|
78
113
|
export interface ScheduleDay<T, Kind extends string = string> {
|
|
79
114
|
readonly date: DateString;
|
|
80
115
|
readonly weekday: Weekday;
|
|
@@ -86,14 +121,20 @@ export interface ScheduleDay<T, Kind extends string = string> {
|
|
|
86
121
|
/** その日が祝日なら祝日(holiday の規則に関係なく付く) */
|
|
87
122
|
readonly holiday?: Holiday<Kind>;
|
|
88
123
|
}
|
|
89
|
-
/**
|
|
124
|
+
/**
|
|
125
|
+
* 暦日と時刻(カレンダーのタイムゾーン)。
|
|
126
|
+
* @public
|
|
127
|
+
*/
|
|
90
128
|
export interface CivilDateTimeParts {
|
|
91
129
|
/** 暦日 "YYYY-MM-DD" */
|
|
92
130
|
readonly date: DateString;
|
|
93
131
|
/** 時刻 "HH:mm"。暦日だけの入力では null */
|
|
94
132
|
readonly time: string | null;
|
|
95
133
|
}
|
|
96
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* 祝日を調べる機能(スケジュールと営業日カレンダーで共通)。
|
|
136
|
+
* @public
|
|
137
|
+
*/
|
|
97
138
|
export interface HolidayLookup<Kind extends string = string> {
|
|
98
139
|
/** 日時の入力を暦日に変換するタイムゾーン */
|
|
99
140
|
readonly timeZone: string;
|
|
@@ -114,17 +155,70 @@ export interface HolidayLookup<Kind extends string = string> {
|
|
|
114
155
|
/** 期間内の祝日(両端を含む) */
|
|
115
156
|
holidaysBetween(from: DateInput, to: DateInput): readonly Holiday<Kind>[];
|
|
116
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* 日ごとに値を返すスケジュール。
|
|
160
|
+
* @public
|
|
161
|
+
*/
|
|
117
162
|
export interface Schedule<T, Kind extends string = string> extends HolidayLookup<Kind> {
|
|
118
163
|
/** その日の値 */
|
|
119
164
|
valueOn(input: DateInput): T;
|
|
120
165
|
/** その日の値と、値を決めた規則 */
|
|
121
166
|
dayInfo(input: DateInput): ScheduleDay<T, Kind>;
|
|
122
167
|
}
|
|
123
|
-
/** "MM-DD"
|
|
124
|
-
export declare function
|
|
125
|
-
/**
|
|
126
|
-
export declare
|
|
168
|
+
/** @internal "MM-DD" として正しいか(2 月 29 日は可) */
|
|
169
|
+
export declare function isValidMonthDay(value: unknown): value is string;
|
|
170
|
+
/** @internal 曜日の番号として正しいか */
|
|
171
|
+
export declare const isWeekday: (value: unknown) => value is Weekday;
|
|
172
|
+
/** @internal 個別の日付の規則(日数で表した期間) */
|
|
173
|
+
export interface DateRule<T> {
|
|
174
|
+
readonly from: number;
|
|
175
|
+
readonly to: number;
|
|
176
|
+
readonly value: T;
|
|
177
|
+
readonly name: string | undefined;
|
|
178
|
+
}
|
|
179
|
+
/** @internal 毎年の月日の規則 */
|
|
180
|
+
export interface AnnualRule<T> {
|
|
181
|
+
readonly from: string;
|
|
182
|
+
readonly to: string;
|
|
183
|
+
readonly value: T;
|
|
184
|
+
readonly name: string | undefined;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* @internal 個別の日付の規則を検証して取り込む。
|
|
188
|
+
* entry は "YYYY-MM-DD"・{ date }・{ from, to } のいずれか。値は valueOf で決める
|
|
189
|
+
*/
|
|
190
|
+
export declare function compileDateRules<T>(c: IssueCollector, path: string, entries: unknown, valueOf: (entry: Record<string, unknown>, path: string) => T | undefined, allowString: boolean): DateRule<T>[];
|
|
191
|
+
/** @internal 毎年の月日の規則を検証して取り込む(entry は "MM-DD"・{ date }・{ from, to }) */
|
|
192
|
+
export declare function compileAnnualRules<T>(c: IssueCollector, path: string, entries: unknown, valueOf: (entry: Record<string, unknown>, path: string) => T | undefined, allowString: boolean): AnnualRule<T>[];
|
|
193
|
+
/** @internal 祝日の提供元とタイムゾーンの設定を検証した結果 */
|
|
194
|
+
export interface HolidaySource<Kind extends string> {
|
|
195
|
+
readonly providers: readonly HolidayProvider<Kind>[];
|
|
196
|
+
readonly timeZone: string;
|
|
197
|
+
readonly outOfRange: "throw" | "ignore";
|
|
198
|
+
}
|
|
199
|
+
/** @internal 祝日の提供元とタイムゾーンの設定を検証する */
|
|
200
|
+
export declare function compileHolidaySource<Kind extends string>(c: IssueCollector, options: Record<string, unknown>): HolidaySource<Kind>;
|
|
201
|
+
/** @internal 祝日を調べる機能を作る(設定は検証済みであること) */
|
|
202
|
+
export declare function createHolidayLookup<Kind extends string>(source: HolidaySource<Kind>): HolidayLookup<Kind> & {
|
|
127
203
|
holidayOn(date: DateString): Holiday<Kind> | undefined;
|
|
128
204
|
};
|
|
129
|
-
/**
|
|
205
|
+
/** @internal 検証済みのスケジュールの規則 */
|
|
206
|
+
export interface CompiledSchedule<T, Kind extends string> {
|
|
207
|
+
readonly source: HolidaySource<Kind>;
|
|
208
|
+
readonly weekly: readonly T[];
|
|
209
|
+
readonly holiday: HolidayRule<T>;
|
|
210
|
+
readonly annual: readonly AnnualRule<T>[];
|
|
211
|
+
readonly dates: readonly DateRule<T>[];
|
|
212
|
+
}
|
|
213
|
+
/** @internal スケジュールの設定を検証して取り込む(誤りは c に集める) */
|
|
214
|
+
export declare function compileScheduleOptions<T, Kind extends string>(c: IssueCollector, options: unknown): CompiledSchedule<T, Kind> | undefined;
|
|
215
|
+
/** @internal 検証済みの規則からスケジュールを作る */
|
|
216
|
+
export declare function buildSchedule<T, Kind extends string>(compiled: CompiledSchedule<T, Kind>): Schedule<T, Kind>;
|
|
217
|
+
/**
|
|
218
|
+
* 曜日・祝日・特定日の規則から、日ごとに値を返すスケジュールを作る。
|
|
219
|
+
*
|
|
220
|
+
* 設定は作る時点で複製されるので、あとから元の配列やオブジェクトを書き換えても結果は変わらない
|
|
221
|
+
* (値 T そのものは複製しない)。設定の誤りは code 付きの RangeError になる。
|
|
222
|
+
* @public
|
|
223
|
+
*/
|
|
130
224
|
export declare function createSchedule<T, Kind extends string = string>(options: ScheduleOptions<T, Kind>): Schedule<T, Kind>;
|