@kalyx/core 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/README.md +76 -8
- package/dist/index.cjs +151 -20
- package/dist/index.d.cts +167 -75
- package/dist/index.d.ts +348 -9
- package/dist/index.js +538 -9
- package/package.json +20 -1
- package/dist/adapters/date-fns.d.ts +0 -16
- package/dist/adapters/date-fns.d.ts.map +0 -1
- package/dist/adapters/date-fns.js +0 -148
- package/dist/adapters/date-fns.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types.d.ts +0 -106
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -8
- package/dist/types.js.map +0 -1
- package/dist/utils/calendar.d.ts +0 -35
- package/dist/utils/calendar.d.ts.map +0 -1
- package/dist/utils/calendar.js +0 -141
- package/dist/utils/calendar.js.map +0 -1
- package/dist/utils/date.d.ts +0 -22
- package/dist/utils/date.d.ts.map +0 -1
- package/dist/utils/date.js +0 -66
- package/dist/utils/date.js.map +0 -1
- package/dist/utils/locale.d.ts +0 -33
- package/dist/utils/locale.d.ts.map +0 -1
- package/dist/utils/locale.js +0 -70
- package/dist/utils/locale.js.map +0 -1
- package/dist/utils/time.d.ts +0 -58
- package/dist/utils/time.d.ts.map +0 -1
- package/dist/utils/time.js +0 -127
- package/dist/utils/time.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,538 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
// src/adapters/date-fns.ts
|
|
2
|
+
import {
|
|
3
|
+
parseISO,
|
|
4
|
+
addDays as dfAddDays,
|
|
5
|
+
addMonths as dfAddMonths,
|
|
6
|
+
addYears as dfAddYears,
|
|
7
|
+
isBefore as dfIsBefore,
|
|
8
|
+
isAfter as dfIsAfter,
|
|
9
|
+
isValid as dfIsValid
|
|
10
|
+
} from "date-fns";
|
|
11
|
+
function toDate(iso) {
|
|
12
|
+
return parseISO(iso);
|
|
13
|
+
}
|
|
14
|
+
function toISO(date) {
|
|
15
|
+
return date.toISOString();
|
|
16
|
+
}
|
|
17
|
+
function normalize(value) {
|
|
18
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
19
|
+
return `${value}T00:00:00.000Z`;
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
function utcStartOfDay(d) {
|
|
24
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
|
25
|
+
}
|
|
26
|
+
function utcStartOfMonth(d) {
|
|
27
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
|
|
28
|
+
}
|
|
29
|
+
function utcEndOfMonth(d) {
|
|
30
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0, 23, 59, 59, 999));
|
|
31
|
+
}
|
|
32
|
+
function utcStartOfWeek(d, weekStartsOn) {
|
|
33
|
+
const day = d.getUTCDay();
|
|
34
|
+
const diff = (day - weekStartsOn + 7) % 7;
|
|
35
|
+
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - diff));
|
|
36
|
+
}
|
|
37
|
+
function utcEndOfWeek(d, weekStartsOn) {
|
|
38
|
+
const start = utcStartOfWeek(d, weekStartsOn);
|
|
39
|
+
return new Date(Date.UTC(
|
|
40
|
+
start.getUTCFullYear(),
|
|
41
|
+
start.getUTCMonth(),
|
|
42
|
+
start.getUTCDate() + 6,
|
|
43
|
+
23,
|
|
44
|
+
59,
|
|
45
|
+
59,
|
|
46
|
+
999
|
|
47
|
+
));
|
|
48
|
+
}
|
|
49
|
+
var DateFnsAdapter = {
|
|
50
|
+
parse(value) {
|
|
51
|
+
if (!value) return "";
|
|
52
|
+
return normalize(value);
|
|
53
|
+
},
|
|
54
|
+
format(iso, formatStr) {
|
|
55
|
+
const d = toDate(iso);
|
|
56
|
+
const tokens = {
|
|
57
|
+
yyyy: String(d.getUTCFullYear()),
|
|
58
|
+
MM: String(d.getUTCMonth() + 1).padStart(2, "0"),
|
|
59
|
+
dd: String(d.getUTCDate()).padStart(2, "0"),
|
|
60
|
+
HH: String(d.getUTCHours()).padStart(2, "0"),
|
|
61
|
+
mm: String(d.getUTCMinutes()).padStart(2, "0"),
|
|
62
|
+
ss: String(d.getUTCSeconds()).padStart(2, "0"),
|
|
63
|
+
M: String(d.getUTCMonth() + 1),
|
|
64
|
+
d: String(d.getUTCDate())
|
|
65
|
+
};
|
|
66
|
+
let result = formatStr;
|
|
67
|
+
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
68
|
+
result = result.split(token).join(value);
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
},
|
|
72
|
+
addDays(iso, n) {
|
|
73
|
+
return toISO(dfAddDays(toDate(iso), n));
|
|
74
|
+
},
|
|
75
|
+
addMonths(iso, n) {
|
|
76
|
+
return toISO(dfAddMonths(toDate(iso), n));
|
|
77
|
+
},
|
|
78
|
+
addYears(iso, n) {
|
|
79
|
+
return toISO(dfAddYears(toDate(iso), n));
|
|
80
|
+
},
|
|
81
|
+
isBefore(a, b) {
|
|
82
|
+
return dfIsBefore(toDate(a), toDate(b));
|
|
83
|
+
},
|
|
84
|
+
isAfter(a, b) {
|
|
85
|
+
return dfIsAfter(toDate(a), toDate(b));
|
|
86
|
+
},
|
|
87
|
+
isSameDay(a, b) {
|
|
88
|
+
if (!a || !b) return false;
|
|
89
|
+
const da = toDate(a);
|
|
90
|
+
const db = toDate(b);
|
|
91
|
+
return da.getUTCFullYear() === db.getUTCFullYear() && da.getUTCMonth() === db.getUTCMonth() && da.getUTCDate() === db.getUTCDate();
|
|
92
|
+
},
|
|
93
|
+
isSameMonth(a, b) {
|
|
94
|
+
const da = toDate(a);
|
|
95
|
+
const db = toDate(b);
|
|
96
|
+
return da.getUTCFullYear() === db.getUTCFullYear() && da.getUTCMonth() === db.getUTCMonth();
|
|
97
|
+
},
|
|
98
|
+
startOfDay(iso) {
|
|
99
|
+
return toISO(utcStartOfDay(toDate(iso)));
|
|
100
|
+
},
|
|
101
|
+
startOfMonth(iso) {
|
|
102
|
+
return toISO(utcStartOfMonth(toDate(iso)));
|
|
103
|
+
},
|
|
104
|
+
endOfMonth(iso) {
|
|
105
|
+
return toISO(utcEndOfMonth(toDate(iso)));
|
|
106
|
+
},
|
|
107
|
+
startOfWeek(iso, weekStartsOn = 0) {
|
|
108
|
+
return toISO(utcStartOfWeek(toDate(iso), weekStartsOn));
|
|
109
|
+
},
|
|
110
|
+
endOfWeek(iso, weekStartsOn = 0) {
|
|
111
|
+
return toISO(utcEndOfWeek(toDate(iso), weekStartsOn));
|
|
112
|
+
},
|
|
113
|
+
now() {
|
|
114
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
115
|
+
},
|
|
116
|
+
today() {
|
|
117
|
+
return toISO(utcStartOfDay(/* @__PURE__ */ new Date()));
|
|
118
|
+
},
|
|
119
|
+
isValid(value) {
|
|
120
|
+
if (!value) return false;
|
|
121
|
+
const normalized = normalize(value);
|
|
122
|
+
const date = parseISO(normalized);
|
|
123
|
+
return dfIsValid(date);
|
|
124
|
+
},
|
|
125
|
+
getYear(iso) {
|
|
126
|
+
return toDate(iso).getUTCFullYear();
|
|
127
|
+
},
|
|
128
|
+
getMonth(iso) {
|
|
129
|
+
return toDate(iso).getUTCMonth();
|
|
130
|
+
},
|
|
131
|
+
getDate(iso) {
|
|
132
|
+
return toDate(iso).getUTCDate();
|
|
133
|
+
},
|
|
134
|
+
getDay(iso) {
|
|
135
|
+
return toDate(iso).getUTCDay();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
// src/utils/calendar.ts
|
|
140
|
+
function getCalendarDays(monthISO, adapter, options = {}) {
|
|
141
|
+
const {
|
|
142
|
+
weekStartsOn = 0,
|
|
143
|
+
today,
|
|
144
|
+
selected,
|
|
145
|
+
focusedDate,
|
|
146
|
+
disabled = [],
|
|
147
|
+
range,
|
|
148
|
+
rangeHover
|
|
149
|
+
} = options;
|
|
150
|
+
const todayISO = today ?? adapter.today();
|
|
151
|
+
const monthStart = adapter.startOfMonth(monthISO);
|
|
152
|
+
const gridStart = adapter.startOfWeek(monthStart, weekStartsOn);
|
|
153
|
+
const normalizedRange = normalizeRangeForDisplay(range, rangeHover, adapter);
|
|
154
|
+
const weeks = [];
|
|
155
|
+
let current = gridStart;
|
|
156
|
+
for (let week = 0; week < 6; week++) {
|
|
157
|
+
const days = [];
|
|
158
|
+
for (let day = 0; day < 7; day++) {
|
|
159
|
+
const isCurrentMonth = adapter.isSameMonth(current, monthISO);
|
|
160
|
+
const isTodayDate = adapter.isSameDay(current, todayISO);
|
|
161
|
+
const isSelected_ = selected ? adapter.isSameDay(current, selected) : false;
|
|
162
|
+
const isFocused_ = focusedDate ? adapter.isSameDay(current, focusedDate) : false;
|
|
163
|
+
const isDisabled_ = isDateDisabled(current, disabled, adapter);
|
|
164
|
+
const rangeFlags = computeRangeFlags(current, normalizedRange, adapter);
|
|
165
|
+
days.push({
|
|
166
|
+
isoString: current,
|
|
167
|
+
dayNumber: adapter.getDate(current),
|
|
168
|
+
isCurrentMonth,
|
|
169
|
+
isToday: isTodayDate,
|
|
170
|
+
isSelected: isSelected_,
|
|
171
|
+
isDisabled: isDisabled_,
|
|
172
|
+
isFocused: isFocused_,
|
|
173
|
+
...rangeFlags
|
|
174
|
+
});
|
|
175
|
+
current = adapter.addDays(current, 1);
|
|
176
|
+
}
|
|
177
|
+
weeks.push(days);
|
|
178
|
+
if (!adapter.isSameMonth(current, monthISO) && week >= 3) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return weeks;
|
|
183
|
+
}
|
|
184
|
+
function normalizeRangeForDisplay(range, hover, adapter) {
|
|
185
|
+
if (!range) return { start: null, end: null };
|
|
186
|
+
const { start, end } = range;
|
|
187
|
+
if (start && end) {
|
|
188
|
+
if (adapter.isAfter(start, end)) {
|
|
189
|
+
return { start: end, end: start };
|
|
190
|
+
}
|
|
191
|
+
return { start, end };
|
|
192
|
+
}
|
|
193
|
+
if (start && !end && hover) {
|
|
194
|
+
if (adapter.isAfter(start, hover)) {
|
|
195
|
+
return { start: hover, end: start };
|
|
196
|
+
}
|
|
197
|
+
return { start, end: hover };
|
|
198
|
+
}
|
|
199
|
+
if (start && !end) {
|
|
200
|
+
return { start, end: null };
|
|
201
|
+
}
|
|
202
|
+
return { start: null, end: null };
|
|
203
|
+
}
|
|
204
|
+
function computeRangeFlags(iso, range, adapter) {
|
|
205
|
+
const { start, end } = range;
|
|
206
|
+
if (!start) {
|
|
207
|
+
return { isRangeStart: false, isRangeEnd: false, isInRange: false };
|
|
208
|
+
}
|
|
209
|
+
const isRangeStart = adapter.isSameDay(iso, start);
|
|
210
|
+
if (!end) {
|
|
211
|
+
return { isRangeStart, isRangeEnd: false, isInRange: false };
|
|
212
|
+
}
|
|
213
|
+
const isRangeEnd = adapter.isSameDay(iso, end);
|
|
214
|
+
const isInRange = !isRangeStart && !isRangeEnd && adapter.isAfter(iso, start) && adapter.isBefore(iso, end);
|
|
215
|
+
return { isRangeStart, isRangeEnd, isInRange };
|
|
216
|
+
}
|
|
217
|
+
function isDateDisabled(iso, rules, adapter) {
|
|
218
|
+
for (const rule of rules) {
|
|
219
|
+
if ("date" in rule) {
|
|
220
|
+
if (adapter.isSameDay(iso, rule.date)) return true;
|
|
221
|
+
} else if ("before" in rule) {
|
|
222
|
+
if (adapter.isBefore(iso, rule.before)) return true;
|
|
223
|
+
} else if ("after" in rule) {
|
|
224
|
+
if (adapter.isAfter(iso, rule.after)) return true;
|
|
225
|
+
} else if ("dayOfWeek" in rule) {
|
|
226
|
+
if (rule.dayOfWeek.includes(adapter.getDay(iso))) return true;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
function minDate(a, b, adapter) {
|
|
232
|
+
return adapter.isBefore(a, b) ? a : b;
|
|
233
|
+
}
|
|
234
|
+
function maxDate(a, b, adapter) {
|
|
235
|
+
return adapter.isAfter(a, b) ? a : b;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// src/utils/date.ts
|
|
239
|
+
var ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
240
|
+
var ISO_DATETIME_REGEX = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
|
|
241
|
+
function normalizeISO(value) {
|
|
242
|
+
if (!value) return "";
|
|
243
|
+
if (ISO_DATETIME_REGEX.test(value)) return value;
|
|
244
|
+
if (ISO_DATE_REGEX.test(value)) return `${value}T00:00:00.000Z`;
|
|
245
|
+
return value;
|
|
246
|
+
}
|
|
247
|
+
function parseInputValue(input, adapter) {
|
|
248
|
+
if (!input.trim()) return null;
|
|
249
|
+
const cleaned = input.replace(/\//g, "-").trim();
|
|
250
|
+
if (ISO_DATE_REGEX.test(cleaned)) {
|
|
251
|
+
const normalized = normalizeISO(cleaned);
|
|
252
|
+
if (adapter.isValid(normalized)) {
|
|
253
|
+
return normalized;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (/^\d{8}$/.test(cleaned)) {
|
|
257
|
+
const formatted = `${cleaned.slice(0, 4)}-${cleaned.slice(4, 6)}-${cleaned.slice(6, 8)}`;
|
|
258
|
+
const normalized = normalizeISO(formatted);
|
|
259
|
+
if (adapter.isValid(normalized)) {
|
|
260
|
+
return normalized;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/utils/time.ts
|
|
267
|
+
function setTime(iso, time) {
|
|
268
|
+
const date = new Date(iso);
|
|
269
|
+
if (time.hours !== void 0) date.setUTCHours(time.hours);
|
|
270
|
+
if (time.minutes !== void 0) date.setUTCMinutes(time.minutes);
|
|
271
|
+
if (time.seconds !== void 0) date.setUTCSeconds(time.seconds);
|
|
272
|
+
return date.toISOString();
|
|
273
|
+
}
|
|
274
|
+
function getTime(iso) {
|
|
275
|
+
const date = new Date(iso);
|
|
276
|
+
return {
|
|
277
|
+
hours: date.getUTCHours(),
|
|
278
|
+
minutes: date.getUTCMinutes(),
|
|
279
|
+
seconds: date.getUTCSeconds()
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function parseTimeString(input) {
|
|
283
|
+
if (!input) return null;
|
|
284
|
+
const match = input.trim().match(/^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/);
|
|
285
|
+
if (!match) return null;
|
|
286
|
+
const hours = parseInt(match[1], 10);
|
|
287
|
+
const minutes = parseInt(match[2], 10);
|
|
288
|
+
const seconds = match[3] ? parseInt(match[3], 10) : 0;
|
|
289
|
+
if (hours < 0 || hours > 23) return null;
|
|
290
|
+
if (minutes < 0 || minutes > 59) return null;
|
|
291
|
+
if (seconds < 0 || seconds > 59) return null;
|
|
292
|
+
return { hours, minutes, seconds };
|
|
293
|
+
}
|
|
294
|
+
function formatTimeString(time, withSeconds = false) {
|
|
295
|
+
const hh = String(time.hours).padStart(2, "0");
|
|
296
|
+
const mm = String(time.minutes).padStart(2, "0");
|
|
297
|
+
if (withSeconds) {
|
|
298
|
+
const ss = String(time.seconds).padStart(2, "0");
|
|
299
|
+
return `${hh}:${mm}:${ss}`;
|
|
300
|
+
}
|
|
301
|
+
return `${hh}:${mm}`;
|
|
302
|
+
}
|
|
303
|
+
function to12Hour(hours24) {
|
|
304
|
+
const period = hours24 >= 12 ? "PM" : "AM";
|
|
305
|
+
let hours12 = hours24 % 12;
|
|
306
|
+
if (hours12 === 0) hours12 = 12;
|
|
307
|
+
return { hours12, period };
|
|
308
|
+
}
|
|
309
|
+
function to24Hour(hours12, period) {
|
|
310
|
+
if (period === "AM") {
|
|
311
|
+
return hours12 === 12 ? 0 : hours12;
|
|
312
|
+
}
|
|
313
|
+
return hours12 === 12 ? 12 : hours12 + 12;
|
|
314
|
+
}
|
|
315
|
+
function generateHours(format = "24h") {
|
|
316
|
+
if (format === "12h") {
|
|
317
|
+
return Array.from({ length: 12 }, (_, i) => i + 1);
|
|
318
|
+
}
|
|
319
|
+
return Array.from({ length: 24 }, (_, i) => i);
|
|
320
|
+
}
|
|
321
|
+
function generateMinutes(step = 1) {
|
|
322
|
+
if (step < 1 || step > 30) {
|
|
323
|
+
throw new Error(`[generateMinutes] step must be between 1 and 30, got ${step}`);
|
|
324
|
+
}
|
|
325
|
+
const result = [];
|
|
326
|
+
for (let i = 0; i < 60; i += step) {
|
|
327
|
+
result.push(i);
|
|
328
|
+
}
|
|
329
|
+
return result;
|
|
330
|
+
}
|
|
331
|
+
function isSameTime(a, b) {
|
|
332
|
+
return a.hours === b.hours && a.minutes === b.minutes && a.seconds === b.seconds;
|
|
333
|
+
}
|
|
334
|
+
function formatTimeFromISO(iso, format) {
|
|
335
|
+
const time = getTime(iso);
|
|
336
|
+
if (format === "h:mm a" || format === "h:mm:ss a") {
|
|
337
|
+
const { hours12, period } = to12Hour(time.hours);
|
|
338
|
+
const mm = String(time.minutes).padStart(2, "0");
|
|
339
|
+
if (format === "h:mm a") {
|
|
340
|
+
return `${hours12}:${mm} ${period}`;
|
|
341
|
+
}
|
|
342
|
+
const ss = String(time.seconds).padStart(2, "0");
|
|
343
|
+
return `${hours12}:${mm}:${ss} ${period}`;
|
|
344
|
+
}
|
|
345
|
+
return formatTimeString(time, format === "HH:mm:ss");
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/utils/locale.ts
|
|
349
|
+
var formatterCache = /* @__PURE__ */ new Map();
|
|
350
|
+
function getCachedFormatter(locale, options) {
|
|
351
|
+
const key = `${locale}:${JSON.stringify(options)}`;
|
|
352
|
+
let fmt = formatterCache.get(key);
|
|
353
|
+
if (!fmt) {
|
|
354
|
+
fmt = new Intl.DateTimeFormat(locale, options);
|
|
355
|
+
formatterCache.set(key, fmt);
|
|
356
|
+
}
|
|
357
|
+
return fmt;
|
|
358
|
+
}
|
|
359
|
+
var REFERENCE_YEAR = 2026;
|
|
360
|
+
function getMonthName(month, locale = "en-US") {
|
|
361
|
+
const date = new Date(Date.UTC(REFERENCE_YEAR, month, 1));
|
|
362
|
+
return getCachedFormatter(locale, { month: "long", timeZone: "UTC" }).format(date);
|
|
363
|
+
}
|
|
364
|
+
function formatMonthYear(year, month, locale = "en-US") {
|
|
365
|
+
const date = new Date(Date.UTC(year, month, 1));
|
|
366
|
+
return getCachedFormatter(locale, {
|
|
367
|
+
year: "numeric",
|
|
368
|
+
month: "long",
|
|
369
|
+
timeZone: "UTC"
|
|
370
|
+
}).format(date);
|
|
371
|
+
}
|
|
372
|
+
function getWeekdayNames(locale = "en-US", weekStartsOn = 0) {
|
|
373
|
+
const shortFormatter = getCachedFormatter(locale, { weekday: "short", timeZone: "UTC" });
|
|
374
|
+
const fullFormatter = getCachedFormatter(locale, { weekday: "long", timeZone: "UTC" });
|
|
375
|
+
const days = [];
|
|
376
|
+
for (let i = 0; i < 7; i++) {
|
|
377
|
+
const date = new Date(Date.UTC(REFERENCE_YEAR, 0, 4 + i));
|
|
378
|
+
days.push({
|
|
379
|
+
short: shortFormatter.format(date),
|
|
380
|
+
full: fullFormatter.format(date)
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (weekStartsOn === 1) {
|
|
384
|
+
const sunday = days.shift();
|
|
385
|
+
days.push(sunday);
|
|
386
|
+
}
|
|
387
|
+
return days;
|
|
388
|
+
}
|
|
389
|
+
function formatFullDate(iso, locale = "en-US") {
|
|
390
|
+
const date = new Date(iso);
|
|
391
|
+
return getCachedFormatter(locale, {
|
|
392
|
+
year: "numeric",
|
|
393
|
+
month: "long",
|
|
394
|
+
day: "numeric",
|
|
395
|
+
weekday: "long",
|
|
396
|
+
timeZone: "UTC"
|
|
397
|
+
}).format(date);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/utils/timezone.ts
|
|
401
|
+
import { parseISO as parseISO2 } from "date-fns";
|
|
402
|
+
var formatterCache2 = /* @__PURE__ */ new Map();
|
|
403
|
+
function getCachedPartsFormatter(timeZone) {
|
|
404
|
+
const key = `parts:${timeZone}`;
|
|
405
|
+
let fmt = formatterCache2.get(key);
|
|
406
|
+
if (!fmt) {
|
|
407
|
+
fmt = new Intl.DateTimeFormat("en-US", {
|
|
408
|
+
timeZone,
|
|
409
|
+
year: "numeric",
|
|
410
|
+
month: "2-digit",
|
|
411
|
+
day: "2-digit",
|
|
412
|
+
hour: "2-digit",
|
|
413
|
+
minute: "2-digit",
|
|
414
|
+
second: "2-digit",
|
|
415
|
+
hourCycle: "h23"
|
|
416
|
+
});
|
|
417
|
+
formatterCache2.set(key, fmt);
|
|
418
|
+
}
|
|
419
|
+
return fmt;
|
|
420
|
+
}
|
|
421
|
+
function partsInTimezone(utc, timeZone) {
|
|
422
|
+
const dtf = getCachedPartsFormatter(timeZone);
|
|
423
|
+
const parts = Object.fromEntries(
|
|
424
|
+
dtf.formatToParts(utc).map((p) => [p.type, p.value])
|
|
425
|
+
);
|
|
426
|
+
return {
|
|
427
|
+
year: Number(parts.year),
|
|
428
|
+
month: Number(parts.month),
|
|
429
|
+
day: Number(parts.day),
|
|
430
|
+
// Some locales/engines return '24' instead of '0' at midnight
|
|
431
|
+
hour: parts.hour === "24" ? 0 : Number(parts.hour),
|
|
432
|
+
minute: Number(parts.minute),
|
|
433
|
+
second: Number(parts.second)
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function formatInTimezone(iso, formatStr, timeZone) {
|
|
437
|
+
const p = partsInTimezone(parseISO2(iso), timeZone);
|
|
438
|
+
const tokens = {
|
|
439
|
+
yyyy: String(p.year),
|
|
440
|
+
MM: String(p.month).padStart(2, "0"),
|
|
441
|
+
dd: String(p.day).padStart(2, "0"),
|
|
442
|
+
HH: String(p.hour).padStart(2, "0"),
|
|
443
|
+
mm: String(p.minute).padStart(2, "0"),
|
|
444
|
+
ss: String(p.second).padStart(2, "0")
|
|
445
|
+
};
|
|
446
|
+
let result = formatStr;
|
|
447
|
+
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
448
|
+
result = result.split(token).join(value);
|
|
449
|
+
}
|
|
450
|
+
return result;
|
|
451
|
+
}
|
|
452
|
+
function getTimezoneOffsetMinutes(iso, timeZone) {
|
|
453
|
+
const utc = parseISO2(iso);
|
|
454
|
+
const p = partsInTimezone(utc, timeZone);
|
|
455
|
+
const asUtcEpoch = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
456
|
+
return Math.round((asUtcEpoch - utc.getTime()) / 6e4);
|
|
457
|
+
}
|
|
458
|
+
function startOfDayInTimezone(iso, timeZone) {
|
|
459
|
+
const utc = parseISO2(iso);
|
|
460
|
+
const p = partsInTimezone(utc, timeZone);
|
|
461
|
+
const civilMidnightUtc = Date.UTC(p.year, p.month - 1, p.day, 0, 0, 0);
|
|
462
|
+
const midnightProbe = new Date(civilMidnightUtc).toISOString();
|
|
463
|
+
const offsetMinutes = getTimezoneOffsetMinutes(midnightProbe, timeZone);
|
|
464
|
+
return new Date(civilMidnightUtc - offsetMinutes * 6e4).toISOString();
|
|
465
|
+
}
|
|
466
|
+
function isSameDayInTimezone(a, b, timeZone) {
|
|
467
|
+
const pa = partsInTimezone(parseISO2(a), timeZone);
|
|
468
|
+
const pb = partsInTimezone(parseISO2(b), timeZone);
|
|
469
|
+
return pa.year === pb.year && pa.month === pb.month && pa.day === pb.day;
|
|
470
|
+
}
|
|
471
|
+
function todayInTimezone(timeZone) {
|
|
472
|
+
return startOfDayInTimezone((/* @__PURE__ */ new Date()).toISOString(), timeZone);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// src/utils/labels.ts
|
|
476
|
+
var DEFAULT_DATEPICKER_LABELS = {
|
|
477
|
+
triggerOpen: "Open calendar",
|
|
478
|
+
triggerClose: "Close calendar",
|
|
479
|
+
popoverLabel: "Choose date",
|
|
480
|
+
prevMonth: "Previous month",
|
|
481
|
+
nextMonth: "Next month",
|
|
482
|
+
prevYear: "Previous year",
|
|
483
|
+
nextYear: "Next year",
|
|
484
|
+
prevDecade: "Previous decade",
|
|
485
|
+
nextDecade: "Next decade"
|
|
486
|
+
};
|
|
487
|
+
var DEFAULT_RANGEPICKER_LABELS = {
|
|
488
|
+
...DEFAULT_DATEPICKER_LABELS,
|
|
489
|
+
popoverLabel: "Choose date range",
|
|
490
|
+
startInput: "Start date",
|
|
491
|
+
endInput: "End date",
|
|
492
|
+
presetsGroup: "Date range presets"
|
|
493
|
+
};
|
|
494
|
+
var DEFAULT_TIMEPICKER_LABELS = {
|
|
495
|
+
timeInput: "Time",
|
|
496
|
+
hourList: "Hour",
|
|
497
|
+
minuteList: "Minute",
|
|
498
|
+
amPmToggle: "AM/PM",
|
|
499
|
+
hourOption: (hour) => `${hour} hours`,
|
|
500
|
+
minuteOption: (minute) => `${minute} minutes`
|
|
501
|
+
};
|
|
502
|
+
var DEFAULT_DATETIMEPICKER_LABELS = {
|
|
503
|
+
...DEFAULT_DATEPICKER_LABELS,
|
|
504
|
+
...DEFAULT_TIMEPICKER_LABELS,
|
|
505
|
+
dateTimeInput: "Date and time"
|
|
506
|
+
};
|
|
507
|
+
export {
|
|
508
|
+
DEFAULT_DATEPICKER_LABELS,
|
|
509
|
+
DEFAULT_DATETIMEPICKER_LABELS,
|
|
510
|
+
DEFAULT_RANGEPICKER_LABELS,
|
|
511
|
+
DEFAULT_TIMEPICKER_LABELS,
|
|
512
|
+
DateFnsAdapter,
|
|
513
|
+
formatFullDate,
|
|
514
|
+
formatInTimezone,
|
|
515
|
+
formatMonthYear,
|
|
516
|
+
formatTimeFromISO,
|
|
517
|
+
formatTimeString,
|
|
518
|
+
generateHours,
|
|
519
|
+
generateMinutes,
|
|
520
|
+
getCalendarDays,
|
|
521
|
+
getMonthName,
|
|
522
|
+
getTime,
|
|
523
|
+
getTimezoneOffsetMinutes,
|
|
524
|
+
getWeekdayNames,
|
|
525
|
+
isDateDisabled,
|
|
526
|
+
isSameDayInTimezone,
|
|
527
|
+
isSameTime,
|
|
528
|
+
maxDate,
|
|
529
|
+
minDate,
|
|
530
|
+
normalizeISO,
|
|
531
|
+
parseInputValue,
|
|
532
|
+
parseTimeString,
|
|
533
|
+
setTime,
|
|
534
|
+
startOfDayInTimezone,
|
|
535
|
+
to12Hour,
|
|
536
|
+
to24Hour,
|
|
537
|
+
todayInTimezone
|
|
538
|
+
};
|
package/package.json
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kalyx/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Kalyx core — platform-agnostic date logic",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"author": "jiji-hoon96",
|
|
7
|
+
"homepage": "https://github.com/jiji-hoon96/kalyx#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/jiji-hoon96/kalyx.git",
|
|
11
|
+
"directory": "packages/core"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/jiji-hoon96/kalyx/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"date",
|
|
18
|
+
"datepicker",
|
|
19
|
+
"calendar",
|
|
20
|
+
"timezone",
|
|
21
|
+
"iso8601",
|
|
22
|
+
"utc",
|
|
23
|
+
"date-fns"
|
|
24
|
+
],
|
|
6
25
|
"type": "module",
|
|
7
26
|
"main": "./dist/index.cjs",
|
|
8
27
|
"module": "./dist/index.js",
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { DateAdapter } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* date-fns 기반 DateAdapter 구현체.
|
|
4
|
-
* 모든 연산은 UTC 기준으로 동작하여 timezone 간섭을 방지한다.
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* import { DateFnsAdapter } from '@kalyx/core';
|
|
9
|
-
*
|
|
10
|
-
* DateFnsAdapter.format('2026-01-15T00:00:00.000Z', 'yyyy-MM-dd'); // "2026-01-15"
|
|
11
|
-
* DateFnsAdapter.addDays('2026-01-15T00:00:00.000Z', 7); // "2026-01-22T..."
|
|
12
|
-
* DateFnsAdapter.isSameDay('2026-01-15T00:00:00.000Z', '2026-01-15T23:59:59.000Z'); // true
|
|
13
|
-
* ```
|
|
14
|
-
*/
|
|
15
|
-
export declare const DateFnsAdapter: DateAdapter;
|
|
16
|
-
//# sourceMappingURL=date-fns.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"date-fns.d.ts","sourceRoot":"","sources":["../../src/adapters/date-fns.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAmD/C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,EAAE,WAsH5B,CAAC"}
|