@bbn/bbn 2.0.23 → 2.0.25

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.
@@ -0,0 +1 @@
1
+ export default function buildLocaleFromIntl(): void;
@@ -0,0 +1,37 @@
1
+ import extend from "../../fn/object/extend.js";
2
+ import numProperties from "../../fn/object/numProperties.js";
3
+ export default function buildLocaleFromIntl() {
4
+ if (numProperties(bbn.dt.locales)) {
5
+ return;
6
+ }
7
+ const langs = [bbn.env.lang, ...navigator.languages];
8
+ const fmtMonthLong = new Intl.DateTimeFormat(langs, { month: 'long' });
9
+ const fmtMonthShort = new Intl.DateTimeFormat(langs, { month: 'short' });
10
+ const fmtWeekLong = new Intl.DateTimeFormat(langs, { weekday: 'long' });
11
+ const fmtWeekShort = new Intl.DateTimeFormat(langs, { weekday: 'short' });
12
+ // Create 12 dates for months (2020 chosen arbitrarily)
13
+ const monthsLong = [];
14
+ const monthsShort = [];
15
+ for (let m = 0; m < 12; m++) {
16
+ const d = new Date(2020, m, 1);
17
+ monthsLong.push(fmtMonthLong.format(d));
18
+ monthsShort.push(fmtMonthShort.format(d));
19
+ }
20
+ // Create 7 dates for weekdays (starting from Sunday 2020-02-02 which *is* Sunday)
21
+ // 2020-02-02 is Sunday → guarantees stable weekday list
22
+ const baseSunday = new Date(2020, 1, 2); // YYYY, MM (0-based), DD
23
+ const weekdaysLong = [];
24
+ const weekdaysShort = [];
25
+ for (let i = 0; i < 7; i++) {
26
+ const d = new Date(baseSunday.getTime() + i * 86400000);
27
+ weekdaysLong.push(fmtWeekLong.format(d));
28
+ weekdaysShort.push(fmtWeekShort.format(d));
29
+ }
30
+ extend(bbn.dt.locales, {
31
+ monthsLong,
32
+ monthsShort,
33
+ weekdaysLong,
34
+ weekdaysShort,
35
+ });
36
+ }
37
+ ;
@@ -0,0 +1,7 @@
1
+ import { Temporal } from 'temporal-polyfill';
2
+ export default function parse(input: string, format: string | string[], locale?: {
3
+ monthsLong?: string[];
4
+ monthsShort?: string[];
5
+ weekdaysLong?: string[];
6
+ weekdaysShort?: string[];
7
+ }): Temporal.Instant | Temporal.PlainDateTime | Temporal.PlainDate | Temporal.PlainTime | Temporal.PlainYearMonth | Temporal.PlainMonthDay;
@@ -0,0 +1,527 @@
1
+ import buildLocaleFromIntl from './buildLocaleFromIntl.js';
2
+ export default function parse(input, format, locale) {
3
+ var _a, _b, _c, _d;
4
+ buildLocaleFromIntl();
5
+ const TemporalAny = globalThis.Temporal;
6
+ if (!TemporalAny) {
7
+ throw new Error('Temporal API is required (load @js-temporal/polyfill)');
8
+ }
9
+ const T = TemporalAny;
10
+ const loc = {
11
+ monthsLong: (_a = locale === null || locale === void 0 ? void 0 : locale.monthsLong) !== null && _a !== void 0 ? _a : bbn.dt.locales.monthsLong,
12
+ monthsShort: (_b = locale === null || locale === void 0 ? void 0 : locale.monthsShort) !== null && _b !== void 0 ? _b : bbn.dt.locales.monthsShort,
13
+ weekdaysLong: (_c = locale === null || locale === void 0 ? void 0 : locale.weekdaysLong) !== null && _c !== void 0 ? _c : bbn.dt.locales.weekdaysLong,
14
+ weekdaysShort: (_d = locale === null || locale === void 0 ? void 0 : locale.weekdaysShort) !== null && _d !== void 0 ? _d : bbn.dt.locales.weekdaysShort
15
+ };
16
+ const escapeRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
17
+ const makeTokenSpecs = () => [
18
+ // Years
19
+ {
20
+ token: 'YYYY',
21
+ regex: '\\d{4}',
22
+ apply: (v, ctx) => {
23
+ ctx.year = parseInt(v, 10);
24
+ ctx.hasYear = true;
25
+ }
26
+ },
27
+ {
28
+ token: 'YY',
29
+ regex: '\\d{2}',
30
+ apply: (v, ctx) => {
31
+ const n = parseInt(v, 10);
32
+ ctx.year = n >= 70 ? 1900 + n : 2000 + n;
33
+ ctx.hasYear = true;
34
+ }
35
+ },
36
+ {
37
+ token: 'Y',
38
+ regex: '[+-]?\\d{1,6}',
39
+ apply: (v, ctx) => {
40
+ ctx.year = parseInt(v, 10);
41
+ ctx.hasYear = true;
42
+ }
43
+ },
44
+ // Months
45
+ {
46
+ token: 'MMMM',
47
+ regex: '[^\\d\\s]+',
48
+ apply: (v, ctx) => {
49
+ const idx = loc.monthsLong
50
+ .findIndex((m) => m.toLowerCase() === v.toLowerCase());
51
+ if (idx === -1) {
52
+ throw new Error('Invalid month name: ' + v);
53
+ }
54
+ ctx.month = idx + 1;
55
+ ctx.hasMonth = true;
56
+ }
57
+ },
58
+ {
59
+ token: 'MMM',
60
+ regex: '[^\\d\\s]+',
61
+ apply: (v, ctx) => {
62
+ const idx = loc.monthsShort
63
+ .findIndex((m) => m.toLowerCase() === v.toLowerCase());
64
+ if (idx === -1) {
65
+ throw new Error('Invalid short month name: ' + v);
66
+ }
67
+ ctx.month = idx + 1;
68
+ ctx.hasMonth = true;
69
+ }
70
+ },
71
+ {
72
+ token: 'MM',
73
+ regex: '\\d{2}',
74
+ apply: (v, ctx) => {
75
+ const n = parseInt(v, 10);
76
+ if (n < 1 || n > 12) {
77
+ throw new Error('Invalid month: ' + n);
78
+ }
79
+ ctx.month = n;
80
+ ctx.hasMonth = true;
81
+ }
82
+ },
83
+ {
84
+ token: 'mm',
85
+ regex: '\\d{2}',
86
+ apply: (v, ctx) => {
87
+ const n = parseInt(v, 10);
88
+ if (n < 0 || n > 59) {
89
+ throw new Error('Invalid minute: ' + n);
90
+ }
91
+ // NOTE: in your original code this wrote to month, but name suggests minutes.
92
+ // I'm keeping original behavior, but you might want to correct this.
93
+ ctx.month = n;
94
+ ctx.hasMonth = true;
95
+ }
96
+ },
97
+ {
98
+ token: 'M',
99
+ regex: '\\d{1,2}',
100
+ apply: (v, ctx) => {
101
+ const n = parseInt(v, 10);
102
+ if (n < 1 || n > 12) {
103
+ throw new Error('Invalid month: ' + n);
104
+ }
105
+ ctx.month = n;
106
+ ctx.hasMonth = true;
107
+ }
108
+ },
109
+ {
110
+ token: 'm', // PHP-like month
111
+ regex: '\\d{2}',
112
+ apply: (v, ctx) => {
113
+ const n = parseInt(v, 10);
114
+ if (n < 1 || n > 12) {
115
+ throw new Error('Invalid month: ' + n);
116
+ }
117
+ ctx.month = n;
118
+ ctx.hasMonth = true;
119
+ }
120
+ },
121
+ // Day of month
122
+ {
123
+ token: 'DD',
124
+ regex: '\\d{2}',
125
+ apply: (v, ctx) => {
126
+ const n = parseInt(v, 10);
127
+ if (n < 1 || n > 31) {
128
+ throw new Error('Invalid day of month: ' + n);
129
+ }
130
+ ctx.day = n;
131
+ ctx.hasDay = true;
132
+ }
133
+ },
134
+ {
135
+ token: 'D',
136
+ regex: '\\d{1,2}',
137
+ apply: (v, ctx) => {
138
+ const n = parseInt(v, 10);
139
+ if (n < 1 || n > 31) {
140
+ throw new Error('Invalid day of month: ' + n);
141
+ }
142
+ ctx.day = n;
143
+ ctx.hasDay = true;
144
+ }
145
+ },
146
+ {
147
+ token: 'd', // PHP-like day-of-month
148
+ regex: '\\d{2}',
149
+ apply: (v, ctx) => {
150
+ const n = parseInt(v, 10);
151
+ if (n < 1 || n > 31) {
152
+ throw new Error('Invalid day of month: ' + n);
153
+ }
154
+ ctx.day = n;
155
+ ctx.hasDay = true;
156
+ }
157
+ },
158
+ // Weekday (validated only, not used for construction)
159
+ {
160
+ token: 'dddd',
161
+ regex: '[^\\d\\s]+',
162
+ apply: (v, ctx) => {
163
+ const idx = loc.weekdaysLong
164
+ .findIndex((w) => w.toLowerCase() === v.toLowerCase());
165
+ if (idx === -1) {
166
+ throw new Error('Invalid weekday name: ' + v);
167
+ }
168
+ ctx.weekday = idx; // 0-6
169
+ }
170
+ },
171
+ {
172
+ token: 'ddd',
173
+ regex: '[^\\d\\s]+',
174
+ apply: (v, ctx) => {
175
+ const idx = loc.weekdaysShort
176
+ .findIndex((w) => w.toLowerCase() === v.toLowerCase());
177
+ if (idx === -1) {
178
+ throw new Error('Invalid short weekday name: ' + v);
179
+ }
180
+ ctx.weekday = idx;
181
+ }
182
+ },
183
+ {
184
+ token: 'EE',
185
+ regex: '\\d{1}',
186
+ apply: (v, ctx) => {
187
+ const n = parseInt(v, 10);
188
+ if (n < 0 || n > 7) {
189
+ throw new Error('Invalid weekday number: ' + n);
190
+ }
191
+ ctx.weekday = n;
192
+ }
193
+ },
194
+ // Hours
195
+ {
196
+ token: 'HH',
197
+ regex: '\\d{2}',
198
+ apply: (v, ctx) => {
199
+ const n = parseInt(v, 10);
200
+ if (n < 0 || n > 23) {
201
+ throw new Error('Invalid hour: ' + n);
202
+ }
203
+ ctx.hour = n;
204
+ ctx.hasHour = true;
205
+ }
206
+ },
207
+ {
208
+ token: 'H',
209
+ regex: '\\d{1,2}',
210
+ apply: (v, ctx) => {
211
+ const n = parseInt(v, 10);
212
+ if (n < 0 || n > 23) {
213
+ throw new Error('Invalid hour: ' + n);
214
+ }
215
+ ctx.hour = n;
216
+ ctx.hasHour = true;
217
+ }
218
+ },
219
+ {
220
+ token: 'h', // PHP-like 24h alias here
221
+ regex: '\\d{2}',
222
+ apply: (v, ctx) => {
223
+ const n = parseInt(v, 10);
224
+ if (n < 0 || n > 23) {
225
+ throw new Error('Invalid hour: ' + n);
226
+ }
227
+ ctx.hour = n;
228
+ ctx.hasHour = true;
229
+ }
230
+ },
231
+ // Minutes
232
+ {
233
+ token: 'II',
234
+ regex: '\\d{2}',
235
+ apply: (v, ctx) => {
236
+ const n = parseInt(v, 10);
237
+ if (n < 0 || n > 59) {
238
+ throw new Error('Invalid minute: ' + n);
239
+ }
240
+ ctx.minute = n;
241
+ ctx.hasMinute = true;
242
+ }
243
+ },
244
+ {
245
+ token: 'I',
246
+ regex: '\\d{1,2}',
247
+ apply: (v, ctx) => {
248
+ const n = parseInt(v, 10);
249
+ if (n < 0 || n > 59) {
250
+ throw new Error('Invalid minute: ' + n);
251
+ }
252
+ ctx.minute = n;
253
+ ctx.hasMinute = true;
254
+ }
255
+ },
256
+ {
257
+ token: 'i', // PHP-like minutes
258
+ regex: '\\d{2}',
259
+ apply: (v, ctx) => {
260
+ const n = parseInt(v, 10);
261
+ if (n < 0 || n > 59) {
262
+ throw new Error('Invalid minute: ' + n);
263
+ }
264
+ ctx.minute = n;
265
+ ctx.hasMinute = true;
266
+ }
267
+ },
268
+ // Seconds
269
+ {
270
+ token: 'SS',
271
+ regex: '\\d{2}',
272
+ apply: (v, ctx) => {
273
+ const n = parseInt(v, 10);
274
+ if (n < 0 || n > 59) {
275
+ throw new Error('Invalid second: ' + n);
276
+ }
277
+ ctx.second = n;
278
+ ctx.hasSecond = true;
279
+ }
280
+ },
281
+ {
282
+ token: 'S',
283
+ regex: '\\d{1,2}',
284
+ apply: (v, ctx) => {
285
+ const n = parseInt(v, 10);
286
+ if (n < 0 || n > 59) {
287
+ throw new Error('Invalid second: ' + n);
288
+ }
289
+ ctx.second = n;
290
+ ctx.hasSecond = true;
291
+ }
292
+ },
293
+ {
294
+ token: 's', // PHP-like seconds
295
+ regex: '\\d{2}',
296
+ apply: (v, ctx) => {
297
+ const n = parseInt(v, 10);
298
+ if (n < 0 || n > 59) {
299
+ throw new Error('Invalid second: ' + n);
300
+ }
301
+ ctx.second = n;
302
+ ctx.hasSecond = true;
303
+ }
304
+ },
305
+ // Milliseconds
306
+ {
307
+ token: 'ms',
308
+ regex: '\\d{1,3}',
309
+ apply: (v, ctx) => {
310
+ const n = parseInt(v, 10);
311
+ if (n < 0 || n > 999) {
312
+ throw new Error('Invalid millisecond: ' + n);
313
+ }
314
+ ctx.ms = n;
315
+ ctx.hasMs = true;
316
+ }
317
+ },
318
+ // Week (parsed, not used yet)
319
+ {
320
+ token: 'WWWW',
321
+ regex: '\\d{1,2}',
322
+ apply: (v, ctx) => {
323
+ const n = parseInt(v, 10);
324
+ if (n < 1 || n > 53) {
325
+ throw new Error('Invalid week number: ' + n);
326
+ }
327
+ ctx.week = n;
328
+ }
329
+ },
330
+ {
331
+ token: 'WWW',
332
+ regex: '\\d{1,2}',
333
+ apply: (v, ctx) => {
334
+ const n = parseInt(v, 10);
335
+ if (n < 1 || n > 53) {
336
+ throw new Error('Invalid week number: ' + n);
337
+ }
338
+ ctx.week = n;
339
+ }
340
+ },
341
+ {
342
+ token: 'WW',
343
+ regex: '\\d{1,2}',
344
+ apply: (v, ctx) => {
345
+ const n = parseInt(v, 10);
346
+ if (n < 1 || n > 53) {
347
+ throw new Error('Invalid week number: ' + n);
348
+ }
349
+ ctx.week = n;
350
+ }
351
+ },
352
+ {
353
+ token: 'W',
354
+ regex: '\\d{1,2}',
355
+ apply: (v, ctx) => {
356
+ const n = parseInt(v, 10);
357
+ if (n < 1 || n > 53) {
358
+ throw new Error('Invalid week number: ' + n);
359
+ }
360
+ ctx.week = n;
361
+ }
362
+ },
363
+ // Timezone offset (Z)
364
+ {
365
+ token: 'Z',
366
+ regex: '(?:Z|[+-]\\d{2}:?\\d{2})',
367
+ apply: (v, ctx) => {
368
+ if (v === 'Z' || v === 'z') {
369
+ ctx.offsetMinutes = 0;
370
+ return;
371
+ }
372
+ const sign = v[0] === '-' ? -1 : 1;
373
+ const rest = v.slice(1); // "02:00" or "0500"
374
+ let hh;
375
+ let mm;
376
+ if (rest.includes(':')) {
377
+ const [h, m] = rest.split(':');
378
+ hh = parseInt(h, 10);
379
+ mm = parseInt(m, 10);
380
+ }
381
+ else {
382
+ hh = parseInt(rest.slice(0, 2), 10);
383
+ mm = parseInt(rest.slice(2, 4), 10);
384
+ }
385
+ if (hh < 0 || hh > 23 || mm < 0 || mm > 59) {
386
+ throw new Error('Invalid timezone offset: ' + v);
387
+ }
388
+ ctx.offsetMinutes = sign * (hh * 60 + mm);
389
+ }
390
+ },
391
+ // Timezone name (z) – IANA like "Europe/Rome"
392
+ {
393
+ token: 'z',
394
+ regex: '[A-Za-z_\\/]+',
395
+ apply: (v, ctx) => {
396
+ ctx.timeZone = v;
397
+ }
398
+ }
399
+ ];
400
+ function parseWithFormat(fmt) {
401
+ var _a;
402
+ const ctx = {
403
+ year: 1970,
404
+ month: 1,
405
+ day: 1,
406
+ hour: 0,
407
+ minute: 0,
408
+ second: 0,
409
+ ms: 0,
410
+ hasYear: false,
411
+ hasMonth: false,
412
+ hasDay: false,
413
+ hasHour: false,
414
+ hasMinute: false,
415
+ hasSecond: false,
416
+ hasMs: false
417
+ };
418
+ const tokenSpecs = makeTokenSpecs();
419
+ const tokensByLength = [...tokenSpecs].sort((a, b) => b.token.length - a.token.length);
420
+ let pattern = '';
421
+ const applyFns = [];
422
+ let i = 0;
423
+ while (i < fmt.length) {
424
+ let matchedToken = null;
425
+ for (const spec of tokensByLength) {
426
+ if (fmt.startsWith(spec.token, i)) {
427
+ matchedToken = spec;
428
+ break;
429
+ }
430
+ }
431
+ if (matchedToken) {
432
+ pattern += `(${matchedToken.regex})`;
433
+ if (matchedToken.apply) {
434
+ applyFns.push(value => matchedToken.apply(value, ctx));
435
+ }
436
+ else {
437
+ applyFns.push(() => { });
438
+ }
439
+ i += matchedToken.token.length;
440
+ }
441
+ else {
442
+ pattern += escapeRegex(fmt[i]);
443
+ i += 1;
444
+ }
445
+ }
446
+ const fullRegex = new RegExp('^' + pattern + '$');
447
+ const match = fullRegex.exec(input);
448
+ if (!match) {
449
+ throw new Error(`Date string "${input}" does not match format "${fmt}"`);
450
+ }
451
+ for (let idx = 1; idx < match.length; idx++) {
452
+ const value = match[idx];
453
+ const apply = applyFns[idx - 1];
454
+ if (value != null && apply) {
455
+ apply(value);
456
+ }
457
+ }
458
+ const hasDate = ctx.hasYear || ctx.hasMonth || ctx.hasDay;
459
+ const hasFullDate = ctx.hasYear && ctx.hasMonth && ctx.hasDay;
460
+ const hasYearMonthOnly = ctx.hasYear && ctx.hasMonth && !ctx.hasDay;
461
+ const hasMonthDayOnly = !ctx.hasYear && ctx.hasMonth && ctx.hasDay;
462
+ const hasTime = ctx.hasHour || ctx.hasMinute || ctx.hasSecond || ctx.hasMs;
463
+ const hasZone = ctx.timeZone != null || ctx.offsetMinutes != null;
464
+ // ---------- 1) If timezone (Z or z) → Instant ----------
465
+ if (hasZone) {
466
+ // Fill date/time with whatever we have + defaults (1970-01-01 etc.)
467
+ let pdt;
468
+ try {
469
+ pdt = new T.PlainDateTime(ctx.year, ctx.month, ctx.day, ctx.hour, ctx.minute, ctx.second, ctx.ms * 1000000);
470
+ }
471
+ catch (_b) {
472
+ throw new Error('Invalid date/time components');
473
+ }
474
+ if (ctx.timeZone) {
475
+ const tz = T.TimeZone.from(ctx.timeZone);
476
+ const zdt = pdt.toZonedDateTime(tz);
477
+ return zdt.toInstant();
478
+ }
479
+ // offsetMinutes only
480
+ const utcMs = Date.UTC(ctx.year, ctx.month - 1, ctx.day, ctx.hour, ctx.minute, ctx.second, ctx.ms);
481
+ const epochMs = utcMs - ((_a = ctx.offsetMinutes) !== null && _a !== void 0 ? _a : 0) * 60000;
482
+ return T.Instant.fromEpochMilliseconds(epochMs);
483
+ }
484
+ // ---------- 2) No timezone: decide which Plain* type ----------
485
+ if (hasDate && hasTime) {
486
+ // Full DateTime (even if some date fields defaulted; we require full date)
487
+ if (!hasFullDate) {
488
+ throw new Error('PlainDateTime requires year, month and day');
489
+ }
490
+ return new T.PlainDateTime(ctx.year, ctx.month, ctx.day, ctx.hour, ctx.minute, ctx.second, ctx.ms * 1000000);
491
+ }
492
+ if (hasDate && !hasTime) {
493
+ if (hasFullDate) {
494
+ return new T.PlainDate(ctx.year, ctx.month, ctx.day);
495
+ }
496
+ if (hasYearMonthOnly) {
497
+ return new T.PlainYearMonth(ctx.year, ctx.month);
498
+ }
499
+ if (hasMonthDayOnly) {
500
+ // Reference year: 1972 is often used (leap year)
501
+ return new T.PlainMonthDay(ctx.month, ctx.day, 1972);
502
+ }
503
+ // e.g. only year → ambiguous, you can decide another behavior if you want
504
+ throw new Error('Not enough date components for a known Temporal type');
505
+ }
506
+ if (!hasDate && hasTime) {
507
+ // PlainTime
508
+ return new T.PlainTime(ctx.hour, ctx.minute, ctx.second, ctx.ms * 1000000);
509
+ }
510
+ throw new Error('No date or time information found in input');
511
+ }
512
+ // ---------- Handle single format or array of formats ----------
513
+ if (Array.isArray(format)) {
514
+ let lastError = null;
515
+ for (const fmt of format) {
516
+ try {
517
+ return parseWithFormat(fmt);
518
+ }
519
+ catch (e) {
520
+ lastError = e;
521
+ }
522
+ }
523
+ throw lastError !== null && lastError !== void 0 ? lastError : new Error('No format matched');
524
+ }
525
+ return parseWithFormat(format);
526
+ }
527
+ ;
package/dist/dt.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ import parse from './dt/functions/parse.js';
2
+ declare const dt: {
3
+ (value: any, inputFormat?: null | String): void;
4
+ locales: any;
5
+ parse: typeof parse;
6
+ time(): void;
7
+ date(): void;
8
+ dateTime(): void;
9
+ duration(): void;
10
+ zoned(): void;
11
+ monthDay(): void;
12
+ yearMonth(): void;
13
+ };
14
+ export default dt;