@typescript-calendar-lib/core 0.1.1 → 0.4.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nazozokc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,235 @@
1
+ # @typescript-calendar-lib/core
2
+
3
+ Shared, framework-agnostic calendar utilities: date math, locale data, and grid building. Zero runtime dependencies.
4
+
5
+ > **Tip:** You normally don't need `core` directly — the `cli`, `react`, and `tui` packages re-export the types and utilities you'll need. Reach for `core` when building your own renderer.
6
+
7
+ ## Installation
8
+
9
+ ```sh
10
+ pnpm add @typescript-calendar-lib/core
11
+ # or
12
+ npm install @typescript-calendar-lib/core
13
+ # or
14
+ bun add @typescript-calendar-lib/core
15
+ ```
16
+
17
+ ## Types
18
+
19
+ ### `Locale`
20
+
21
+ ```ts
22
+ type Locale = "en" | "ja" | "es" | "de" | "fr" | "ko" | "zh";
23
+ ```
24
+
25
+ English, Japanese, Spanish, German, French, Korean, and Chinese (Simplified).
26
+
27
+ ### `WeekStart`
28
+
29
+ ```ts
30
+ type WeekStart = "sunday" | "monday";
31
+ ```
32
+
33
+ ### `HighlightStyle`
34
+
35
+ ```ts
36
+ type HighlightStyle = "bracket" | "reverse";
37
+ ```
38
+
39
+ `"bracket"` wraps the day in brackets (`[8]`), `"reverse"` uses reverse-video styling.
40
+
41
+ ### `CalendarOptions`
42
+
43
+ ```ts
44
+ interface CalendarOptions {
45
+ year: number;
46
+ month: number; // 1-indexed (1–12)
47
+ locale?: Locale; // default: "en"
48
+ weekStart?: WeekStart; // default: "sunday"
49
+ highlight?: Date; // date to highlight (e.g. today)
50
+ highlightStyle?: HighlightStyle; // default: "bracket"
51
+ range?: { from: Date; to: Date };
52
+ color?: boolean; // default: false
53
+ }
54
+ ```
55
+
56
+ ### `CalendarYearOptions`
57
+
58
+ Same as `CalendarOptions` minus `month` — renders a full year:
59
+
60
+ ```ts
61
+ interface CalendarYearOptions {
62
+ year: number;
63
+ locale?: Locale;
64
+ weekStart?: WeekStart;
65
+ highlight?: Date;
66
+ highlightStyle?: HighlightStyle;
67
+ range?: { from: Date; to: Date };
68
+ color?: boolean;
69
+ }
70
+ ```
71
+
72
+ ### `CalendarRangeOptions`
73
+
74
+ Same as `CalendarOptions` minus `year`/`month`, rendered for every month between `from` and `to` (inclusive):
75
+
76
+ ```ts
77
+ interface CalendarRangeOptions {
78
+ from: Date;
79
+ to: Date;
80
+ locale?: Locale;
81
+ weekStart?: WeekStart;
82
+ highlight?: Date;
83
+ highlightStyle?: HighlightStyle;
84
+ range?: { from: Date; to: Date };
85
+ color?: boolean;
86
+ }
87
+ ```
88
+
89
+ ### `RenderMonthOptions`
90
+
91
+ Same as `CalendarOptions` minus `year`/`month`. Used by the low-level `renderMonth` helper.
92
+
93
+ ## Constants
94
+
95
+ ### `LOCALES`
96
+
97
+ ```ts
98
+ const LOCALES: Record<Locale, LocaleData>;
99
+ ```
100
+
101
+ Contains month names, weekday headers, and short weekday headers for each locale.
102
+
103
+ ### `MIN_YEAR` / `MAX_YEAR`
104
+
105
+ The supported year range (`1` / `9999`). Values outside this range are rejected by validation.
106
+
107
+ ## Functions
108
+
109
+ ### `getMonthName(locale, month): string`
110
+
111
+ Returns the localized month name for a 1-indexed month:
112
+
113
+ ```ts
114
+ getMonthName("en", 9); // "September"
115
+ getMonthName("ja", 9); // "9月"
116
+ ```
117
+
118
+ ### `getWeekdayHeaders(locale, weekStart): readonly string[]`
119
+
120
+ Returns the weekday header array respecting `weekStart`:
121
+
122
+ ```ts
123
+ getWeekdayHeaders("en", "sunday"); // ["Sun", "Mon", ..., "Sat"]
124
+ getWeekdayHeaders("en", "monday"); // ["Mon", "Tue", ..., "Sun"]
125
+ ```
126
+
127
+ ### `buildMonthGrid(year, month, weekStart): (number | null)[][]`
128
+
129
+ Builds a 6×7 grid of day numbers (`1`–`31`) with `null` for empty cells. The grid is always 6 rows tall for stable layouts:
130
+
131
+ ```ts
132
+ buildMonthGrid(2026, 9, "sunday");
133
+ // [
134
+ // [null, null, 1, 2, 3, 4, 5],
135
+ // [6, 7, 8, 9, 10, 11, 12],
136
+ // ...
137
+ // ]
138
+ ```
139
+
140
+ ### `firstDayOfMonth(year, month): Date`
141
+
142
+ Returns a `Date` for the 1st of the month:
143
+
144
+ ```ts
145
+ firstDayOfMonth(2026, 9); // Tue Sep 01 2026
146
+ ```
147
+
148
+ ### `lastDayOfMonth(year, month): Date`
149
+
150
+ Returns a `Date` for the last day of the month:
151
+
152
+ ```ts
153
+ lastDayOfMonth(2026, 2); // Sat Feb 28 2026 (no leap year)
154
+ lastDayOfMonth(2028, 2); // Tue Feb 29 2028 (leap year)
155
+ ```
156
+
157
+ ### `isDateInRange(date, range?): boolean`
158
+
159
+ Returns `true` if `date` falls within `range` (inclusive). Returns `false` when `range` is `undefined`.
160
+
161
+ Throws `RangeError` when `range.from` is after `range.to`, or when any date is invalid.
162
+
163
+ ### `isSameDay(a, b): boolean`
164
+
165
+ Compares dates by year, month, and day only (ignores time component). Throws `RangeError` when either date is invalid.
166
+
167
+ ### `getMonthRange(from, to): { year: number; month: number }[]`
168
+
169
+ Returns the list of all months from `from` to `to` (inclusive):
170
+
171
+ ```ts
172
+ getMonthRange(new Date(2026, 5, 1), new Date(2026, 8, 30));
173
+ // [{ year: 2026, month: 6 }, { year: 2026, month: 7 },
174
+ // { year: 2026, month: 8 }, { year: 2026, month: 9 }]
175
+ ```
176
+
177
+ Throws `RangeError` when `from` is after `to`, or when either date is invalid.
178
+
179
+ ### `createDate(year, monthIndex, day): Date`
180
+
181
+ Creates a local `Date` at `00:00:00` without the `new Date(year, ...)` 1900-interpretation for years `0`–`99`. `monthIndex` is 0-based like `Date`.
182
+
183
+ ### `assertValidDate(date): void`
184
+
185
+ Throws `RangeError` when `date` is not a `Date` instance or is an `Invalid Date`.
186
+
187
+ ## Input validation
188
+
189
+ All functions validate their inputs and throw `RangeError` on invalid values instead of silently producing wrong results:
190
+
191
+ | Input | Accepted range | Example of invalid input |
192
+ | :--- | :--- | :--- |
193
+ | `year` | integer `1`–`9999` | `0`, `-1`, `10000`, `2026.5`, `NaN` |
194
+ | `month` | integer `1`–`12` | `0`, `13`, `2.5`, `NaN` |
195
+ | `weekStart` | `"sunday"` \| `"monday"` | `"tuesday"` |
196
+ | `locale` | `"en"` \| `"ja"` \| `"es"` \| `"de"` \| `"fr"` \| `"ko"` \| `"zh"` | `"xx"` |
197
+ | `range` | `from <= to`, valid `Date`s | reversed or invalid dates |
198
+
199
+ > **Note:** `new Date(year, ...)` interprets years `0`–`99` as `1900 + year`. The library avoids this by using `setFullYear` internally (`createDate`), so `firstDayOfMonth(50, 9)` correctly returns the year `50` — but you still cannot pass year `0` or `10000`.
200
+
201
+ ## Exports
202
+
203
+ ```ts
204
+ // Types
205
+ import type {
206
+ CalendarOptions,
207
+ CalendarRangeOptions,
208
+ CalendarYearOptions,
209
+ HighlightStyle,
210
+ Locale,
211
+ RenderMonthOptions,
212
+ WeekStart,
213
+ } from "@typescript-calendar-lib/core";
214
+
215
+ // Values
216
+ import {
217
+ LOCALES,
218
+ MAX_YEAR,
219
+ MIN_YEAR,
220
+ assertValidDate,
221
+ buildMonthGrid,
222
+ createDate,
223
+ firstDayOfMonth,
224
+ getMonthName,
225
+ getMonthRange,
226
+ getWeekdayHeaders,
227
+ isDateInRange,
228
+ isSameDay,
229
+ lastDayOfMonth,
230
+ } from "@typescript-calendar-lib/core";
231
+ ```
232
+
233
+ ## License
234
+
235
+ MIT
@@ -0,0 +1,103 @@
1
+ //#region src/types.d.ts
2
+ type Locale = "en" | "ja" | "es" | "de" | "fr" | "ko" | "zh";
3
+ type WeekStart = "sunday" | "monday";
4
+ type HighlightStyle = "bracket" | "reverse";
5
+ interface BaseCalendarOptions {
6
+ locale?: Locale;
7
+ weekStart?: WeekStart;
8
+ highlight?: Date;
9
+ highlightStyle?: HighlightStyle;
10
+ range?: {
11
+ from: Date;
12
+ to: Date;
13
+ };
14
+ color?: boolean;
15
+ }
16
+ interface CalendarOptions extends BaseCalendarOptions {
17
+ year: number;
18
+ month: number;
19
+ }
20
+ interface CalendarYearOptions extends BaseCalendarOptions {
21
+ year: number;
22
+ }
23
+ interface CalendarRangeOptions extends BaseCalendarOptions {
24
+ from: Date;
25
+ to: Date;
26
+ }
27
+ interface RenderMonthOptions extends BaseCalendarOptions {}
28
+ //#endregion
29
+ //#region src/locale.d.ts
30
+ interface LocaleData {
31
+ months: readonly string[];
32
+ weekdays: readonly string[];
33
+ weekdaysShort: readonly string[];
34
+ weekdaysMonday: readonly string[];
35
+ weekdaysMondayShort: readonly string[];
36
+ }
37
+ export declare const LOCALES: Record<Locale, LocaleData>;
38
+ export declare function getWeekdayHeaders(locale: Locale, weekStart: WeekStart): readonly string[];
39
+ export declare function getMonthName(locale: Locale, month: number): string;
40
+ //#endregion
41
+ //#region src/utils.d.ts
42
+ interface CalendarCellState {
43
+ isWeekend: boolean;
44
+ isToday: boolean;
45
+ isHighlight: boolean;
46
+ isInRange: boolean;
47
+ }
48
+ /** 月初日(1日)を返す */
49
+ export declare function firstDayOfMonth(year: number, month: number): Date;
50
+ /** 月末日を返す */
51
+ export declare function lastDayOfMonth(year: number, month: number): Date;
52
+ /**
53
+ * 月の日付グリッドを生成する(6行×7列)
54
+ * 各セルは日数(1-31)または null(空欄)
55
+ */
56
+ export declare function buildMonthGrid(year: number, month: number, weekStart: WeekStart): (number | null)[][];
57
+ /** 日付が範囲内に含まれるか */
58
+ export declare function isDateInRange(date: Date, range?: {
59
+ from: Date;
60
+ to: Date;
61
+ }): boolean;
62
+ /** 日付が今日と一致するか(日付のみ比較) */
63
+ export declare function isSameDay(a: Date, b: Date): boolean;
64
+ /** 日付セルの表示状態を計算する */
65
+ export declare function getCalendarCellState(date: Date, options?: {
66
+ today?: Date;
67
+ highlight?: Date;
68
+ range?: {
69
+ from: Date;
70
+ to: Date;
71
+ };
72
+ }): CalendarCellState;
73
+ /** 日付が範囲の開始年から終了年までの月リストを返す */
74
+ export declare function getMonthRange(from: Date, to: Date): {
75
+ year: number;
76
+ month: number;
77
+ }[];
78
+ /** グレゴリオ暦のうるう年判定(4で割れるが100では割れない、または400で割れる) */
79
+ export declare function isLeapYear(year: number): boolean;
80
+ /** 月の日数(1-31)を返す */
81
+ export declare function daysInMonth(year: number, month: number): number;
82
+ //#endregion
83
+ //#region src/validation.d.ts
84
+ /** サポートする年の最小値(JS Date は 0-99 を 1900+年 と解釈するため 1 から) */
85
+ export declare const MIN_YEAR = 1;
86
+ /** サポートする年の最大値 */
87
+ export declare const MAX_YEAR = 9999;
88
+ /** Date が有効(Invalid Date でない)ことを検証する */
89
+ export declare function assertValidDate(date: Date): void;
90
+ /**
91
+ * ローカルの指定日 00:00:00 を生成する。
92
+ * `new Date(year, ...)` は year 0-99 を 1900+year と解釈するため、
93
+ * setFullYear を使って year 0-99 も正しく扱えるようにする。
94
+ *
95
+ * - year は 1-9999、monthIndex は 0-12、day は 0-31 の整数である必要がある(それ以外は RangeError)
96
+ * - monthIndex 12 は翌年1月(`new Date` と同じロールオーバー。lastDayOfMonth が 12月の月末取得に使う)
97
+ * - day 0 は前月の月末(`new Date` と同じ規則)
98
+ * - day がその月の日数を超える場合は翌月にロールする(`new Date` と同じ挙動)
99
+ */
100
+ export declare function createDate(year: number, monthIndex: number, day: number): Date;
101
+ //#endregion
102
+ export type { CalendarCellState, CalendarOptions, CalendarRangeOptions, CalendarYearOptions, HighlightStyle, Locale, LocaleData, RenderMonthOptions, WeekStart };
103
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,346 @@
1
+ //#region src/validation.ts
2
+ /** サポートする年の最小値(JS Date は 0-99 を 1900+年 と解釈するため 1 から) */
3
+ const MIN_YEAR = 1;
4
+ /** サポートする年の最大値 */
5
+ const MAX_YEAR = 9999;
6
+ /** 値が min-max の整数であることを検証する(不正なら RangeError) */
7
+ function assertIntegerInRange(name, value, min, max) {
8
+ if (!Number.isInteger(value) || value < min || value > max) throw new RangeError(`Invalid ${name}: ${value} (expected an integer between ${min} and ${max})`);
9
+ }
10
+ /** 年が 1-9999 の整数であることを検証する */
11
+ function assertValidYear(year) {
12
+ assertIntegerInRange("year", year, 1, MAX_YEAR);
13
+ }
14
+ /** 月が 1-12 の整数であることを検証する */
15
+ function assertValidMonth(month) {
16
+ assertIntegerInRange("month", month, 1, 12);
17
+ }
18
+ /** 年の組み合わせ(year と month)を検証する */
19
+ function assertValidYearMonth(year, month) {
20
+ assertValidYear(year);
21
+ assertValidMonth(month);
22
+ }
23
+ /** 週の開始曜日を検証する */
24
+ function assertValidWeekStart(weekStart) {
25
+ if (weekStart !== "sunday" && weekStart !== "monday") throw new RangeError(`Invalid weekStart: "${weekStart}" (expected "sunday" or "monday")`);
26
+ }
27
+ /** Date が有効(Invalid Date でない)ことを検証する */
28
+ function assertValidDate(date) {
29
+ if (!(date instanceof Date) || Number.isNaN(date.getTime())) throw new RangeError(`Invalid Date: ${String(date)}`);
30
+ }
31
+ /**
32
+ * ローカルの指定日 00:00:00 を生成する。
33
+ * `new Date(year, ...)` は year 0-99 を 1900+year と解釈するため、
34
+ * setFullYear を使って year 0-99 も正しく扱えるようにする。
35
+ *
36
+ * - year は 1-9999、monthIndex は 0-12、day は 0-31 の整数である必要がある(それ以外は RangeError)
37
+ * - monthIndex 12 は翌年1月(`new Date` と同じロールオーバー。lastDayOfMonth が 12月の月末取得に使う)
38
+ * - day 0 は前月の月末(`new Date` と同じ規則)
39
+ * - day がその月の日数を超える場合は翌月にロールする(`new Date` と同じ挙動)
40
+ */
41
+ function createDate(year, monthIndex, day) {
42
+ assertValidYear(year);
43
+ assertIntegerInRange("monthIndex", monthIndex, 0, 12);
44
+ assertIntegerInRange("day", day, 0, 31);
45
+ const date = /* @__PURE__ */ new Date(0);
46
+ date.setHours(0, 0, 0, 0);
47
+ date.setFullYear(year, monthIndex, day);
48
+ return date;
49
+ }
50
+ //#endregion
51
+ //#region src/locale.ts
52
+ /** 月曜始まりの配列は日曜始まりの配列を前ローテーションして生成する */
53
+ function makeLocale(months, weekdays, weekdaysShort = weekdays) {
54
+ const rotate = (arr) => [...arr.slice(1), arr[0]];
55
+ return {
56
+ months,
57
+ weekdays,
58
+ weekdaysShort,
59
+ weekdaysMonday: rotate(weekdays),
60
+ weekdaysMondayShort: rotate(weekdaysShort)
61
+ };
62
+ }
63
+ const LOCALES = {
64
+ en: makeLocale([
65
+ "January",
66
+ "February",
67
+ "March",
68
+ "April",
69
+ "May",
70
+ "June",
71
+ "July",
72
+ "August",
73
+ "September",
74
+ "October",
75
+ "November",
76
+ "December"
77
+ ], [
78
+ "Sun",
79
+ "Mon",
80
+ "Tue",
81
+ "Wed",
82
+ "Thu",
83
+ "Fri",
84
+ "Sat"
85
+ ], [
86
+ "Su",
87
+ "Mo",
88
+ "Tu",
89
+ "We",
90
+ "Th",
91
+ "Fr",
92
+ "Sa"
93
+ ]),
94
+ ja: makeLocale([
95
+ "1月",
96
+ "2月",
97
+ "3月",
98
+ "4月",
99
+ "5月",
100
+ "6月",
101
+ "7月",
102
+ "8月",
103
+ "9月",
104
+ "10月",
105
+ "11月",
106
+ "12月"
107
+ ], [
108
+ "日",
109
+ "月",
110
+ "火",
111
+ "水",
112
+ "木",
113
+ "金",
114
+ "土"
115
+ ]),
116
+ es: makeLocale([
117
+ "enero",
118
+ "febrero",
119
+ "marzo",
120
+ "abril",
121
+ "mayo",
122
+ "junio",
123
+ "julio",
124
+ "agosto",
125
+ "septiembre",
126
+ "octubre",
127
+ "noviembre",
128
+ "diciembre"
129
+ ], [
130
+ "dom",
131
+ "lun",
132
+ "mar",
133
+ "mié",
134
+ "jue",
135
+ "vie",
136
+ "sáb"
137
+ ], [
138
+ "do",
139
+ "lu",
140
+ "ma",
141
+ "mi",
142
+ "ju",
143
+ "vi",
144
+ "sá"
145
+ ]),
146
+ de: makeLocale([
147
+ "Januar",
148
+ "Februar",
149
+ "März",
150
+ "April",
151
+ "Mai",
152
+ "Juni",
153
+ "Juli",
154
+ "August",
155
+ "September",
156
+ "Oktober",
157
+ "November",
158
+ "Dezember"
159
+ ], [
160
+ "So",
161
+ "Mo",
162
+ "Di",
163
+ "Mi",
164
+ "Do",
165
+ "Fr",
166
+ "Sa"
167
+ ]),
168
+ fr: makeLocale([
169
+ "janvier",
170
+ "février",
171
+ "mars",
172
+ "avril",
173
+ "mai",
174
+ "juin",
175
+ "juillet",
176
+ "août",
177
+ "septembre",
178
+ "octobre",
179
+ "novembre",
180
+ "décembre"
181
+ ], [
182
+ "dim.",
183
+ "lun.",
184
+ "mar.",
185
+ "mer.",
186
+ "jeu.",
187
+ "ven.",
188
+ "sam."
189
+ ], [
190
+ "di",
191
+ "lu",
192
+ "ma",
193
+ "me",
194
+ "je",
195
+ "ve",
196
+ "sa"
197
+ ]),
198
+ ko: makeLocale([
199
+ "1월",
200
+ "2월",
201
+ "3월",
202
+ "4월",
203
+ "5월",
204
+ "6월",
205
+ "7월",
206
+ "8월",
207
+ "9월",
208
+ "10월",
209
+ "11월",
210
+ "12월"
211
+ ], [
212
+ "일",
213
+ "월",
214
+ "화",
215
+ "수",
216
+ "목",
217
+ "금",
218
+ "토"
219
+ ]),
220
+ zh: makeLocale([
221
+ "一月",
222
+ "二月",
223
+ "三月",
224
+ "四月",
225
+ "五月",
226
+ "六月",
227
+ "七月",
228
+ "八月",
229
+ "九月",
230
+ "十月",
231
+ "十一月",
232
+ "十二月"
233
+ ], [
234
+ "日",
235
+ "一",
236
+ "二",
237
+ "三",
238
+ "四",
239
+ "五",
240
+ "六"
241
+ ])
242
+ };
243
+ /** ロケールデータを取得する(未知のロケールは RangeError) */
244
+ function getLocaleData(locale) {
245
+ const data = LOCALES[locale];
246
+ if (data === void 0) throw new RangeError(`Invalid locale: "${locale}"`);
247
+ return data;
248
+ }
249
+ function getWeekdayHeaders(locale, weekStart) {
250
+ assertValidWeekStart(weekStart);
251
+ const data = getLocaleData(locale);
252
+ return weekStart === "monday" ? data.weekdaysMonday : data.weekdays;
253
+ }
254
+ function getMonthName(locale, month) {
255
+ assertValidMonth(month);
256
+ return getLocaleData(locale).months[month - 1];
257
+ }
258
+ //#endregion
259
+ //#region src/utils.ts
260
+ /** 月初日(1日)を返す */
261
+ function firstDayOfMonth(year, month) {
262
+ assertValidYearMonth(year, month);
263
+ return createDate(year, month - 1, 1);
264
+ }
265
+ /** 月末日を返す */
266
+ function lastDayOfMonth(year, month) {
267
+ assertValidYearMonth(year, month);
268
+ return createDate(year, month, 0);
269
+ }
270
+ /**
271
+ * 月の日付グリッドを生成する(6行×7列)
272
+ * 各セルは日数(1-31)または null(空欄)
273
+ */
274
+ function buildMonthGrid(year, month, weekStart) {
275
+ assertValidYearMonth(year, month);
276
+ assertValidWeekStart(weekStart);
277
+ const daysInMonth = lastDayOfMonth(year, month).getDate();
278
+ let skip = firstDayOfMonth(year, month).getDay();
279
+ if (weekStart === "monday") skip = (skip + 6) % 7;
280
+ const grid = [];
281
+ let day = 1;
282
+ for (let week = 0; week < 6; week++) {
283
+ const row = [];
284
+ for (let col = 0; col < 7; col++) if (day > daysInMonth || week === 0 && col < skip) row.push(null);
285
+ else row.push(day++);
286
+ grid.push(row);
287
+ }
288
+ return grid;
289
+ }
290
+ /** 範囲(from/to)が有効であることを検証する */
291
+ function assertValidRange(from, to) {
292
+ assertValidDate(from);
293
+ assertValidDate(to);
294
+ if (from.getTime() > to.getTime()) throw new RangeError("Invalid range: from must not be after to");
295
+ }
296
+ /** 日付が範囲内に含まれるか */
297
+ function isDateInRange(date, range) {
298
+ if (!range) return false;
299
+ assertValidRange(range.from, range.to);
300
+ assertValidDate(date);
301
+ const time = date.getTime();
302
+ return time >= range.from.getTime() && time <= range.to.getTime();
303
+ }
304
+ /** 日付が今日と一致するか(日付のみ比較) */
305
+ function isSameDay(a, b) {
306
+ assertValidDate(a);
307
+ assertValidDate(b);
308
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
309
+ }
310
+ /** 日付セルの表示状態を計算する */
311
+ function getCalendarCellState(date, options = {}) {
312
+ assertValidDate(date);
313
+ return {
314
+ isWeekend: date.getDay() === 0 || date.getDay() === 6,
315
+ isToday: options.today !== void 0 && isSameDay(date, options.today),
316
+ isHighlight: options.highlight !== void 0 && isSameDay(date, options.highlight),
317
+ isInRange: options.range !== void 0 && isDateInRange(date, options.range)
318
+ };
319
+ }
320
+ /** 日付が範囲の開始年から終了年までの月リストを返す */
321
+ function getMonthRange(from, to) {
322
+ assertValidRange(from, to);
323
+ const start = from.getFullYear() * 12 + from.getMonth();
324
+ const end = to.getFullYear() * 12 + to.getMonth();
325
+ return Array.from({ length: end - start + 1 }, (_, i) => {
326
+ const n = start + i;
327
+ return {
328
+ year: Math.floor(n / 12),
329
+ month: n % 12 + 1
330
+ };
331
+ });
332
+ }
333
+ /** グレゴリオ暦のうるう年判定(4で割れるが100では割れない、または400で割れる) */
334
+ function isLeapYear(year) {
335
+ assertValidYear(year);
336
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
337
+ }
338
+ /** 月の日数(1-31)を返す */
339
+ function daysInMonth(year, month) {
340
+ assertValidYearMonth(year, month);
341
+ return lastDayOfMonth(year, month).getDate();
342
+ }
343
+ //#endregion
344
+ export { LOCALES, MAX_YEAR, MIN_YEAR, assertValidDate, buildMonthGrid, createDate, daysInMonth, firstDayOfMonth, getCalendarCellState, getMonthName, getMonthRange, getWeekdayHeaders, isDateInRange, isLeapYear, isSameDay, lastDayOfMonth };
345
+
346
+ //# sourceMappingURL=index.js.map