@kalyx/core 1.0.0-rc.12 → 1.0.0-rc.13
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 +28 -0
- package/dist/index.cjs +107 -248
- package/dist/index.d.cts +1 -16
- package/dist/index.d.ts +1 -16
- package/dist/index.js +107 -255
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @kalyx/core
|
|
2
2
|
|
|
3
|
+
## 1.0.0-rc.13
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 5b6c37f: Extract `@kalyx/adapter-date-fns` and make `@kalyx/core` neutral
|
|
8
|
+
|
|
9
|
+
Step 1 + 2 of the four-step adapter-extraction plan (see `.claude/skills/adapter-extraction.md`). After this change, `@kalyx/core` no longer depends on `date-fns` or `date-fns-tz`; it ships only the platform-agnostic date logic (`getCalendarDays`, `isDateDisabled`, timezone helpers, labels, the `DateAdapter` contract). The DateFnsAdapter implementation now lives in its own publishable package so dayjs / luxon / Temporal adapters can be added later without forcing every Kalyx user to bundle two date libraries.
|
|
10
|
+
|
|
11
|
+
### What changed
|
|
12
|
+
- **`@kalyx/core`** — `DateFnsAdapter` is no longer exported and `date-fns` / `date-fns-tz` are no longer listed as dependencies. `utils/timezone.ts` was the lone leak and uses native `new Date(string)` now (every caller already routes through `normalizeISO` or `DateAdapter.parse`, so the input subset is fully spec-defined).
|
|
13
|
+
- **`@kalyx/adapter-date-fns`** — new package with the full `DateFnsAdapter` implementation moved verbatim. Same UTC semantics, same timezone-aware paths, same 35 adapter tests.
|
|
14
|
+
- **`@kalyx/react`** — imports `DateFnsAdapter` from `@kalyx/adapter-date-fns` now. The default adapter is still wired up automatically — anyone using `import { DatePicker } from '@kalyx/react'` keeps the previous behaviour with zero changes. The adapter package is a direct dependency so consumers installing just `@kalyx/react` continue to get a working default.
|
|
15
|
+
|
|
16
|
+
### Migration
|
|
17
|
+
|
|
18
|
+
If you imported `DateFnsAdapter` directly from `@kalyx/core`:
|
|
19
|
+
|
|
20
|
+
```diff
|
|
21
|
+
- import { DateFnsAdapter } from '@kalyx/core';
|
|
22
|
+
+ import { DateFnsAdapter } from '@kalyx/adapter-date-fns';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`@kalyx/react` consumers don't need to change anything — the adapter is still re-exported from `@kalyx/react`.
|
|
26
|
+
|
|
27
|
+
### Next (separate PR)
|
|
28
|
+
|
|
29
|
+
The `/headless` entry point (`@kalyx/react/headless`) that lets dayjs/luxon users tree-shake date-fns out is a follow-up. The component Roots still default to the date-fns adapter inline; the entry split requires moving that fallback out of each Root and into the entry boundary.
|
|
30
|
+
|
|
3
31
|
## 1.0.0-rc.12
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,6 @@ __export(index_exports, {
|
|
|
24
24
|
DEFAULT_DATETIMEPICKER_LABELS: () => DEFAULT_DATETIMEPICKER_LABELS,
|
|
25
25
|
DEFAULT_RANGEPICKER_LABELS: () => DEFAULT_RANGEPICKER_LABELS,
|
|
26
26
|
DEFAULT_TIMEPICKER_LABELS: () => DEFAULT_TIMEPICKER_LABELS,
|
|
27
|
-
DateFnsAdapter: () => DateFnsAdapter,
|
|
28
27
|
civilMidnightFromUtcDay: () => civilMidnightFromUtcDay,
|
|
29
28
|
formatFullDate: () => formatFullDate,
|
|
30
29
|
formatInTimezone: () => formatInTimezone,
|
|
@@ -57,249 +56,6 @@ __export(index_exports, {
|
|
|
57
56
|
});
|
|
58
57
|
module.exports = __toCommonJS(index_exports);
|
|
59
58
|
|
|
60
|
-
// src/adapters/date-fns.ts
|
|
61
|
-
var import_date_fns2 = require("date-fns");
|
|
62
|
-
|
|
63
|
-
// src/utils/timezone.ts
|
|
64
|
-
var import_date_fns = require("date-fns");
|
|
65
|
-
var formatterCache = /* @__PURE__ */ new Map();
|
|
66
|
-
function getCachedPartsFormatter(timeZone) {
|
|
67
|
-
const key = `parts:${timeZone}`;
|
|
68
|
-
let fmt = formatterCache.get(key);
|
|
69
|
-
if (!fmt) {
|
|
70
|
-
fmt = new Intl.DateTimeFormat("en-US", {
|
|
71
|
-
timeZone,
|
|
72
|
-
year: "numeric",
|
|
73
|
-
month: "2-digit",
|
|
74
|
-
day: "2-digit",
|
|
75
|
-
hour: "2-digit",
|
|
76
|
-
minute: "2-digit",
|
|
77
|
-
second: "2-digit",
|
|
78
|
-
hourCycle: "h23"
|
|
79
|
-
});
|
|
80
|
-
formatterCache.set(key, fmt);
|
|
81
|
-
}
|
|
82
|
-
return fmt;
|
|
83
|
-
}
|
|
84
|
-
function partsInTimezone(utc, timeZone) {
|
|
85
|
-
const dtf = getCachedPartsFormatter(timeZone);
|
|
86
|
-
const parts = Object.fromEntries(dtf.formatToParts(utc).map((p) => [p.type, p.value]));
|
|
87
|
-
return {
|
|
88
|
-
year: Number(parts.year),
|
|
89
|
-
month: Number(parts.month),
|
|
90
|
-
day: Number(parts.day),
|
|
91
|
-
// Some locales/engines return '24' instead of '0' at midnight
|
|
92
|
-
hour: parts.hour === "24" ? 0 : Number(parts.hour),
|
|
93
|
-
minute: Number(parts.minute),
|
|
94
|
-
second: Number(parts.second)
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function formatInTimezone(iso, formatStr, timeZone) {
|
|
98
|
-
const p = partsInTimezone((0, import_date_fns.parseISO)(iso), timeZone);
|
|
99
|
-
const tokens = {
|
|
100
|
-
yyyy: String(p.year),
|
|
101
|
-
MM: String(p.month).padStart(2, "0"),
|
|
102
|
-
dd: String(p.day).padStart(2, "0"),
|
|
103
|
-
HH: String(p.hour).padStart(2, "0"),
|
|
104
|
-
mm: String(p.minute).padStart(2, "0"),
|
|
105
|
-
ss: String(p.second).padStart(2, "0")
|
|
106
|
-
};
|
|
107
|
-
let result = formatStr;
|
|
108
|
-
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
109
|
-
result = result.split(token).join(value);
|
|
110
|
-
}
|
|
111
|
-
return result;
|
|
112
|
-
}
|
|
113
|
-
function getTimezoneOffsetMinutes(iso, timeZone) {
|
|
114
|
-
const utc = (0, import_date_fns.parseISO)(iso);
|
|
115
|
-
const p = partsInTimezone(utc, timeZone);
|
|
116
|
-
const asUtcEpoch = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
117
|
-
return Math.round((asUtcEpoch - utc.getTime()) / 6e4);
|
|
118
|
-
}
|
|
119
|
-
function startOfDayInTimezone(iso, timeZone) {
|
|
120
|
-
const utc = (0, import_date_fns.parseISO)(iso);
|
|
121
|
-
const p = partsInTimezone(utc, timeZone);
|
|
122
|
-
const civilMidnightUtc = Date.UTC(p.year, p.month - 1, p.day, 0, 0, 0);
|
|
123
|
-
const midnightProbe = new Date(civilMidnightUtc).toISOString();
|
|
124
|
-
const offsetMinutes = getTimezoneOffsetMinutes(midnightProbe, timeZone);
|
|
125
|
-
return new Date(civilMidnightUtc - offsetMinutes * 6e4).toISOString();
|
|
126
|
-
}
|
|
127
|
-
function isSameDayInTimezone(a, b, timeZone) {
|
|
128
|
-
const pa = partsInTimezone((0, import_date_fns.parseISO)(a), timeZone);
|
|
129
|
-
const pb = partsInTimezone((0, import_date_fns.parseISO)(b), timeZone);
|
|
130
|
-
return pa.year === pb.year && pa.month === pb.month && pa.day === pb.day;
|
|
131
|
-
}
|
|
132
|
-
function todayInTimezone(timeZone) {
|
|
133
|
-
return startOfDayInTimezone((/* @__PURE__ */ new Date()).toISOString(), timeZone);
|
|
134
|
-
}
|
|
135
|
-
function civilMidnightFromUtcDay(gridUtcIso, timeZone) {
|
|
136
|
-
const utc = (0, import_date_fns.parseISO)(gridUtcIso);
|
|
137
|
-
const probe = new Date(
|
|
138
|
-
Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), 12, 0, 0)
|
|
139
|
-
).toISOString();
|
|
140
|
-
return startOfDayInTimezone(probe, timeZone);
|
|
141
|
-
}
|
|
142
|
-
function getTimeInTimezone(iso, timeZone) {
|
|
143
|
-
const p = partsInTimezone((0, import_date_fns.parseISO)(iso), timeZone);
|
|
144
|
-
return { hours: p.hour, minutes: p.minute, seconds: p.second };
|
|
145
|
-
}
|
|
146
|
-
function setTimeInTimezone(iso, partial, timeZone) {
|
|
147
|
-
const p = partsInTimezone((0, import_date_fns.parseISO)(iso), timeZone);
|
|
148
|
-
const targetHours = partial.hours ?? p.hour;
|
|
149
|
-
const targetMinutes = partial.minutes ?? p.minute;
|
|
150
|
-
const targetSeconds = partial.seconds ?? p.second;
|
|
151
|
-
const civilEpoch = Date.UTC(
|
|
152
|
-
p.year,
|
|
153
|
-
p.month - 1,
|
|
154
|
-
p.day,
|
|
155
|
-
targetHours,
|
|
156
|
-
targetMinutes,
|
|
157
|
-
targetSeconds
|
|
158
|
-
);
|
|
159
|
-
const probe1 = new Date(civilEpoch).toISOString();
|
|
160
|
-
const offset1 = getTimezoneOffsetMinutes(probe1, timeZone);
|
|
161
|
-
const realEpoch1 = civilEpoch - offset1 * 6e4;
|
|
162
|
-
const probe2 = new Date(realEpoch1).toISOString();
|
|
163
|
-
const offset2 = getTimezoneOffsetMinutes(probe2, timeZone);
|
|
164
|
-
const realEpoch2 = civilEpoch - offset2 * 6e4;
|
|
165
|
-
return new Date(realEpoch2).toISOString();
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// src/adapters/date-fns.ts
|
|
169
|
-
function toDate(iso) {
|
|
170
|
-
return (0, import_date_fns2.parseISO)(iso);
|
|
171
|
-
}
|
|
172
|
-
function toISO(date) {
|
|
173
|
-
return date.toISOString();
|
|
174
|
-
}
|
|
175
|
-
function normalize(value) {
|
|
176
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
177
|
-
return `${value}T00:00:00.000Z`;
|
|
178
|
-
}
|
|
179
|
-
return value;
|
|
180
|
-
}
|
|
181
|
-
function utcStartOfDay(d) {
|
|
182
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
|
183
|
-
}
|
|
184
|
-
function utcStartOfMonth(d) {
|
|
185
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
|
|
186
|
-
}
|
|
187
|
-
function utcEndOfMonth(d) {
|
|
188
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0, 23, 59, 59, 999));
|
|
189
|
-
}
|
|
190
|
-
function utcStartOfWeek(d, weekStartsOn) {
|
|
191
|
-
const day = d.getUTCDay();
|
|
192
|
-
const diff = (day - weekStartsOn + 7) % 7;
|
|
193
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - diff));
|
|
194
|
-
}
|
|
195
|
-
function utcEndOfWeek(d, weekStartsOn) {
|
|
196
|
-
const start = utcStartOfWeek(d, weekStartsOn);
|
|
197
|
-
return new Date(
|
|
198
|
-
Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate() + 6, 23, 59, 59, 999)
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
var DateFnsAdapter = {
|
|
202
|
-
parse(value) {
|
|
203
|
-
if (!value) return "";
|
|
204
|
-
return normalize(value);
|
|
205
|
-
},
|
|
206
|
-
format(iso, formatStr, timezone) {
|
|
207
|
-
if (timezone) {
|
|
208
|
-
return formatInTimezone(iso, formatStr, timezone);
|
|
209
|
-
}
|
|
210
|
-
const d = toDate(iso);
|
|
211
|
-
const tokens = {
|
|
212
|
-
yyyy: String(d.getUTCFullYear()),
|
|
213
|
-
MM: String(d.getUTCMonth() + 1).padStart(2, "0"),
|
|
214
|
-
dd: String(d.getUTCDate()).padStart(2, "0"),
|
|
215
|
-
HH: String(d.getUTCHours()).padStart(2, "0"),
|
|
216
|
-
mm: String(d.getUTCMinutes()).padStart(2, "0"),
|
|
217
|
-
ss: String(d.getUTCSeconds()).padStart(2, "0"),
|
|
218
|
-
M: String(d.getUTCMonth() + 1),
|
|
219
|
-
d: String(d.getUTCDate())
|
|
220
|
-
};
|
|
221
|
-
let result = formatStr;
|
|
222
|
-
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
223
|
-
result = result.split(token).join(value);
|
|
224
|
-
}
|
|
225
|
-
return result;
|
|
226
|
-
},
|
|
227
|
-
addDays(iso, n) {
|
|
228
|
-
return toISO((0, import_date_fns2.addDays)(toDate(iso), n));
|
|
229
|
-
},
|
|
230
|
-
addMonths(iso, n) {
|
|
231
|
-
return toISO((0, import_date_fns2.addMonths)(toDate(iso), n));
|
|
232
|
-
},
|
|
233
|
-
addYears(iso, n) {
|
|
234
|
-
return toISO((0, import_date_fns2.addYears)(toDate(iso), n));
|
|
235
|
-
},
|
|
236
|
-
isBefore(a, b) {
|
|
237
|
-
return (0, import_date_fns2.isBefore)(toDate(a), toDate(b));
|
|
238
|
-
},
|
|
239
|
-
isAfter(a, b) {
|
|
240
|
-
return (0, import_date_fns2.isAfter)(toDate(a), toDate(b));
|
|
241
|
-
},
|
|
242
|
-
isSameDay(a, b, timezone) {
|
|
243
|
-
if (!a || !b) return false;
|
|
244
|
-
if (timezone) {
|
|
245
|
-
return isSameDayInTimezone(a, b, timezone);
|
|
246
|
-
}
|
|
247
|
-
const da = toDate(a);
|
|
248
|
-
const db = toDate(b);
|
|
249
|
-
return da.getUTCFullYear() === db.getUTCFullYear() && da.getUTCMonth() === db.getUTCMonth() && da.getUTCDate() === db.getUTCDate();
|
|
250
|
-
},
|
|
251
|
-
isSameMonth(a, b) {
|
|
252
|
-
const da = toDate(a);
|
|
253
|
-
const db = toDate(b);
|
|
254
|
-
return da.getUTCFullYear() === db.getUTCFullYear() && da.getUTCMonth() === db.getUTCMonth();
|
|
255
|
-
},
|
|
256
|
-
startOfDay(iso, timezone) {
|
|
257
|
-
if (timezone) {
|
|
258
|
-
return startOfDayInTimezone(iso, timezone);
|
|
259
|
-
}
|
|
260
|
-
return toISO(utcStartOfDay(toDate(iso)));
|
|
261
|
-
},
|
|
262
|
-
startOfMonth(iso) {
|
|
263
|
-
return toISO(utcStartOfMonth(toDate(iso)));
|
|
264
|
-
},
|
|
265
|
-
endOfMonth(iso) {
|
|
266
|
-
return toISO(utcEndOfMonth(toDate(iso)));
|
|
267
|
-
},
|
|
268
|
-
startOfWeek(iso, weekStartsOn = 0) {
|
|
269
|
-
return toISO(utcStartOfWeek(toDate(iso), weekStartsOn));
|
|
270
|
-
},
|
|
271
|
-
endOfWeek(iso, weekStartsOn = 0) {
|
|
272
|
-
return toISO(utcEndOfWeek(toDate(iso), weekStartsOn));
|
|
273
|
-
},
|
|
274
|
-
now() {
|
|
275
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
276
|
-
},
|
|
277
|
-
today(timezone) {
|
|
278
|
-
if (timezone) {
|
|
279
|
-
return todayInTimezone(timezone);
|
|
280
|
-
}
|
|
281
|
-
return toISO(utcStartOfDay(/* @__PURE__ */ new Date()));
|
|
282
|
-
},
|
|
283
|
-
isValid(value) {
|
|
284
|
-
if (!value) return false;
|
|
285
|
-
const normalized = normalize(value);
|
|
286
|
-
const date = (0, import_date_fns2.parseISO)(normalized);
|
|
287
|
-
return (0, import_date_fns2.isValid)(date);
|
|
288
|
-
},
|
|
289
|
-
getYear(iso) {
|
|
290
|
-
return toDate(iso).getUTCFullYear();
|
|
291
|
-
},
|
|
292
|
-
getMonth(iso) {
|
|
293
|
-
return toDate(iso).getUTCMonth();
|
|
294
|
-
},
|
|
295
|
-
getDate(iso) {
|
|
296
|
-
return toDate(iso).getUTCDate();
|
|
297
|
-
},
|
|
298
|
-
getDay(iso) {
|
|
299
|
-
return toDate(iso).getUTCDay();
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
|
|
303
59
|
// src/utils/calendar.ts
|
|
304
60
|
function getCalendarDays(monthISO, adapter, options = {}) {
|
|
305
61
|
const {
|
|
@@ -528,13 +284,13 @@ function formatTimeFromISO(iso, format) {
|
|
|
528
284
|
}
|
|
529
285
|
|
|
530
286
|
// src/utils/locale.ts
|
|
531
|
-
var
|
|
287
|
+
var formatterCache = /* @__PURE__ */ new Map();
|
|
532
288
|
function getCachedFormatter(locale, options) {
|
|
533
289
|
const key = `${locale}:${JSON.stringify(options)}`;
|
|
534
|
-
let fmt =
|
|
290
|
+
let fmt = formatterCache.get(key);
|
|
535
291
|
if (!fmt) {
|
|
536
292
|
fmt = new Intl.DateTimeFormat(locale, options);
|
|
537
|
-
|
|
293
|
+
formatterCache.set(key, fmt);
|
|
538
294
|
}
|
|
539
295
|
return fmt;
|
|
540
296
|
}
|
|
@@ -579,6 +335,110 @@ function formatFullDate(iso, locale = "en-US") {
|
|
|
579
335
|
}).format(date);
|
|
580
336
|
}
|
|
581
337
|
|
|
338
|
+
// src/utils/timezone.ts
|
|
339
|
+
var formatterCache2 = /* @__PURE__ */ new Map();
|
|
340
|
+
function getCachedPartsFormatter(timeZone) {
|
|
341
|
+
const key = `parts:${timeZone}`;
|
|
342
|
+
let fmt = formatterCache2.get(key);
|
|
343
|
+
if (!fmt) {
|
|
344
|
+
fmt = new Intl.DateTimeFormat("en-US", {
|
|
345
|
+
timeZone,
|
|
346
|
+
year: "numeric",
|
|
347
|
+
month: "2-digit",
|
|
348
|
+
day: "2-digit",
|
|
349
|
+
hour: "2-digit",
|
|
350
|
+
minute: "2-digit",
|
|
351
|
+
second: "2-digit",
|
|
352
|
+
hourCycle: "h23"
|
|
353
|
+
});
|
|
354
|
+
formatterCache2.set(key, fmt);
|
|
355
|
+
}
|
|
356
|
+
return fmt;
|
|
357
|
+
}
|
|
358
|
+
function partsInTimezone(utc, timeZone) {
|
|
359
|
+
const dtf = getCachedPartsFormatter(timeZone);
|
|
360
|
+
const parts = Object.fromEntries(dtf.formatToParts(utc).map((p) => [p.type, p.value]));
|
|
361
|
+
return {
|
|
362
|
+
year: Number(parts.year),
|
|
363
|
+
month: Number(parts.month),
|
|
364
|
+
day: Number(parts.day),
|
|
365
|
+
// Some locales/engines return '24' instead of '0' at midnight
|
|
366
|
+
hour: parts.hour === "24" ? 0 : Number(parts.hour),
|
|
367
|
+
minute: Number(parts.minute),
|
|
368
|
+
second: Number(parts.second)
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
function formatInTimezone(iso, formatStr, timeZone) {
|
|
372
|
+
const p = partsInTimezone(new Date(iso), timeZone);
|
|
373
|
+
const tokens = {
|
|
374
|
+
yyyy: String(p.year),
|
|
375
|
+
MM: String(p.month).padStart(2, "0"),
|
|
376
|
+
dd: String(p.day).padStart(2, "0"),
|
|
377
|
+
HH: String(p.hour).padStart(2, "0"),
|
|
378
|
+
mm: String(p.minute).padStart(2, "0"),
|
|
379
|
+
ss: String(p.second).padStart(2, "0")
|
|
380
|
+
};
|
|
381
|
+
let result = formatStr;
|
|
382
|
+
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
383
|
+
result = result.split(token).join(value);
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
function getTimezoneOffsetMinutes(iso, timeZone) {
|
|
388
|
+
const utc = new Date(iso);
|
|
389
|
+
const p = partsInTimezone(utc, timeZone);
|
|
390
|
+
const asUtcEpoch = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
391
|
+
return Math.round((asUtcEpoch - utc.getTime()) / 6e4);
|
|
392
|
+
}
|
|
393
|
+
function startOfDayInTimezone(iso, timeZone) {
|
|
394
|
+
const utc = new Date(iso);
|
|
395
|
+
const p = partsInTimezone(utc, timeZone);
|
|
396
|
+
const civilMidnightUtc = Date.UTC(p.year, p.month - 1, p.day, 0, 0, 0);
|
|
397
|
+
const midnightProbe = new Date(civilMidnightUtc).toISOString();
|
|
398
|
+
const offsetMinutes = getTimezoneOffsetMinutes(midnightProbe, timeZone);
|
|
399
|
+
return new Date(civilMidnightUtc - offsetMinutes * 6e4).toISOString();
|
|
400
|
+
}
|
|
401
|
+
function isSameDayInTimezone(a, b, timeZone) {
|
|
402
|
+
const pa = partsInTimezone(new Date(a), timeZone);
|
|
403
|
+
const pb = partsInTimezone(new Date(b), timeZone);
|
|
404
|
+
return pa.year === pb.year && pa.month === pb.month && pa.day === pb.day;
|
|
405
|
+
}
|
|
406
|
+
function todayInTimezone(timeZone) {
|
|
407
|
+
return startOfDayInTimezone((/* @__PURE__ */ new Date()).toISOString(), timeZone);
|
|
408
|
+
}
|
|
409
|
+
function civilMidnightFromUtcDay(gridUtcIso, timeZone) {
|
|
410
|
+
const utc = new Date(gridUtcIso);
|
|
411
|
+
const probe = new Date(
|
|
412
|
+
Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), 12, 0, 0)
|
|
413
|
+
).toISOString();
|
|
414
|
+
return startOfDayInTimezone(probe, timeZone);
|
|
415
|
+
}
|
|
416
|
+
function getTimeInTimezone(iso, timeZone) {
|
|
417
|
+
const p = partsInTimezone(new Date(iso), timeZone);
|
|
418
|
+
return { hours: p.hour, minutes: p.minute, seconds: p.second };
|
|
419
|
+
}
|
|
420
|
+
function setTimeInTimezone(iso, partial, timeZone) {
|
|
421
|
+
const p = partsInTimezone(new Date(iso), timeZone);
|
|
422
|
+
const targetHours = partial.hours ?? p.hour;
|
|
423
|
+
const targetMinutes = partial.minutes ?? p.minute;
|
|
424
|
+
const targetSeconds = partial.seconds ?? p.second;
|
|
425
|
+
const civilEpoch = Date.UTC(
|
|
426
|
+
p.year,
|
|
427
|
+
p.month - 1,
|
|
428
|
+
p.day,
|
|
429
|
+
targetHours,
|
|
430
|
+
targetMinutes,
|
|
431
|
+
targetSeconds
|
|
432
|
+
);
|
|
433
|
+
const probe1 = new Date(civilEpoch).toISOString();
|
|
434
|
+
const offset1 = getTimezoneOffsetMinutes(probe1, timeZone);
|
|
435
|
+
const realEpoch1 = civilEpoch - offset1 * 6e4;
|
|
436
|
+
const probe2 = new Date(realEpoch1).toISOString();
|
|
437
|
+
const offset2 = getTimezoneOffsetMinutes(probe2, timeZone);
|
|
438
|
+
const realEpoch2 = civilEpoch - offset2 * 6e4;
|
|
439
|
+
return new Date(realEpoch2).toISOString();
|
|
440
|
+
}
|
|
441
|
+
|
|
582
442
|
// src/utils/labels.ts
|
|
583
443
|
var DEFAULT_DATEPICKER_LABELS = {
|
|
584
444
|
triggerOpen: "Open calendar",
|
|
@@ -619,7 +479,6 @@ var DEFAULT_DATETIMEPICKER_LABELS = {
|
|
|
619
479
|
DEFAULT_DATETIMEPICKER_LABELS,
|
|
620
480
|
DEFAULT_RANGEPICKER_LABELS,
|
|
621
481
|
DEFAULT_TIMEPICKER_LABELS,
|
|
622
|
-
DateFnsAdapter,
|
|
623
482
|
civilMidnightFromUtcDay,
|
|
624
483
|
formatFullDate,
|
|
625
484
|
formatInTimezone,
|
package/dist/index.d.cts
CHANGED
|
@@ -119,21 +119,6 @@ interface CalendarOptions {
|
|
|
119
119
|
fixedWeeks?: boolean;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/**
|
|
123
|
-
* DateAdapter implementation backed by date-fns.
|
|
124
|
-
* All operations run in UTC to avoid timezone interference.
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* ```ts
|
|
128
|
-
* import { DateFnsAdapter } from '@kalyx/core';
|
|
129
|
-
*
|
|
130
|
-
* DateFnsAdapter.format('2026-01-15T00:00:00.000Z', 'yyyy-MM-dd'); // "2026-01-15"
|
|
131
|
-
* DateFnsAdapter.addDays('2026-01-15T00:00:00.000Z', 7); // "2026-01-22T..."
|
|
132
|
-
* DateFnsAdapter.isSameDay('2026-01-15T00:00:00.000Z', '2026-01-15T23:59:59.000Z'); // true
|
|
133
|
-
* ```
|
|
134
|
-
*/
|
|
135
|
-
declare const DateFnsAdapter: DateAdapter;
|
|
136
|
-
|
|
137
122
|
/**
|
|
138
123
|
* Builds the calendar grid for the given month.
|
|
139
124
|
* Returns a 2D array (`CalendarGrid`) organized by week.
|
|
@@ -442,4 +427,4 @@ declare const DEFAULT_RANGEPICKER_LABELS: RangePickerLabels;
|
|
|
442
427
|
declare const DEFAULT_TIMEPICKER_LABELS: TimePickerLabels;
|
|
443
428
|
declare const DEFAULT_DATETIMEPICKER_LABELS: DateTimePickerLabels;
|
|
444
429
|
|
|
445
|
-
export { type CalendarDay, type CalendarGrid, type CalendarOptions, type CalendarWeek, DEFAULT_DATEPICKER_LABELS, DEFAULT_DATETIMEPICKER_LABELS, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, type DateAdapter,
|
|
430
|
+
export { type CalendarDay, type CalendarGrid, type CalendarOptions, type CalendarWeek, DEFAULT_DATEPICKER_LABELS, DEFAULT_DATETIMEPICKER_LABELS, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, type DateAdapter, type DatePickerLabels, type DateRange, type DateTimePickerLabels, type DisabledRule, type ISODateString, type RangePickerLabels, type TimePickerLabels, type TimeValue, type WeekStartsOn, type WeekdayInfo, civilMidnightFromUtcDay, formatFullDate, formatInTimezone, formatMonthYear, formatTimeFromISO, formatTimeString, generateHours, generateMinutes, getCalendarDays, getISOWeekNumber, getMonthName, getTime, getTimeInTimezone, getTimezoneOffsetMinutes, getWeekdayNames, isDateDisabled, isSameDayInTimezone, isSameTime, maxDate, minDate, normalizeISO, parseInputValue, parseTimeString, setTime, setTimeInTimezone, startOfDayInTimezone, to12Hour, to24Hour, todayInTimezone };
|
package/dist/index.d.ts
CHANGED
|
@@ -119,21 +119,6 @@ interface CalendarOptions {
|
|
|
119
119
|
fixedWeeks?: boolean;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/**
|
|
123
|
-
* DateAdapter implementation backed by date-fns.
|
|
124
|
-
* All operations run in UTC to avoid timezone interference.
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* ```ts
|
|
128
|
-
* import { DateFnsAdapter } from '@kalyx/core';
|
|
129
|
-
*
|
|
130
|
-
* DateFnsAdapter.format('2026-01-15T00:00:00.000Z', 'yyyy-MM-dd'); // "2026-01-15"
|
|
131
|
-
* DateFnsAdapter.addDays('2026-01-15T00:00:00.000Z', 7); // "2026-01-22T..."
|
|
132
|
-
* DateFnsAdapter.isSameDay('2026-01-15T00:00:00.000Z', '2026-01-15T23:59:59.000Z'); // true
|
|
133
|
-
* ```
|
|
134
|
-
*/
|
|
135
|
-
declare const DateFnsAdapter: DateAdapter;
|
|
136
|
-
|
|
137
122
|
/**
|
|
138
123
|
* Builds the calendar grid for the given month.
|
|
139
124
|
* Returns a 2D array (`CalendarGrid`) organized by week.
|
|
@@ -442,4 +427,4 @@ declare const DEFAULT_RANGEPICKER_LABELS: RangePickerLabels;
|
|
|
442
427
|
declare const DEFAULT_TIMEPICKER_LABELS: TimePickerLabels;
|
|
443
428
|
declare const DEFAULT_DATETIMEPICKER_LABELS: DateTimePickerLabels;
|
|
444
429
|
|
|
445
|
-
export { type CalendarDay, type CalendarGrid, type CalendarOptions, type CalendarWeek, DEFAULT_DATEPICKER_LABELS, DEFAULT_DATETIMEPICKER_LABELS, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, type DateAdapter,
|
|
430
|
+
export { type CalendarDay, type CalendarGrid, type CalendarOptions, type CalendarWeek, DEFAULT_DATEPICKER_LABELS, DEFAULT_DATETIMEPICKER_LABELS, DEFAULT_RANGEPICKER_LABELS, DEFAULT_TIMEPICKER_LABELS, type DateAdapter, type DatePickerLabels, type DateRange, type DateTimePickerLabels, type DisabledRule, type ISODateString, type RangePickerLabels, type TimePickerLabels, type TimeValue, type WeekStartsOn, type WeekdayInfo, civilMidnightFromUtcDay, formatFullDate, formatInTimezone, formatMonthYear, formatTimeFromISO, formatTimeString, generateHours, generateMinutes, getCalendarDays, getISOWeekNumber, getMonthName, getTime, getTimeInTimezone, getTimezoneOffsetMinutes, getWeekdayNames, isDateDisabled, isSameDayInTimezone, isSameTime, maxDate, minDate, normalizeISO, parseInputValue, parseTimeString, setTime, setTimeInTimezone, startOfDayInTimezone, to12Hour, to24Hour, todayInTimezone };
|
package/dist/index.js
CHANGED
|
@@ -1,254 +1,3 @@
|
|
|
1
|
-
// src/adapters/date-fns.ts
|
|
2
|
-
import {
|
|
3
|
-
parseISO as parseISO2,
|
|
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
|
-
|
|
12
|
-
// src/utils/timezone.ts
|
|
13
|
-
import { parseISO } from "date-fns";
|
|
14
|
-
var formatterCache = /* @__PURE__ */ new Map();
|
|
15
|
-
function getCachedPartsFormatter(timeZone) {
|
|
16
|
-
const key = `parts:${timeZone}`;
|
|
17
|
-
let fmt = formatterCache.get(key);
|
|
18
|
-
if (!fmt) {
|
|
19
|
-
fmt = new Intl.DateTimeFormat("en-US", {
|
|
20
|
-
timeZone,
|
|
21
|
-
year: "numeric",
|
|
22
|
-
month: "2-digit",
|
|
23
|
-
day: "2-digit",
|
|
24
|
-
hour: "2-digit",
|
|
25
|
-
minute: "2-digit",
|
|
26
|
-
second: "2-digit",
|
|
27
|
-
hourCycle: "h23"
|
|
28
|
-
});
|
|
29
|
-
formatterCache.set(key, fmt);
|
|
30
|
-
}
|
|
31
|
-
return fmt;
|
|
32
|
-
}
|
|
33
|
-
function partsInTimezone(utc, timeZone) {
|
|
34
|
-
const dtf = getCachedPartsFormatter(timeZone);
|
|
35
|
-
const parts = Object.fromEntries(dtf.formatToParts(utc).map((p) => [p.type, p.value]));
|
|
36
|
-
return {
|
|
37
|
-
year: Number(parts.year),
|
|
38
|
-
month: Number(parts.month),
|
|
39
|
-
day: Number(parts.day),
|
|
40
|
-
// Some locales/engines return '24' instead of '0' at midnight
|
|
41
|
-
hour: parts.hour === "24" ? 0 : Number(parts.hour),
|
|
42
|
-
minute: Number(parts.minute),
|
|
43
|
-
second: Number(parts.second)
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
function formatInTimezone(iso, formatStr, timeZone) {
|
|
47
|
-
const p = partsInTimezone(parseISO(iso), timeZone);
|
|
48
|
-
const tokens = {
|
|
49
|
-
yyyy: String(p.year),
|
|
50
|
-
MM: String(p.month).padStart(2, "0"),
|
|
51
|
-
dd: String(p.day).padStart(2, "0"),
|
|
52
|
-
HH: String(p.hour).padStart(2, "0"),
|
|
53
|
-
mm: String(p.minute).padStart(2, "0"),
|
|
54
|
-
ss: String(p.second).padStart(2, "0")
|
|
55
|
-
};
|
|
56
|
-
let result = formatStr;
|
|
57
|
-
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
58
|
-
result = result.split(token).join(value);
|
|
59
|
-
}
|
|
60
|
-
return result;
|
|
61
|
-
}
|
|
62
|
-
function getTimezoneOffsetMinutes(iso, timeZone) {
|
|
63
|
-
const utc = parseISO(iso);
|
|
64
|
-
const p = partsInTimezone(utc, timeZone);
|
|
65
|
-
const asUtcEpoch = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
66
|
-
return Math.round((asUtcEpoch - utc.getTime()) / 6e4);
|
|
67
|
-
}
|
|
68
|
-
function startOfDayInTimezone(iso, timeZone) {
|
|
69
|
-
const utc = parseISO(iso);
|
|
70
|
-
const p = partsInTimezone(utc, timeZone);
|
|
71
|
-
const civilMidnightUtc = Date.UTC(p.year, p.month - 1, p.day, 0, 0, 0);
|
|
72
|
-
const midnightProbe = new Date(civilMidnightUtc).toISOString();
|
|
73
|
-
const offsetMinutes = getTimezoneOffsetMinutes(midnightProbe, timeZone);
|
|
74
|
-
return new Date(civilMidnightUtc - offsetMinutes * 6e4).toISOString();
|
|
75
|
-
}
|
|
76
|
-
function isSameDayInTimezone(a, b, timeZone) {
|
|
77
|
-
const pa = partsInTimezone(parseISO(a), timeZone);
|
|
78
|
-
const pb = partsInTimezone(parseISO(b), timeZone);
|
|
79
|
-
return pa.year === pb.year && pa.month === pb.month && pa.day === pb.day;
|
|
80
|
-
}
|
|
81
|
-
function todayInTimezone(timeZone) {
|
|
82
|
-
return startOfDayInTimezone((/* @__PURE__ */ new Date()).toISOString(), timeZone);
|
|
83
|
-
}
|
|
84
|
-
function civilMidnightFromUtcDay(gridUtcIso, timeZone) {
|
|
85
|
-
const utc = parseISO(gridUtcIso);
|
|
86
|
-
const probe = new Date(
|
|
87
|
-
Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), 12, 0, 0)
|
|
88
|
-
).toISOString();
|
|
89
|
-
return startOfDayInTimezone(probe, timeZone);
|
|
90
|
-
}
|
|
91
|
-
function getTimeInTimezone(iso, timeZone) {
|
|
92
|
-
const p = partsInTimezone(parseISO(iso), timeZone);
|
|
93
|
-
return { hours: p.hour, minutes: p.minute, seconds: p.second };
|
|
94
|
-
}
|
|
95
|
-
function setTimeInTimezone(iso, partial, timeZone) {
|
|
96
|
-
const p = partsInTimezone(parseISO(iso), timeZone);
|
|
97
|
-
const targetHours = partial.hours ?? p.hour;
|
|
98
|
-
const targetMinutes = partial.minutes ?? p.minute;
|
|
99
|
-
const targetSeconds = partial.seconds ?? p.second;
|
|
100
|
-
const civilEpoch = Date.UTC(
|
|
101
|
-
p.year,
|
|
102
|
-
p.month - 1,
|
|
103
|
-
p.day,
|
|
104
|
-
targetHours,
|
|
105
|
-
targetMinutes,
|
|
106
|
-
targetSeconds
|
|
107
|
-
);
|
|
108
|
-
const probe1 = new Date(civilEpoch).toISOString();
|
|
109
|
-
const offset1 = getTimezoneOffsetMinutes(probe1, timeZone);
|
|
110
|
-
const realEpoch1 = civilEpoch - offset1 * 6e4;
|
|
111
|
-
const probe2 = new Date(realEpoch1).toISOString();
|
|
112
|
-
const offset2 = getTimezoneOffsetMinutes(probe2, timeZone);
|
|
113
|
-
const realEpoch2 = civilEpoch - offset2 * 6e4;
|
|
114
|
-
return new Date(realEpoch2).toISOString();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// src/adapters/date-fns.ts
|
|
118
|
-
function toDate(iso) {
|
|
119
|
-
return parseISO2(iso);
|
|
120
|
-
}
|
|
121
|
-
function toISO(date) {
|
|
122
|
-
return date.toISOString();
|
|
123
|
-
}
|
|
124
|
-
function normalize(value) {
|
|
125
|
-
if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
126
|
-
return `${value}T00:00:00.000Z`;
|
|
127
|
-
}
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
function utcStartOfDay(d) {
|
|
131
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()));
|
|
132
|
-
}
|
|
133
|
-
function utcStartOfMonth(d) {
|
|
134
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1));
|
|
135
|
-
}
|
|
136
|
-
function utcEndOfMonth(d) {
|
|
137
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth() + 1, 0, 23, 59, 59, 999));
|
|
138
|
-
}
|
|
139
|
-
function utcStartOfWeek(d, weekStartsOn) {
|
|
140
|
-
const day = d.getUTCDay();
|
|
141
|
-
const diff = (day - weekStartsOn + 7) % 7;
|
|
142
|
-
return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - diff));
|
|
143
|
-
}
|
|
144
|
-
function utcEndOfWeek(d, weekStartsOn) {
|
|
145
|
-
const start = utcStartOfWeek(d, weekStartsOn);
|
|
146
|
-
return new Date(
|
|
147
|
-
Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate() + 6, 23, 59, 59, 999)
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
var DateFnsAdapter = {
|
|
151
|
-
parse(value) {
|
|
152
|
-
if (!value) return "";
|
|
153
|
-
return normalize(value);
|
|
154
|
-
},
|
|
155
|
-
format(iso, formatStr, timezone) {
|
|
156
|
-
if (timezone) {
|
|
157
|
-
return formatInTimezone(iso, formatStr, timezone);
|
|
158
|
-
}
|
|
159
|
-
const d = toDate(iso);
|
|
160
|
-
const tokens = {
|
|
161
|
-
yyyy: String(d.getUTCFullYear()),
|
|
162
|
-
MM: String(d.getUTCMonth() + 1).padStart(2, "0"),
|
|
163
|
-
dd: String(d.getUTCDate()).padStart(2, "0"),
|
|
164
|
-
HH: String(d.getUTCHours()).padStart(2, "0"),
|
|
165
|
-
mm: String(d.getUTCMinutes()).padStart(2, "0"),
|
|
166
|
-
ss: String(d.getUTCSeconds()).padStart(2, "0"),
|
|
167
|
-
M: String(d.getUTCMonth() + 1),
|
|
168
|
-
d: String(d.getUTCDate())
|
|
169
|
-
};
|
|
170
|
-
let result = formatStr;
|
|
171
|
-
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
172
|
-
result = result.split(token).join(value);
|
|
173
|
-
}
|
|
174
|
-
return result;
|
|
175
|
-
},
|
|
176
|
-
addDays(iso, n) {
|
|
177
|
-
return toISO(dfAddDays(toDate(iso), n));
|
|
178
|
-
},
|
|
179
|
-
addMonths(iso, n) {
|
|
180
|
-
return toISO(dfAddMonths(toDate(iso), n));
|
|
181
|
-
},
|
|
182
|
-
addYears(iso, n) {
|
|
183
|
-
return toISO(dfAddYears(toDate(iso), n));
|
|
184
|
-
},
|
|
185
|
-
isBefore(a, b) {
|
|
186
|
-
return dfIsBefore(toDate(a), toDate(b));
|
|
187
|
-
},
|
|
188
|
-
isAfter(a, b) {
|
|
189
|
-
return dfIsAfter(toDate(a), toDate(b));
|
|
190
|
-
},
|
|
191
|
-
isSameDay(a, b, timezone) {
|
|
192
|
-
if (!a || !b) return false;
|
|
193
|
-
if (timezone) {
|
|
194
|
-
return isSameDayInTimezone(a, b, timezone);
|
|
195
|
-
}
|
|
196
|
-
const da = toDate(a);
|
|
197
|
-
const db = toDate(b);
|
|
198
|
-
return da.getUTCFullYear() === db.getUTCFullYear() && da.getUTCMonth() === db.getUTCMonth() && da.getUTCDate() === db.getUTCDate();
|
|
199
|
-
},
|
|
200
|
-
isSameMonth(a, b) {
|
|
201
|
-
const da = toDate(a);
|
|
202
|
-
const db = toDate(b);
|
|
203
|
-
return da.getUTCFullYear() === db.getUTCFullYear() && da.getUTCMonth() === db.getUTCMonth();
|
|
204
|
-
},
|
|
205
|
-
startOfDay(iso, timezone) {
|
|
206
|
-
if (timezone) {
|
|
207
|
-
return startOfDayInTimezone(iso, timezone);
|
|
208
|
-
}
|
|
209
|
-
return toISO(utcStartOfDay(toDate(iso)));
|
|
210
|
-
},
|
|
211
|
-
startOfMonth(iso) {
|
|
212
|
-
return toISO(utcStartOfMonth(toDate(iso)));
|
|
213
|
-
},
|
|
214
|
-
endOfMonth(iso) {
|
|
215
|
-
return toISO(utcEndOfMonth(toDate(iso)));
|
|
216
|
-
},
|
|
217
|
-
startOfWeek(iso, weekStartsOn = 0) {
|
|
218
|
-
return toISO(utcStartOfWeek(toDate(iso), weekStartsOn));
|
|
219
|
-
},
|
|
220
|
-
endOfWeek(iso, weekStartsOn = 0) {
|
|
221
|
-
return toISO(utcEndOfWeek(toDate(iso), weekStartsOn));
|
|
222
|
-
},
|
|
223
|
-
now() {
|
|
224
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
225
|
-
},
|
|
226
|
-
today(timezone) {
|
|
227
|
-
if (timezone) {
|
|
228
|
-
return todayInTimezone(timezone);
|
|
229
|
-
}
|
|
230
|
-
return toISO(utcStartOfDay(/* @__PURE__ */ new Date()));
|
|
231
|
-
},
|
|
232
|
-
isValid(value) {
|
|
233
|
-
if (!value) return false;
|
|
234
|
-
const normalized = normalize(value);
|
|
235
|
-
const date = parseISO2(normalized);
|
|
236
|
-
return dfIsValid(date);
|
|
237
|
-
},
|
|
238
|
-
getYear(iso) {
|
|
239
|
-
return toDate(iso).getUTCFullYear();
|
|
240
|
-
},
|
|
241
|
-
getMonth(iso) {
|
|
242
|
-
return toDate(iso).getUTCMonth();
|
|
243
|
-
},
|
|
244
|
-
getDate(iso) {
|
|
245
|
-
return toDate(iso).getUTCDate();
|
|
246
|
-
},
|
|
247
|
-
getDay(iso) {
|
|
248
|
-
return toDate(iso).getUTCDay();
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
1
|
// src/utils/calendar.ts
|
|
253
2
|
function getCalendarDays(monthISO, adapter, options = {}) {
|
|
254
3
|
const {
|
|
@@ -477,13 +226,13 @@ function formatTimeFromISO(iso, format) {
|
|
|
477
226
|
}
|
|
478
227
|
|
|
479
228
|
// src/utils/locale.ts
|
|
480
|
-
var
|
|
229
|
+
var formatterCache = /* @__PURE__ */ new Map();
|
|
481
230
|
function getCachedFormatter(locale, options) {
|
|
482
231
|
const key = `${locale}:${JSON.stringify(options)}`;
|
|
483
|
-
let fmt =
|
|
232
|
+
let fmt = formatterCache.get(key);
|
|
484
233
|
if (!fmt) {
|
|
485
234
|
fmt = new Intl.DateTimeFormat(locale, options);
|
|
486
|
-
|
|
235
|
+
formatterCache.set(key, fmt);
|
|
487
236
|
}
|
|
488
237
|
return fmt;
|
|
489
238
|
}
|
|
@@ -528,6 +277,110 @@ function formatFullDate(iso, locale = "en-US") {
|
|
|
528
277
|
}).format(date);
|
|
529
278
|
}
|
|
530
279
|
|
|
280
|
+
// src/utils/timezone.ts
|
|
281
|
+
var formatterCache2 = /* @__PURE__ */ new Map();
|
|
282
|
+
function getCachedPartsFormatter(timeZone) {
|
|
283
|
+
const key = `parts:${timeZone}`;
|
|
284
|
+
let fmt = formatterCache2.get(key);
|
|
285
|
+
if (!fmt) {
|
|
286
|
+
fmt = new Intl.DateTimeFormat("en-US", {
|
|
287
|
+
timeZone,
|
|
288
|
+
year: "numeric",
|
|
289
|
+
month: "2-digit",
|
|
290
|
+
day: "2-digit",
|
|
291
|
+
hour: "2-digit",
|
|
292
|
+
minute: "2-digit",
|
|
293
|
+
second: "2-digit",
|
|
294
|
+
hourCycle: "h23"
|
|
295
|
+
});
|
|
296
|
+
formatterCache2.set(key, fmt);
|
|
297
|
+
}
|
|
298
|
+
return fmt;
|
|
299
|
+
}
|
|
300
|
+
function partsInTimezone(utc, timeZone) {
|
|
301
|
+
const dtf = getCachedPartsFormatter(timeZone);
|
|
302
|
+
const parts = Object.fromEntries(dtf.formatToParts(utc).map((p) => [p.type, p.value]));
|
|
303
|
+
return {
|
|
304
|
+
year: Number(parts.year),
|
|
305
|
+
month: Number(parts.month),
|
|
306
|
+
day: Number(parts.day),
|
|
307
|
+
// Some locales/engines return '24' instead of '0' at midnight
|
|
308
|
+
hour: parts.hour === "24" ? 0 : Number(parts.hour),
|
|
309
|
+
minute: Number(parts.minute),
|
|
310
|
+
second: Number(parts.second)
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
function formatInTimezone(iso, formatStr, timeZone) {
|
|
314
|
+
const p = partsInTimezone(new Date(iso), timeZone);
|
|
315
|
+
const tokens = {
|
|
316
|
+
yyyy: String(p.year),
|
|
317
|
+
MM: String(p.month).padStart(2, "0"),
|
|
318
|
+
dd: String(p.day).padStart(2, "0"),
|
|
319
|
+
HH: String(p.hour).padStart(2, "0"),
|
|
320
|
+
mm: String(p.minute).padStart(2, "0"),
|
|
321
|
+
ss: String(p.second).padStart(2, "0")
|
|
322
|
+
};
|
|
323
|
+
let result = formatStr;
|
|
324
|
+
for (const [token, value] of Object.entries(tokens).sort((a, b) => b[0].length - a[0].length)) {
|
|
325
|
+
result = result.split(token).join(value);
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
function getTimezoneOffsetMinutes(iso, timeZone) {
|
|
330
|
+
const utc = new Date(iso);
|
|
331
|
+
const p = partsInTimezone(utc, timeZone);
|
|
332
|
+
const asUtcEpoch = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
|
|
333
|
+
return Math.round((asUtcEpoch - utc.getTime()) / 6e4);
|
|
334
|
+
}
|
|
335
|
+
function startOfDayInTimezone(iso, timeZone) {
|
|
336
|
+
const utc = new Date(iso);
|
|
337
|
+
const p = partsInTimezone(utc, timeZone);
|
|
338
|
+
const civilMidnightUtc = Date.UTC(p.year, p.month - 1, p.day, 0, 0, 0);
|
|
339
|
+
const midnightProbe = new Date(civilMidnightUtc).toISOString();
|
|
340
|
+
const offsetMinutes = getTimezoneOffsetMinutes(midnightProbe, timeZone);
|
|
341
|
+
return new Date(civilMidnightUtc - offsetMinutes * 6e4).toISOString();
|
|
342
|
+
}
|
|
343
|
+
function isSameDayInTimezone(a, b, timeZone) {
|
|
344
|
+
const pa = partsInTimezone(new Date(a), timeZone);
|
|
345
|
+
const pb = partsInTimezone(new Date(b), timeZone);
|
|
346
|
+
return pa.year === pb.year && pa.month === pb.month && pa.day === pb.day;
|
|
347
|
+
}
|
|
348
|
+
function todayInTimezone(timeZone) {
|
|
349
|
+
return startOfDayInTimezone((/* @__PURE__ */ new Date()).toISOString(), timeZone);
|
|
350
|
+
}
|
|
351
|
+
function civilMidnightFromUtcDay(gridUtcIso, timeZone) {
|
|
352
|
+
const utc = new Date(gridUtcIso);
|
|
353
|
+
const probe = new Date(
|
|
354
|
+
Date.UTC(utc.getUTCFullYear(), utc.getUTCMonth(), utc.getUTCDate(), 12, 0, 0)
|
|
355
|
+
).toISOString();
|
|
356
|
+
return startOfDayInTimezone(probe, timeZone);
|
|
357
|
+
}
|
|
358
|
+
function getTimeInTimezone(iso, timeZone) {
|
|
359
|
+
const p = partsInTimezone(new Date(iso), timeZone);
|
|
360
|
+
return { hours: p.hour, minutes: p.minute, seconds: p.second };
|
|
361
|
+
}
|
|
362
|
+
function setTimeInTimezone(iso, partial, timeZone) {
|
|
363
|
+
const p = partsInTimezone(new Date(iso), timeZone);
|
|
364
|
+
const targetHours = partial.hours ?? p.hour;
|
|
365
|
+
const targetMinutes = partial.minutes ?? p.minute;
|
|
366
|
+
const targetSeconds = partial.seconds ?? p.second;
|
|
367
|
+
const civilEpoch = Date.UTC(
|
|
368
|
+
p.year,
|
|
369
|
+
p.month - 1,
|
|
370
|
+
p.day,
|
|
371
|
+
targetHours,
|
|
372
|
+
targetMinutes,
|
|
373
|
+
targetSeconds
|
|
374
|
+
);
|
|
375
|
+
const probe1 = new Date(civilEpoch).toISOString();
|
|
376
|
+
const offset1 = getTimezoneOffsetMinutes(probe1, timeZone);
|
|
377
|
+
const realEpoch1 = civilEpoch - offset1 * 6e4;
|
|
378
|
+
const probe2 = new Date(realEpoch1).toISOString();
|
|
379
|
+
const offset2 = getTimezoneOffsetMinutes(probe2, timeZone);
|
|
380
|
+
const realEpoch2 = civilEpoch - offset2 * 6e4;
|
|
381
|
+
return new Date(realEpoch2).toISOString();
|
|
382
|
+
}
|
|
383
|
+
|
|
531
384
|
// src/utils/labels.ts
|
|
532
385
|
var DEFAULT_DATEPICKER_LABELS = {
|
|
533
386
|
triggerOpen: "Open calendar",
|
|
@@ -567,7 +420,6 @@ export {
|
|
|
567
420
|
DEFAULT_DATETIMEPICKER_LABELS,
|
|
568
421
|
DEFAULT_RANGEPICKER_LABELS,
|
|
569
422
|
DEFAULT_TIMEPICKER_LABELS,
|
|
570
|
-
DateFnsAdapter,
|
|
571
423
|
civilMidnightFromUtcDay,
|
|
572
424
|
formatFullDate,
|
|
573
425
|
formatInTimezone,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kalyx/core",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.13",
|
|
4
4
|
"description": "Kalyx core — platform-agnostic date logic, IANA timezone helpers, and the DateAdapter contract used by @kalyx/react",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "jiji-hoon96",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dst",
|
|
23
23
|
"iso8601",
|
|
24
24
|
"utc",
|
|
25
|
-
"
|
|
25
|
+
"adapter"
|
|
26
26
|
],
|
|
27
27
|
"type": "module",
|
|
28
28
|
"sideEffects": false,
|
|
@@ -46,9 +46,8 @@
|
|
|
46
46
|
"CHANGELOG.md",
|
|
47
47
|
"LICENSE"
|
|
48
48
|
],
|
|
49
|
-
"
|
|
50
|
-
"date-fns": "
|
|
51
|
-
"date-fns-tz": "^3.0.0"
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@kalyx/adapter-date-fns": "1.0.0-rc.1"
|
|
52
51
|
},
|
|
53
52
|
"engines": {
|
|
54
53
|
"node": ">=20.0.0"
|