@mezzanine-ui/core 1.0.4 → 1.1.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/calendar/calendar.d.ts +18 -3
- package/calendar/calendar.js +18 -23
- package/calendarMethodsDayjs/index.js +54 -12
- package/calendarMethodsMoment/index.js +31 -14
- package/calendarMethodsTemporal/index.d.ts +27 -0
- package/calendarMethodsTemporal/index.js +912 -0
- package/calendarMethodsTemporal/tokens.d.ts +105 -0
- package/calendarMethodsTemporal/tokens.js +454 -0
- package/package.json +8 -2
|
@@ -0,0 +1,912 @@
|
|
|
1
|
+
import range from 'lodash/range';
|
|
2
|
+
import chunk from 'lodash/chunk';
|
|
3
|
+
import { isISOWeekLocale } from '../calendar/calendar.js';
|
|
4
|
+
import { buildParseRegex, applyParseRegex, formatTokens } from './tokens.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* CalendarMethodsTemporal — JS-native Temporal implementation of CalendarMethods.
|
|
8
|
+
*
|
|
9
|
+
* Wire format: ISO 8601 string (matches the moment/dayjs adapters, NOT the
|
|
10
|
+
* `Temporal.PlainDateTime#toString()` form). The internal representation is
|
|
11
|
+
* `Temporal.ZonedDateTime` in the system time zone, mirroring how dayjs/moment
|
|
12
|
+
* default to local time when reading/writing ISO strings.
|
|
13
|
+
*
|
|
14
|
+
* Locale week support: uses `Intl.Locale(locale).weekInfo` (Stage 3 / widely
|
|
15
|
+
* available) to determine the first day of week and minimal days in week 1.
|
|
16
|
+
* Falls back to the static ISO_WEEK_LOCALES set when `weekInfo` is missing
|
|
17
|
+
* from the runtime.
|
|
18
|
+
*
|
|
19
|
+
* Polyfill: this module relies on `globalThis.Temporal`. Apps that need to
|
|
20
|
+
* support Safari or Node SSR must install `@js-temporal/polyfill` and
|
|
21
|
+
* register it on `globalThis` from a Client Component (or equivalent client
|
|
22
|
+
* entry point) BEFORE this module is imported:
|
|
23
|
+
*
|
|
24
|
+
* import { Temporal } from '@js-temporal/polyfill';
|
|
25
|
+
* (globalThis as { Temporal?: unknown }).Temporal = Temporal;
|
|
26
|
+
*
|
|
27
|
+
* If Temporal is not present at the time any method is invoked, the call
|
|
28
|
+
* throws with the same installation guide.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the active Temporal namespace. Throws a helpful installation
|
|
32
|
+
* message if the polyfill (or native API) is unavailable.
|
|
33
|
+
*/
|
|
34
|
+
function getTemporal() {
|
|
35
|
+
const globalRef = globalThis;
|
|
36
|
+
if (!globalRef.Temporal) {
|
|
37
|
+
throw new Error('[@mezzanine-ui/core] CalendarMethodsTemporal requires the JS Temporal API. ' +
|
|
38
|
+
'Install `@js-temporal/polyfill` and register it on globalThis at your app entry, ' +
|
|
39
|
+
'BEFORE any code imports CalendarMethodsTemporal:\n' +
|
|
40
|
+
' import { Temporal } from "@js-temporal/polyfill";\n' +
|
|
41
|
+
' (globalThis as { Temporal?: unknown }).Temporal = Temporal;\n' +
|
|
42
|
+
'Or run on a runtime with native Temporal support ' +
|
|
43
|
+
'(Chrome 144+, Firefox 139+, Node 24+ with --harmony-temporal).');
|
|
44
|
+
}
|
|
45
|
+
return globalRef.Temporal;
|
|
46
|
+
}
|
|
47
|
+
const weekInfoCache = new Map();
|
|
48
|
+
/**
|
|
49
|
+
* Best-effort default for `minimalDays` on runtimes whose
|
|
50
|
+
* `Intl.Locale#weekInfo` exposes `firstDay`/`weekend` but not `minimalDays`
|
|
51
|
+
* (older V8 versions, Safari incomplete impls). The static
|
|
52
|
+
* `ISO_WEEK_LOCALES` set is the same source the other adapters use, so
|
|
53
|
+
* deferring to it here keeps Temporal's ISO detection consistent with
|
|
54
|
+
* dayjs/moment when the runtime is partially equipped.
|
|
55
|
+
*
|
|
56
|
+
* Variant-aware: the static set lists country variants like `de-de` and
|
|
57
|
+
* the language code `de`, but not every variant. For `de-AT` / `fr-CH`
|
|
58
|
+
* etc. we fall back to the language-only lookup so ISO inheritance is
|
|
59
|
+
* preserved when CLDR `minimalDays` is missing from the runtime.
|
|
60
|
+
*/
|
|
61
|
+
function defaultMinimalDays(locale) {
|
|
62
|
+
if (isISOWeekLocale(locale))
|
|
63
|
+
return 4;
|
|
64
|
+
const language = locale.split('-')[0].toLowerCase();
|
|
65
|
+
if (language &&
|
|
66
|
+
language !== locale.toLowerCase() &&
|
|
67
|
+
isISOWeekLocale(language)) {
|
|
68
|
+
return 4;
|
|
69
|
+
}
|
|
70
|
+
return 1;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Read locale week info from `Intl.Locale#weekInfo`. Falls back to the
|
|
74
|
+
* mezzanine ISO_WEEK_LOCALES set when the runtime lacks weekInfo support
|
|
75
|
+
* entirely OR when the returned object is missing `minimalDays`.
|
|
76
|
+
*/
|
|
77
|
+
function getWeekInfo(locale) {
|
|
78
|
+
var _a, _b, _c;
|
|
79
|
+
const cached = weekInfoCache.get(locale);
|
|
80
|
+
if (cached)
|
|
81
|
+
return cached;
|
|
82
|
+
let info;
|
|
83
|
+
try {
|
|
84
|
+
const intlLocale = new Intl.Locale(locale);
|
|
85
|
+
const raw = (_a = intlLocale.weekInfo) !== null && _a !== void 0 ? _a : (_b = intlLocale.getWeekInfo) === null || _b === void 0 ? void 0 : _b.call(intlLocale);
|
|
86
|
+
if (raw && typeof raw.firstDay === 'number') {
|
|
87
|
+
info = {
|
|
88
|
+
firstDay: raw.firstDay,
|
|
89
|
+
// Trust the runtime's firstDay (fresher than the static set and
|
|
90
|
+
// covers all 7 weekdays), but fall back to the static set for
|
|
91
|
+
// minimalDays so ISO locales stay classified as ISO when the
|
|
92
|
+
// runtime omits the field.
|
|
93
|
+
minimalDays: typeof raw.minimalDays === 'number'
|
|
94
|
+
? raw.minimalDays
|
|
95
|
+
: defaultMinimalDays(locale),
|
|
96
|
+
weekend: (_c = raw.weekend) !== null && _c !== void 0 ? _c : [6, 7],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
throw new Error('weekInfo unavailable');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch (_d) {
|
|
104
|
+
info = isISOWeekLocale(locale)
|
|
105
|
+
? { firstDay: 1, minimalDays: 4, weekend: [6, 7] }
|
|
106
|
+
: { firstDay: 7, minimalDays: 1, weekend: [6, 7] };
|
|
107
|
+
}
|
|
108
|
+
weekInfoCache.set(locale, info);
|
|
109
|
+
return info;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Whether a locale follows ISO 8601 week rules (Monday-first AND week 1
|
|
113
|
+
* contains at least 4 days of the new year).
|
|
114
|
+
*
|
|
115
|
+
* Used as the fast-path predicate for picking Temporal's native
|
|
116
|
+
* `weekOfYear` / `yearOfWeek` (which are ISO-defined). For non-ISO Monday-
|
|
117
|
+
* first locales such as `en-AU`, `en-NZ`, `ar-AE`, `zh-CN` (firstDay=1 but
|
|
118
|
+
* minimalDays=1), the locale algorithm in `getLocaleWeekInfo` is required —
|
|
119
|
+
* Temporal's native week math would yield wrong results around year ends.
|
|
120
|
+
*/
|
|
121
|
+
function usesISOWeekRules(locale) {
|
|
122
|
+
const info = getWeekInfo(locale);
|
|
123
|
+
return info.firstDay === 1 && info.minimalDays === 4;
|
|
124
|
+
}
|
|
125
|
+
const systemTimeZone = () => getTemporal().Now.zonedDateTimeISO().timeZoneId;
|
|
126
|
+
/**
|
|
127
|
+
* Parse an ISO 8601 string into a ZonedDateTime in the system timezone.
|
|
128
|
+
* Accepts inputs with `Z` suffix, with offset, or naked PlainDateTime form.
|
|
129
|
+
*/
|
|
130
|
+
function parseISO(value) {
|
|
131
|
+
const Temporal = getTemporal();
|
|
132
|
+
const tz = systemTimeZone();
|
|
133
|
+
try {
|
|
134
|
+
return Temporal.Instant.from(value).toZonedDateTimeISO(tz);
|
|
135
|
+
}
|
|
136
|
+
catch (_a) {
|
|
137
|
+
// Naked datetime / partial form — fall back to PlainDateTime semantics.
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
return Temporal.PlainDateTime.from(value).toZonedDateTime(tz);
|
|
141
|
+
}
|
|
142
|
+
catch (_b) {
|
|
143
|
+
return Temporal.PlainDate.from(value)
|
|
144
|
+
.toPlainDateTime(Temporal.PlainTime.from('00:00:00'))
|
|
145
|
+
.toZonedDateTime(tz);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/** Round-trip a ZonedDateTime back to UTC ISO string (matches dayjs/moment wire format). */
|
|
149
|
+
function toISO(value) {
|
|
150
|
+
return value.toInstant().toString();
|
|
151
|
+
}
|
|
152
|
+
const isoDow = (zdt) => zdt.dayOfWeek;
|
|
153
|
+
/**
|
|
154
|
+
* Move the given date back to the start of the week as defined by the locale.
|
|
155
|
+
* `firstDay` follows ISO numbering (1=Mon..7=Sun).
|
|
156
|
+
*/
|
|
157
|
+
function startOfWeekDate(date, firstDay) {
|
|
158
|
+
const offset = (date.dayOfWeek - firstDay + 7) % 7;
|
|
159
|
+
return date.subtract({ days: offset });
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Compute the locale-aware week number for a given date using the standard
|
|
163
|
+
* CLDR/ICU algorithm: week 1 of year Y is the week containing Jan `minimalDays` of Y.
|
|
164
|
+
*/
|
|
165
|
+
function getLocaleWeekInfo(zdt, locale) {
|
|
166
|
+
const Temporal = getTemporal();
|
|
167
|
+
const { firstDay, minimalDays } = getWeekInfo(locale);
|
|
168
|
+
const date = zdt.toPlainDate();
|
|
169
|
+
const thisWeekStart = startOfWeekDate(date, firstDay);
|
|
170
|
+
let year = date.year;
|
|
171
|
+
const week1StartFor = (y) => startOfWeekDate(Temporal.PlainDate.from({
|
|
172
|
+
year: y,
|
|
173
|
+
month: 1,
|
|
174
|
+
day: minimalDays,
|
|
175
|
+
}), firstDay);
|
|
176
|
+
let week1Start = week1StartFor(year);
|
|
177
|
+
if (Temporal.PlainDate.compare(thisWeekStart, week1Start) < 0) {
|
|
178
|
+
year -= 1;
|
|
179
|
+
week1Start = week1StartFor(year);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const nextYearWeek1 = week1StartFor(year + 1);
|
|
183
|
+
if (Temporal.PlainDate.compare(thisWeekStart, nextYearWeek1) >= 0) {
|
|
184
|
+
year += 1;
|
|
185
|
+
week1Start = nextYearWeek1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const daysDiff = thisWeekStart.since(week1Start, {
|
|
189
|
+
largestUnit: 'days',
|
|
190
|
+
}).days;
|
|
191
|
+
return {
|
|
192
|
+
week: Math.floor(daysDiff / 7) + 1,
|
|
193
|
+
weekYear: year,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** Cached Intl formatters keyed by `${locale}|${kind}`. */
|
|
197
|
+
const formatterCache = new Map();
|
|
198
|
+
function getFormatter(locale, options) {
|
|
199
|
+
const key = `${locale}|${JSON.stringify(options)}`;
|
|
200
|
+
let fmt = formatterCache.get(key);
|
|
201
|
+
if (!fmt) {
|
|
202
|
+
fmt = new Intl.DateTimeFormat(locale, options);
|
|
203
|
+
formatterCache.set(key, fmt);
|
|
204
|
+
}
|
|
205
|
+
return fmt;
|
|
206
|
+
}
|
|
207
|
+
function intlMonthNames(locale, width) {
|
|
208
|
+
// Force Gregorian — locales whose default Intl calendar is non-Gregorian
|
|
209
|
+
// (fa-IR → Persian solar Hijri, others may be Buddhist / Hijri / Japanese)
|
|
210
|
+
// would otherwise emit month names from a different calendar system while
|
|
211
|
+
// our Temporal date math is ISO/Gregorian, producing wrong labels.
|
|
212
|
+
const fmt = getFormatter(locale, { month: width, calendar: 'gregory' });
|
|
213
|
+
return range(0, 12).map((m) => {
|
|
214
|
+
const date = new Date(Date.UTC(2024, m, 15));
|
|
215
|
+
return fmt.format(date);
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
function intlWeekdayNames(locale, width) {
|
|
219
|
+
// 2024-01-07 is Sunday; subsequent days run through Saturday.
|
|
220
|
+
const fmt = getFormatter(locale, {
|
|
221
|
+
weekday: width,
|
|
222
|
+
timeZone: 'UTC',
|
|
223
|
+
calendar: 'gregory',
|
|
224
|
+
});
|
|
225
|
+
return range(0, 7).map((d) => {
|
|
226
|
+
const date = new Date(Date.UTC(2024, 0, 7 + d));
|
|
227
|
+
return fmt.format(date);
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
function startOf(zdt, granularity) {
|
|
231
|
+
const lower = granularity.toLowerCase();
|
|
232
|
+
if (lower === 'year' || lower === 'years' || lower === 'y') {
|
|
233
|
+
return zdt.with({
|
|
234
|
+
month: 1,
|
|
235
|
+
day: 1,
|
|
236
|
+
hour: 0,
|
|
237
|
+
minute: 0,
|
|
238
|
+
second: 0,
|
|
239
|
+
millisecond: 0,
|
|
240
|
+
microsecond: 0,
|
|
241
|
+
nanosecond: 0,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (lower === 'month' || lower === 'months') {
|
|
245
|
+
return zdt.with({
|
|
246
|
+
day: 1,
|
|
247
|
+
hour: 0,
|
|
248
|
+
minute: 0,
|
|
249
|
+
second: 0,
|
|
250
|
+
millisecond: 0,
|
|
251
|
+
microsecond: 0,
|
|
252
|
+
nanosecond: 0,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (lower === 'quarter' || lower === 'quarters' || lower === 'q') {
|
|
256
|
+
const quarterStartMonth = Math.floor((zdt.month - 1) / 3) * 3 + 1;
|
|
257
|
+
return zdt.with({
|
|
258
|
+
month: quarterStartMonth,
|
|
259
|
+
day: 1,
|
|
260
|
+
hour: 0,
|
|
261
|
+
minute: 0,
|
|
262
|
+
second: 0,
|
|
263
|
+
millisecond: 0,
|
|
264
|
+
microsecond: 0,
|
|
265
|
+
nanosecond: 0,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (lower === 'week' ||
|
|
269
|
+
lower === 'weeks' ||
|
|
270
|
+
lower === 'isoweek' ||
|
|
271
|
+
lower === 'isoweeks') {
|
|
272
|
+
// Match dayjs/moment defaults when no locale is in scope:
|
|
273
|
+
// `isoWeek` → always Monday-first (firstDay = 1)
|
|
274
|
+
// `week` → Sunday-first by default (firstDay = 7)
|
|
275
|
+
// Locale-aware week bucketing happens through getCurrentWeekFirstDate,
|
|
276
|
+
// which is the path mezzanine components actually use.
|
|
277
|
+
const isoWeekMode = lower.startsWith('isoweek');
|
|
278
|
+
const firstDay = isoWeekMode ? 1 : 7;
|
|
279
|
+
const offset = (zdt.dayOfWeek - firstDay + 7) % 7;
|
|
280
|
+
return zdt.subtract({ days: offset }).with({
|
|
281
|
+
hour: 0,
|
|
282
|
+
minute: 0,
|
|
283
|
+
second: 0,
|
|
284
|
+
millisecond: 0,
|
|
285
|
+
microsecond: 0,
|
|
286
|
+
nanosecond: 0,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
if (lower === 'day' ||
|
|
290
|
+
lower === 'days' ||
|
|
291
|
+
lower === 'd' ||
|
|
292
|
+
lower === 'date') {
|
|
293
|
+
return zdt.with({
|
|
294
|
+
hour: 0,
|
|
295
|
+
minute: 0,
|
|
296
|
+
second: 0,
|
|
297
|
+
millisecond: 0,
|
|
298
|
+
microsecond: 0,
|
|
299
|
+
nanosecond: 0,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (lower === 'hour' || lower === 'hours' || lower === 'h') {
|
|
303
|
+
return zdt.with({
|
|
304
|
+
minute: 0,
|
|
305
|
+
second: 0,
|
|
306
|
+
millisecond: 0,
|
|
307
|
+
microsecond: 0,
|
|
308
|
+
nanosecond: 0,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (lower === 'minute' || lower === 'minutes') {
|
|
312
|
+
return zdt.with({
|
|
313
|
+
second: 0,
|
|
314
|
+
millisecond: 0,
|
|
315
|
+
microsecond: 0,
|
|
316
|
+
nanosecond: 0,
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
if (lower === 'second' || lower === 'seconds' || lower === 's') {
|
|
320
|
+
return zdt.with({
|
|
321
|
+
millisecond: 0,
|
|
322
|
+
microsecond: 0,
|
|
323
|
+
nanosecond: 0,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
// Unknown granularity — return as-is (matches dayjs/moment behavior).
|
|
327
|
+
return zdt;
|
|
328
|
+
}
|
|
329
|
+
function dayOfYear(zdt) {
|
|
330
|
+
return zdt.dayOfYear;
|
|
331
|
+
}
|
|
332
|
+
const CalendarMethodsTemporal = {
|
|
333
|
+
getFirstDayOfWeek: (locale) => {
|
|
334
|
+
const fd = getWeekInfo(locale).firstDay;
|
|
335
|
+
// ISO 1..7 → mezzanine 0..6 (0=Sun, 1=Mon ..)
|
|
336
|
+
return fd === 7 ? 0 : fd;
|
|
337
|
+
},
|
|
338
|
+
// Note: this differs from the global `isISOWeekLocale` (a static set in
|
|
339
|
+
// calendar/calendar.ts shared with the dayjs/moment adapters). The static
|
|
340
|
+
// set can disagree with CLDR `weekInfo` (e.g. it lists ar-SA as ISO, but
|
|
341
|
+
// ar-SA is Sunday-first per CLDR). Inside the Temporal adapter, this
|
|
342
|
+
// method reflects the same source of truth used everywhere else.
|
|
343
|
+
isISOWeekLocale: usesISOWeekRules,
|
|
344
|
+
getNow: () => toISO(getTemporal().Now.zonedDateTimeISO()),
|
|
345
|
+
getSecond: (date) => parseISO(date).second,
|
|
346
|
+
getMinute: (date) => parseISO(date).minute,
|
|
347
|
+
getHour: (date) => parseISO(date).hour,
|
|
348
|
+
getDate: (date) => parseISO(date).day,
|
|
349
|
+
getWeek: (date, locale) => {
|
|
350
|
+
var _a;
|
|
351
|
+
if (usesISOWeekRules(locale)) {
|
|
352
|
+
return (_a = parseISO(date).weekOfYear) !== null && _a !== void 0 ? _a : 1;
|
|
353
|
+
}
|
|
354
|
+
return getLocaleWeekInfo(parseISO(date), locale).week;
|
|
355
|
+
},
|
|
356
|
+
getWeekYear: (date, locale) => {
|
|
357
|
+
var _a;
|
|
358
|
+
if (usesISOWeekRules(locale)) {
|
|
359
|
+
return (_a = parseISO(date).yearOfWeek) !== null && _a !== void 0 ? _a : parseISO(date).year;
|
|
360
|
+
}
|
|
361
|
+
return getLocaleWeekInfo(parseISO(date), locale).weekYear;
|
|
362
|
+
},
|
|
363
|
+
getWeekDay: (date) => {
|
|
364
|
+
const dow = isoDow(parseISO(date));
|
|
365
|
+
return dow === 7 ? 0 : dow;
|
|
366
|
+
},
|
|
367
|
+
getMonth: (date) => parseISO(date).month - 1,
|
|
368
|
+
getYear: (date) => parseISO(date).year,
|
|
369
|
+
getQuarter: (date) => Math.floor((parseISO(date).month - 1) / 3) + 1,
|
|
370
|
+
getHalfYear: (date) => Math.floor((parseISO(date).month - 1) / 6) + 1,
|
|
371
|
+
getWeekDayNames: (locale) => {
|
|
372
|
+
// Intl returns Sunday-indexed names. Rotate left by `firstDay % 7` so the
|
|
373
|
+
// array is ordered starting from the locale's first-day-of-week.
|
|
374
|
+
const sundayIndexed = intlWeekdayNames(locale, 'narrow');
|
|
375
|
+
const offset = getWeekInfo(locale).firstDay % 7;
|
|
376
|
+
return [...sundayIndexed.slice(offset), ...sundayIndexed.slice(0, offset)];
|
|
377
|
+
},
|
|
378
|
+
getMonthShortName: (month, locale) => {
|
|
379
|
+
return intlMonthNames(locale, 'short')[month];
|
|
380
|
+
},
|
|
381
|
+
getMonthShortNames: (locale) => intlMonthNames(locale, 'short'),
|
|
382
|
+
getWeekends: (locale) => {
|
|
383
|
+
// weekInfo.weekend lists the weekend days using ISO numbering (1=Mon..7=Sun).
|
|
384
|
+
// Project that set onto the locale's first-day-rotated week positions.
|
|
385
|
+
const { firstDay, weekend } = getWeekInfo(locale);
|
|
386
|
+
const weekendSet = new Set(weekend);
|
|
387
|
+
const result = [];
|
|
388
|
+
for (let p = 0; p < 7; p += 1) {
|
|
389
|
+
const isoDay = ((p + firstDay - 1) % 7) + 1;
|
|
390
|
+
result.push(weekendSet.has(isoDay));
|
|
391
|
+
}
|
|
392
|
+
return result;
|
|
393
|
+
},
|
|
394
|
+
addHour: (date, diff) => toISO(parseISO(date).add({ hours: diff })),
|
|
395
|
+
addMinute: (date, diff) => toISO(parseISO(date).add({ minutes: diff })),
|
|
396
|
+
addSecond: (date, diff) => toISO(parseISO(date).add({ seconds: diff })),
|
|
397
|
+
addDay: (date, diff) => toISO(parseISO(date).add({ days: diff })),
|
|
398
|
+
addYear: (date, diff) => toISO(parseISO(date).add({ years: diff })),
|
|
399
|
+
addMonth: (date, diff) => toISO(parseISO(date).add({ months: diff })),
|
|
400
|
+
setMillisecond: (date, millisecond) => toISO(parseISO(date).with({ millisecond })),
|
|
401
|
+
setSecond: (date, second) => toISO(parseISO(date).with({ second })),
|
|
402
|
+
setMinute: (date, minute) => toISO(parseISO(date).with({ minute })),
|
|
403
|
+
setHour: (date, hour) => toISO(parseISO(date).with({ hour })),
|
|
404
|
+
// Out-of-range setters: moment/dayjs normalize values like `setMonth(d, -1)`
|
|
405
|
+
// (→ previous year's December) or `setDate(d, 0)` (→ last day of previous
|
|
406
|
+
// month). Temporal's `with()` rejects month=0 and clamps day overflow, so
|
|
407
|
+
// we route through `add()` to preserve cross-boundary semantics.
|
|
408
|
+
setMonth: (date, month) => {
|
|
409
|
+
const current = parseISO(date);
|
|
410
|
+
return toISO(current.add({ months: month - (current.month - 1) }));
|
|
411
|
+
},
|
|
412
|
+
setYear: (date, year) => {
|
|
413
|
+
const current = parseISO(date);
|
|
414
|
+
return toISO(current.add({ years: year - current.year }));
|
|
415
|
+
},
|
|
416
|
+
setDate: (date, target) => {
|
|
417
|
+
const current = parseISO(date);
|
|
418
|
+
return toISO(current.add({ days: target - current.day }));
|
|
419
|
+
},
|
|
420
|
+
startOf: (target, granularity) => toISO(startOf(parseISO(target), String(granularity))),
|
|
421
|
+
getCurrentWeekFirstDate: (value, locale) => {
|
|
422
|
+
const zdt = parseISO(value);
|
|
423
|
+
const { firstDay } = getWeekInfo(locale);
|
|
424
|
+
const offset = (zdt.dayOfWeek - firstDay + 7) % 7;
|
|
425
|
+
return toISO(zdt.subtract({ days: offset }).with({
|
|
426
|
+
hour: 0,
|
|
427
|
+
minute: 0,
|
|
428
|
+
second: 0,
|
|
429
|
+
millisecond: 0,
|
|
430
|
+
microsecond: 0,
|
|
431
|
+
nanosecond: 0,
|
|
432
|
+
}));
|
|
433
|
+
},
|
|
434
|
+
getCurrentMonthFirstDate: (value) => toISO(parseISO(value).with({
|
|
435
|
+
day: 1,
|
|
436
|
+
hour: 0,
|
|
437
|
+
minute: 0,
|
|
438
|
+
second: 0,
|
|
439
|
+
millisecond: 0,
|
|
440
|
+
microsecond: 0,
|
|
441
|
+
nanosecond: 0,
|
|
442
|
+
})),
|
|
443
|
+
getCurrentYearFirstDate: (value) => toISO(parseISO(value).with({
|
|
444
|
+
month: 1,
|
|
445
|
+
day: 1,
|
|
446
|
+
hour: 0,
|
|
447
|
+
minute: 0,
|
|
448
|
+
second: 0,
|
|
449
|
+
millisecond: 0,
|
|
450
|
+
microsecond: 0,
|
|
451
|
+
nanosecond: 0,
|
|
452
|
+
})),
|
|
453
|
+
getCurrentQuarterFirstDate: (value) => {
|
|
454
|
+
const zdt = parseISO(value);
|
|
455
|
+
const quarterStartMonth = Math.floor((zdt.month - 1) / 3) * 3 + 1;
|
|
456
|
+
return toISO(zdt.with({
|
|
457
|
+
month: quarterStartMonth,
|
|
458
|
+
day: 1,
|
|
459
|
+
hour: 0,
|
|
460
|
+
minute: 0,
|
|
461
|
+
second: 0,
|
|
462
|
+
millisecond: 0,
|
|
463
|
+
microsecond: 0,
|
|
464
|
+
nanosecond: 0,
|
|
465
|
+
}));
|
|
466
|
+
},
|
|
467
|
+
getCurrentHalfYearFirstDate: (value) => {
|
|
468
|
+
const zdt = parseISO(value);
|
|
469
|
+
const halfYearStartMonth = Math.floor((zdt.month - 1) / 6) * 6 + 1;
|
|
470
|
+
return toISO(zdt.with({
|
|
471
|
+
month: halfYearStartMonth,
|
|
472
|
+
day: 1,
|
|
473
|
+
hour: 0,
|
|
474
|
+
minute: 0,
|
|
475
|
+
second: 0,
|
|
476
|
+
millisecond: 0,
|
|
477
|
+
microsecond: 0,
|
|
478
|
+
nanosecond: 0,
|
|
479
|
+
}));
|
|
480
|
+
},
|
|
481
|
+
getCalendarGrid: (target, locale) => {
|
|
482
|
+
const Temporal = getTemporal();
|
|
483
|
+
const zdt = parseISO(target);
|
|
484
|
+
const { firstDay } = getWeekInfo(locale);
|
|
485
|
+
const firstOfMonth = zdt.with({ day: 1 });
|
|
486
|
+
const firstDayWeekdayIso = firstOfMonth.dayOfWeek; // 1..7
|
|
487
|
+
const lastDateOfPrevMonth = firstOfMonth.subtract({ days: 1 }).day;
|
|
488
|
+
const lastDateOfCurrentMonth = Temporal.PlainYearMonth.from({
|
|
489
|
+
year: zdt.year,
|
|
490
|
+
month: zdt.month,
|
|
491
|
+
}).daysInMonth;
|
|
492
|
+
// How many cells from the previous month appear before day-1: the offset
|
|
493
|
+
// between the first day of the month and the locale's first-day-of-week.
|
|
494
|
+
const daysFromPrevMonth = (firstDayWeekdayIso - firstDay + 7) % 7;
|
|
495
|
+
const totalDaysInGrid = 42;
|
|
496
|
+
const daysFromNextMonth = totalDaysInGrid - daysFromPrevMonth - lastDateOfCurrentMonth;
|
|
497
|
+
return chunk([
|
|
498
|
+
...range(lastDateOfPrevMonth - daysFromPrevMonth + 1, lastDateOfPrevMonth + 1),
|
|
499
|
+
...range(1, lastDateOfCurrentMonth + 1),
|
|
500
|
+
...range(1, daysFromNextMonth + 1),
|
|
501
|
+
], 7);
|
|
502
|
+
},
|
|
503
|
+
isValid: (date) => {
|
|
504
|
+
// Trigger the polyfill guard up-front: a missing Temporal API is an
|
|
505
|
+
// environmental error that callers must see, not a "this date string
|
|
506
|
+
// is invalid" answer. Only the actual parsing step is wrapped.
|
|
507
|
+
getTemporal();
|
|
508
|
+
try {
|
|
509
|
+
parseISO(date);
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
catch (_a) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
isBefore: (target, comparison) => {
|
|
517
|
+
const Temporal = getTemporal();
|
|
518
|
+
return (Temporal.ZonedDateTime.compare(parseISO(target), parseISO(comparison)) < 0);
|
|
519
|
+
},
|
|
520
|
+
isBetween: (value, target1, target2, granularity) => {
|
|
521
|
+
const Temporal = getTemporal();
|
|
522
|
+
const a = parseISO(target1);
|
|
523
|
+
const b = parseISO(target2);
|
|
524
|
+
const v = parseISO(value);
|
|
525
|
+
// Truncate to the requested granularity so that values within the same
|
|
526
|
+
// bucket compare equal (e.g. `'day'` ignores time-of-day differences).
|
|
527
|
+
// Default granularity matches dayjs/moment default of `'milliseconds'`,
|
|
528
|
+
// which is effectively no truncation.
|
|
529
|
+
const granularityKey = typeof granularity === 'string' && granularity.length > 0
|
|
530
|
+
? granularity
|
|
531
|
+
: 'milliseconds';
|
|
532
|
+
const aT = startOf(a, granularityKey);
|
|
533
|
+
const bT = startOf(b, granularityKey);
|
|
534
|
+
const vT = startOf(v, granularityKey);
|
|
535
|
+
const lo = Temporal.ZonedDateTime.compare(aT, bT) <= 0 ? aT : bT;
|
|
536
|
+
const hi = Temporal.ZonedDateTime.compare(aT, bT) <= 0 ? bT : aT;
|
|
537
|
+
// Match dayjs/moment default inclusivity `'()'` — both bounds exclusive.
|
|
538
|
+
return (Temporal.ZonedDateTime.compare(vT, lo) > 0 &&
|
|
539
|
+
Temporal.ZonedDateTime.compare(vT, hi) < 0);
|
|
540
|
+
},
|
|
541
|
+
isSameDate: (dateOne, dateTwo) => {
|
|
542
|
+
const a = parseISO(dateOne);
|
|
543
|
+
const b = parseISO(dateTwo);
|
|
544
|
+
return a.year === b.year && a.month === b.month && a.day === b.day;
|
|
545
|
+
},
|
|
546
|
+
isSameWeek: (dateOne, dateTwo, locale) => {
|
|
547
|
+
var _a, _b, _c, _d;
|
|
548
|
+
if (usesISOWeekRules(locale)) {
|
|
549
|
+
const a = parseISO(dateOne);
|
|
550
|
+
const b = parseISO(dateTwo);
|
|
551
|
+
return (((_a = a.yearOfWeek) !== null && _a !== void 0 ? _a : a.year) === ((_b = b.yearOfWeek) !== null && _b !== void 0 ? _b : b.year) &&
|
|
552
|
+
((_c = a.weekOfYear) !== null && _c !== void 0 ? _c : 1) === ((_d = b.weekOfYear) !== null && _d !== void 0 ? _d : 1));
|
|
553
|
+
}
|
|
554
|
+
const a = getLocaleWeekInfo(parseISO(dateOne), locale);
|
|
555
|
+
const b = getLocaleWeekInfo(parseISO(dateTwo), locale);
|
|
556
|
+
return a.week === b.week && a.weekYear === b.weekYear;
|
|
557
|
+
},
|
|
558
|
+
isInMonth: (target, month) => parseISO(target).month === month + 1,
|
|
559
|
+
isDateIncluded: (date, targets) => targets.some((target) => CalendarMethodsTemporal.isSameDate(date, target)),
|
|
560
|
+
isWeekIncluded: (firstDateOfWeek, targets, locale) => targets.some((target) => CalendarMethodsTemporal.isSameWeek(firstDateOfWeek, target, locale)),
|
|
561
|
+
isMonthIncluded: (date, targets) => {
|
|
562
|
+
const a = parseISO(date);
|
|
563
|
+
return targets.some((target) => {
|
|
564
|
+
const t = parseISO(target);
|
|
565
|
+
return a.year === t.year && a.month === t.month;
|
|
566
|
+
});
|
|
567
|
+
},
|
|
568
|
+
isYearIncluded: (date, targets) => {
|
|
569
|
+
const y = parseISO(date).year;
|
|
570
|
+
return targets.some((target) => parseISO(target).year === y);
|
|
571
|
+
},
|
|
572
|
+
isQuarterIncluded: (date, targets) => {
|
|
573
|
+
const a = parseISO(date);
|
|
574
|
+
const q = Math.floor((a.month - 1) / 3);
|
|
575
|
+
return targets.some((target) => {
|
|
576
|
+
const t = parseISO(target);
|
|
577
|
+
return t.year === a.year && Math.floor((t.month - 1) / 3) === q;
|
|
578
|
+
});
|
|
579
|
+
},
|
|
580
|
+
isHalfYearIncluded: (date, targets) => {
|
|
581
|
+
const a = parseISO(date);
|
|
582
|
+
const h = Math.floor((a.month - 1) / 6);
|
|
583
|
+
return targets.some((target) => {
|
|
584
|
+
const t = parseISO(target);
|
|
585
|
+
return t.year === a.year && Math.floor((t.month - 1) / 6) === h;
|
|
586
|
+
});
|
|
587
|
+
},
|
|
588
|
+
formatToString: (locale, date, format) => {
|
|
589
|
+
var _a, _b;
|
|
590
|
+
const isoInput = typeof date === 'string' ? date : date.toISOString();
|
|
591
|
+
const zdt = parseISO(isoInput);
|
|
592
|
+
const isoWeek = (_a = zdt.weekOfYear) !== null && _a !== void 0 ? _a : 1;
|
|
593
|
+
const isoWeekYear = (_b = zdt.yearOfWeek) !== null && _b !== void 0 ? _b : zdt.year;
|
|
594
|
+
const localeInfo = usesISOWeekRules(locale)
|
|
595
|
+
? { week: isoWeek, weekYear: isoWeekYear }
|
|
596
|
+
: getLocaleWeekInfo(zdt, locale);
|
|
597
|
+
const monthShorts = intlMonthNames(locale, 'short');
|
|
598
|
+
const monthLongs = intlMonthNames(locale, 'long');
|
|
599
|
+
const weekdayShorts = intlWeekdayNames(locale, 'short');
|
|
600
|
+
const weekdayLongs = intlWeekdayNames(locale, 'long');
|
|
601
|
+
const weekdayNarrows = intlWeekdayNames(locale, 'narrow');
|
|
602
|
+
const sundayIndex = zdt.dayOfWeek === 7 ? 0 : zdt.dayOfWeek;
|
|
603
|
+
const { firstDay } = getWeekInfo(locale);
|
|
604
|
+
const localeWeekDay = (zdt.dayOfWeek - firstDay + 7) % 7;
|
|
605
|
+
return formatTokens(format, {
|
|
606
|
+
year: zdt.year,
|
|
607
|
+
month: zdt.month,
|
|
608
|
+
day: zdt.day,
|
|
609
|
+
hour: zdt.hour,
|
|
610
|
+
minute: zdt.minute,
|
|
611
|
+
second: zdt.second,
|
|
612
|
+
millisecond: zdt.millisecond,
|
|
613
|
+
dayOfWeek: zdt.dayOfWeek,
|
|
614
|
+
localeWeekDay,
|
|
615
|
+
isoWeek,
|
|
616
|
+
isoWeekYear,
|
|
617
|
+
localeWeek: localeInfo.week,
|
|
618
|
+
localeWeekYear: localeInfo.weekYear,
|
|
619
|
+
quarter: Math.floor((zdt.month - 1) / 3) + 1,
|
|
620
|
+
halfYear: zdt.month <= 6 ? 1 : 2,
|
|
621
|
+
monthShort: monthShorts[zdt.month - 1],
|
|
622
|
+
monthLong: monthLongs[zdt.month - 1],
|
|
623
|
+
weekdayShort: weekdayShorts[sundayIndex],
|
|
624
|
+
weekdayLong: weekdayLongs[sundayIndex],
|
|
625
|
+
weekdayNarrow: weekdayNarrows[sundayIndex],
|
|
626
|
+
dayOfYear: dayOfYear(zdt),
|
|
627
|
+
unixMs: Number(zdt.epochMilliseconds),
|
|
628
|
+
});
|
|
629
|
+
},
|
|
630
|
+
formatToISOString: (date) => toISO(parseISO(date)),
|
|
631
|
+
parseFormattedValue: (text, format, locale) => {
|
|
632
|
+
var _a, _b, _c, _d, _e, _f;
|
|
633
|
+
const Temporal = getTemporal();
|
|
634
|
+
const localeNames = {
|
|
635
|
+
monthShort: intlMonthNames(locale, 'short'),
|
|
636
|
+
monthLong: intlMonthNames(locale, 'long'),
|
|
637
|
+
weekdayShort: intlWeekdayNames(locale, 'short'),
|
|
638
|
+
weekdayLong: intlWeekdayNames(locale, 'long'),
|
|
639
|
+
weekdayNarrow: intlWeekdayNames(locale, 'narrow'),
|
|
640
|
+
};
|
|
641
|
+
const { regex, captures, tokens } = buildParseRegex(format, localeNames);
|
|
642
|
+
const fields = applyParseRegex(text, regex, captures, localeNames);
|
|
643
|
+
if (!fields)
|
|
644
|
+
return undefined;
|
|
645
|
+
if (!validateFields(fields))
|
|
646
|
+
return undefined;
|
|
647
|
+
// If a token was present but couldn't be parsed (e.g. half-year out of range),
|
|
648
|
+
// treat the whole parse as failed instead of silently dropping the field.
|
|
649
|
+
if (tokens.has('[H]n') && fields.halfYear === undefined)
|
|
650
|
+
return undefined;
|
|
651
|
+
if (tokens.has('Q') && fields.quarter === undefined)
|
|
652
|
+
return undefined;
|
|
653
|
+
const tz = systemTimeZone();
|
|
654
|
+
// ISO mode is determined by the *weekYear* token (GGGG), not the week
|
|
655
|
+
// number tokens. Mixed formats like `YYYY-WW` (calendar year + ISO week)
|
|
656
|
+
// would otherwise enable isoMode but lack `fields.isoWeekYear`, silently
|
|
657
|
+
// dropping the parsed week number and falling through to the year-only
|
|
658
|
+
// branch.
|
|
659
|
+
const isoMode = tokens.has('GGGG');
|
|
660
|
+
const localeMode = tokens.has('gggg') || tokens.has('ww') || tokens.has('w');
|
|
661
|
+
const hasYear = fields.year !== undefined;
|
|
662
|
+
const hasMonth = fields.month !== undefined;
|
|
663
|
+
const hasDay = fields.day !== undefined;
|
|
664
|
+
const hasQuarter = fields.quarter !== undefined;
|
|
665
|
+
const hasHalfYear = fields.halfYear !== undefined;
|
|
666
|
+
const hasWeek = isoMode ||
|
|
667
|
+
localeMode ||
|
|
668
|
+
fields.localeWeek !== undefined ||
|
|
669
|
+
fields.isoWeek !== undefined;
|
|
670
|
+
const hasUnixMs = fields.unixMs !== undefined;
|
|
671
|
+
const hasDayOfYear = fields.dayOfYear !== undefined;
|
|
672
|
+
// Unix epoch — standalone enough to derive a full datetime regardless
|
|
673
|
+
// of whether other date tokens were present in the format.
|
|
674
|
+
if (hasUnixMs) {
|
|
675
|
+
try {
|
|
676
|
+
return Temporal.Instant.fromEpochMilliseconds(fields.unixMs).toString();
|
|
677
|
+
}
|
|
678
|
+
catch (_g) {
|
|
679
|
+
return undefined;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// Day-of-year + year — derive the date by adding (doy-1) days to Jan 1.
|
|
683
|
+
if (hasDayOfYear && hasYear && !hasMonth && !hasDay) {
|
|
684
|
+
const year = fields.year;
|
|
685
|
+
const doy = fields.dayOfYear;
|
|
686
|
+
const jan1 = Temporal.PlainDate.from({ year, month: 1, day: 1 });
|
|
687
|
+
const daysInYear = Temporal.PlainDate.from({
|
|
688
|
+
year: year + 1,
|
|
689
|
+
month: 1,
|
|
690
|
+
day: 1,
|
|
691
|
+
}).since(jan1, { largestUnit: 'days' }).days;
|
|
692
|
+
if (doy < 1 || doy > daysInYear)
|
|
693
|
+
return undefined;
|
|
694
|
+
return toISO(jan1
|
|
695
|
+
.add({ days: doy - 1 })
|
|
696
|
+
.toPlainDateTime(Temporal.PlainTime.from('00:00:00'))
|
|
697
|
+
.toZonedDateTime(tz));
|
|
698
|
+
}
|
|
699
|
+
// Half-year: re-route to quarter-based normalization.
|
|
700
|
+
if (hasHalfYear && hasYear && !hasMonth && !hasDay) {
|
|
701
|
+
const quarter = fields.halfYear === 1 ? 1 : 3;
|
|
702
|
+
return CalendarMethodsTemporal.getCurrentQuarterFirstDate(toISO(Temporal.PlainDateTime.from({
|
|
703
|
+
year: fields.year,
|
|
704
|
+
month: (quarter - 1) * 3 + 1,
|
|
705
|
+
day: 1,
|
|
706
|
+
}).toZonedDateTime(tz)));
|
|
707
|
+
}
|
|
708
|
+
// Week format
|
|
709
|
+
if (hasWeek && (isoMode ? fields.isoWeekYear : fields.localeWeekYear)) {
|
|
710
|
+
const weekYear = (isoMode ? fields.isoWeekYear : fields.localeWeekYear);
|
|
711
|
+
const weekNum = isoMode ? fields.isoWeek : fields.localeWeek;
|
|
712
|
+
// Reject when the weekYear token was present but the week-number
|
|
713
|
+
// token was missing or unparsed. Otherwise a `gggg`-only or `GGGG`-only
|
|
714
|
+
// format would propagate `undefined` into Temporal's `add({days:NaN})`
|
|
715
|
+
// and throw RangeError instead of returning undefined per contract.
|
|
716
|
+
if (weekNum === undefined || weekNum < 1)
|
|
717
|
+
return undefined;
|
|
718
|
+
const { firstDay, minimalDays } = isoMode
|
|
719
|
+
? { firstDay: 1, minimalDays: 4 }
|
|
720
|
+
: getWeekInfo(locale);
|
|
721
|
+
// Compute the actual max-week of `weekYear` instead of trusting the
|
|
722
|
+
// hard-coded ceiling of 53. Dec 28 always falls into the LAST week of
|
|
723
|
+
// its weekYear under ISO/CLDR rules, so its weekOfYear gives the max.
|
|
724
|
+
const maxWeeks = computeMaxWeeksOfYear(weekYear, firstDay, minimalDays, isoMode);
|
|
725
|
+
if (weekNum > maxWeeks)
|
|
726
|
+
return undefined;
|
|
727
|
+
// Anchor: week 1 contains Jan minimalDays (or Jan 4 for ISO).
|
|
728
|
+
const anchor = Temporal.PlainDate.from({
|
|
729
|
+
year: weekYear,
|
|
730
|
+
month: 1,
|
|
731
|
+
day: minimalDays,
|
|
732
|
+
});
|
|
733
|
+
const week1Start = startOfWeekDate(anchor, firstDay);
|
|
734
|
+
const target = week1Start.add({ days: (weekNum - 1) * 7 });
|
|
735
|
+
return toISO(target
|
|
736
|
+
.toPlainDateTime(Temporal.PlainTime.from('00:00:00'))
|
|
737
|
+
.toZonedDateTime(tz));
|
|
738
|
+
}
|
|
739
|
+
// Quarter format
|
|
740
|
+
if (hasQuarter && hasYear && !hasMonth && !hasDay) {
|
|
741
|
+
const quarter = fields.quarter;
|
|
742
|
+
if (quarter < 1 || quarter > 4)
|
|
743
|
+
return undefined;
|
|
744
|
+
return toISO(Temporal.PlainDateTime.from({
|
|
745
|
+
year: fields.year,
|
|
746
|
+
month: (quarter - 1) * 3 + 1,
|
|
747
|
+
day: 1,
|
|
748
|
+
}).toZonedDateTime(tz));
|
|
749
|
+
}
|
|
750
|
+
// Month-only — first of month.
|
|
751
|
+
if (hasYear && hasMonth && !hasDay && !hasWeek && !hasQuarter) {
|
|
752
|
+
const month = fields.month;
|
|
753
|
+
if (month < 1 || month > 12)
|
|
754
|
+
return undefined;
|
|
755
|
+
return toISO(Temporal.PlainDateTime.from({
|
|
756
|
+
year: fields.year,
|
|
757
|
+
month,
|
|
758
|
+
day: 1,
|
|
759
|
+
}).toZonedDateTime(tz));
|
|
760
|
+
}
|
|
761
|
+
// Year-only — first of year.
|
|
762
|
+
if (hasYear && !hasMonth && !hasDay && !hasWeek && !hasQuarter) {
|
|
763
|
+
return toISO(Temporal.PlainDateTime.from({
|
|
764
|
+
year: fields.year,
|
|
765
|
+
month: 1,
|
|
766
|
+
day: 1,
|
|
767
|
+
}).toZonedDateTime(tz));
|
|
768
|
+
}
|
|
769
|
+
// Full date (with optional time).
|
|
770
|
+
if (hasYear && hasMonth && hasDay) {
|
|
771
|
+
const month = fields.month;
|
|
772
|
+
const day = fields.day;
|
|
773
|
+
if (month < 1 || month > 12)
|
|
774
|
+
return undefined;
|
|
775
|
+
if (day < 1 || day > 31)
|
|
776
|
+
return undefined;
|
|
777
|
+
const hour = resolveHour(fields);
|
|
778
|
+
try {
|
|
779
|
+
return toISO(Temporal.PlainDateTime.from({
|
|
780
|
+
year: fields.year,
|
|
781
|
+
month,
|
|
782
|
+
day,
|
|
783
|
+
hour,
|
|
784
|
+
minute: (_a = fields.minute) !== null && _a !== void 0 ? _a : 0,
|
|
785
|
+
second: (_b = fields.second) !== null && _b !== void 0 ? _b : 0,
|
|
786
|
+
millisecond: (_c = fields.millisecond) !== null && _c !== void 0 ? _c : 0,
|
|
787
|
+
}, { overflow: 'reject' }).toZonedDateTime(tz));
|
|
788
|
+
}
|
|
789
|
+
catch (_h) {
|
|
790
|
+
return undefined;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
// Time-only (HH:mm, HH:mm:ss, hh:mm A, …) — anchor to today's date.
|
|
794
|
+
// Used by TimePicker / TimeRangePicker, which round-trip values through
|
|
795
|
+
// `parseFormattedValue` with formats that contain no date tokens.
|
|
796
|
+
const hasTime = fields.hour !== undefined ||
|
|
797
|
+
fields.hour12 !== undefined ||
|
|
798
|
+
fields.minute !== undefined ||
|
|
799
|
+
fields.second !== undefined ||
|
|
800
|
+
fields.millisecond !== undefined;
|
|
801
|
+
if (hasTime &&
|
|
802
|
+
!hasYear &&
|
|
803
|
+
!hasMonth &&
|
|
804
|
+
!hasDay &&
|
|
805
|
+
!hasWeek &&
|
|
806
|
+
!hasQuarter &&
|
|
807
|
+
!hasHalfYear) {
|
|
808
|
+
const hour = resolveHour(fields);
|
|
809
|
+
const today = Temporal.Now.zonedDateTimeISO(tz);
|
|
810
|
+
try {
|
|
811
|
+
return toISO(today.with({
|
|
812
|
+
hour,
|
|
813
|
+
minute: (_d = fields.minute) !== null && _d !== void 0 ? _d : 0,
|
|
814
|
+
second: (_e = fields.second) !== null && _e !== void 0 ? _e : 0,
|
|
815
|
+
millisecond: (_f = fields.millisecond) !== null && _f !== void 0 ? _f : 0,
|
|
816
|
+
microsecond: 0,
|
|
817
|
+
nanosecond: 0,
|
|
818
|
+
}));
|
|
819
|
+
}
|
|
820
|
+
catch (_j) {
|
|
821
|
+
return undefined;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return undefined;
|
|
825
|
+
},
|
|
826
|
+
};
|
|
827
|
+
function resolveHour(fields) {
|
|
828
|
+
var _a, _b;
|
|
829
|
+
if (fields.hour12 !== undefined) {
|
|
830
|
+
const base = fields.hour12 % 12;
|
|
831
|
+
return ((_a = fields.meridiem) !== null && _a !== void 0 ? _a : 'am') === 'pm' ? base + 12 : base;
|
|
832
|
+
}
|
|
833
|
+
return (_b = fields.hour) !== null && _b !== void 0 ? _b : 0;
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Compute the actual maximum week number of `weekYear` for the supplied
|
|
837
|
+
* locale week rule.
|
|
838
|
+
*
|
|
839
|
+
* For ISO 8601 (minimalDays=4), Dec 28 is guaranteed to fall in the last
|
|
840
|
+
* week of its own weekYear, so Temporal's native `weekOfYear` is correct.
|
|
841
|
+
*
|
|
842
|
+
* For other locale rules (e.g. minimalDays=1 in en-US), Dec 28 may already
|
|
843
|
+
* belong to the NEXT weekYear (en-US 2025-12-28 → 2026-W01). Instead we
|
|
844
|
+
* derive the last day of `weekYear` as `week1StartFor(weekYear + 1) - 1`,
|
|
845
|
+
* then count how many weeks elapse between `week1Start` of `weekYear` and
|
|
846
|
+
* that last day's week start.
|
|
847
|
+
*/
|
|
848
|
+
function computeMaxWeeksOfYear(weekYear, firstDay, minimalDays, isoMode) {
|
|
849
|
+
var _a;
|
|
850
|
+
const Temporal = getTemporal();
|
|
851
|
+
if (isoMode) {
|
|
852
|
+
return ((_a = Temporal.PlainDate.from({
|
|
853
|
+
year: weekYear,
|
|
854
|
+
month: 12,
|
|
855
|
+
day: 28,
|
|
856
|
+
}).weekOfYear) !== null && _a !== void 0 ? _a : 52);
|
|
857
|
+
}
|
|
858
|
+
const week1Start = startOfWeekDate(Temporal.PlainDate.from({
|
|
859
|
+
year: weekYear,
|
|
860
|
+
month: 1,
|
|
861
|
+
day: minimalDays,
|
|
862
|
+
}), firstDay);
|
|
863
|
+
const nextYearWeek1Start = startOfWeekDate(Temporal.PlainDate.from({
|
|
864
|
+
year: weekYear + 1,
|
|
865
|
+
month: 1,
|
|
866
|
+
day: minimalDays,
|
|
867
|
+
}), firstDay);
|
|
868
|
+
// The last day belonging to `weekYear` sits one day before next year's
|
|
869
|
+
// week 1. Count the week-buckets between week 1 of `weekYear` and the
|
|
870
|
+
// bucket containing that last day.
|
|
871
|
+
const lastDayOfWeekYear = nextYearWeek1Start.subtract({ days: 1 });
|
|
872
|
+
const lastDayWeekStart = startOfWeekDate(lastDayOfWeekYear, firstDay);
|
|
873
|
+
const daysDiff = lastDayWeekStart.since(week1Start, {
|
|
874
|
+
largestUnit: 'days',
|
|
875
|
+
}).days;
|
|
876
|
+
return Math.floor(daysDiff / 7) + 1;
|
|
877
|
+
}
|
|
878
|
+
function validateFields(fields, tokens) {
|
|
879
|
+
if (fields.month !== undefined && (fields.month < 1 || fields.month > 12)) {
|
|
880
|
+
return false;
|
|
881
|
+
}
|
|
882
|
+
if (fields.day !== undefined && (fields.day < 1 || fields.day > 31)) {
|
|
883
|
+
return false;
|
|
884
|
+
}
|
|
885
|
+
if (fields.hour !== undefined && (fields.hour < 0 || fields.hour > 23)) {
|
|
886
|
+
return false;
|
|
887
|
+
}
|
|
888
|
+
if (fields.hour12 !== undefined &&
|
|
889
|
+
(fields.hour12 < 1 || fields.hour12 > 12)) {
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
892
|
+
if (fields.minute !== undefined &&
|
|
893
|
+
(fields.minute < 0 || fields.minute > 59)) {
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
if (fields.second !== undefined &&
|
|
897
|
+
(fields.second < 0 || fields.second > 59)) {
|
|
898
|
+
return false;
|
|
899
|
+
}
|
|
900
|
+
if (fields.quarter !== undefined &&
|
|
901
|
+
(fields.quarter < 1 || fields.quarter > 4)) {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
if (fields.halfYear !== undefined &&
|
|
905
|
+
fields.halfYear !== 1 &&
|
|
906
|
+
fields.halfYear !== 2) {
|
|
907
|
+
return false;
|
|
908
|
+
}
|
|
909
|
+
return true;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
export { CalendarMethodsTemporal as default };
|