@bbn/bbn 2.0.29 → 2.0.31
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/dist/bbn.js +1 -1
- package/dist/bbn.js.map +1 -1
- package/dist/dt/functions/buildLocaleFromIntl.d.ts +0 -33
- package/dist/dt/functions/buildLocaleFromIntl.js +65 -59
- package/dist/dt/functions/guessFormat.d.ts +12 -0
- package/dist/dt/functions/guessFormat.js +120 -0
- package/dist/dt.d.ts +2 -0
- package/dist/dt.js +2 -0
- package/package.json +1 -1
|
@@ -1,34 +1 @@
|
|
|
1
|
-
type Style = 'full' | 'long' | 'medium' | 'short';
|
|
2
|
-
type CommonFormats = {
|
|
3
|
-
date: Array<{
|
|
4
|
-
style: Style;
|
|
5
|
-
pattern: string;
|
|
6
|
-
sample: string;
|
|
7
|
-
options: Intl.DateTimeFormatOptions;
|
|
8
|
-
}>;
|
|
9
|
-
time: Array<{
|
|
10
|
-
style: Style;
|
|
11
|
-
pattern: string;
|
|
12
|
-
sample: string;
|
|
13
|
-
options: Intl.DateTimeFormatOptions;
|
|
14
|
-
}>;
|
|
15
|
-
datetime: Array<{
|
|
16
|
-
dateStyle: Style;
|
|
17
|
-
timeStyle: Style;
|
|
18
|
-
pattern: string;
|
|
19
|
-
sample: string;
|
|
20
|
-
options: Intl.DateTimeFormatOptions;
|
|
21
|
-
}>;
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Get all common date/time/datetime formats for a given locale using Intl.DateTimeFormat.
|
|
25
|
-
*
|
|
26
|
-
* - Date formats: dateStyle only (full/long/medium/short)
|
|
27
|
-
* - Time formats: timeStyle only
|
|
28
|
-
* - Datetime formats: all combinations of dateStyle × timeStyle
|
|
29
|
-
*
|
|
30
|
-
* Returns tokens using your convention: YYYY, MM, DD, HH, II, SS, A, etc.
|
|
31
|
-
*/
|
|
32
|
-
export declare function getCommonFormatsForLocale(lng: string | string[]): CommonFormats;
|
|
33
1
|
export default function buildLocaleFromIntl(): void;
|
|
34
|
-
export {};
|
|
@@ -1,45 +1,28 @@
|
|
|
1
1
|
import extend from "../../fn/object/extend.js";
|
|
2
2
|
import numProperties from "../../fn/object/numProperties.js";
|
|
3
|
-
/**
|
|
4
|
-
* Build a token pattern like "DD/MM/YYYY HH:II:SS" from Intl.DateTimeFormat parts.
|
|
5
|
-
*/
|
|
6
3
|
function partsToPattern(parts, hourCycle) {
|
|
7
4
|
let pattern = '';
|
|
8
|
-
// If we see a dayPeriod in parts, it's definitely 12-hour clock
|
|
9
5
|
const hasDayPeriod = parts.some(p => p.type === 'dayPeriod');
|
|
10
6
|
const is12h = hasDayPeriod || hourCycle === 'h12' || hourCycle === 'h11';
|
|
11
7
|
for (const p of parts) {
|
|
12
8
|
switch (p.type) {
|
|
13
9
|
case 'year':
|
|
14
|
-
// Usually "2000" → "YYYY"
|
|
15
10
|
pattern += 'YYYY';
|
|
16
11
|
break;
|
|
17
12
|
case 'month':
|
|
18
|
-
if (/^\d+$/.test(p.value))
|
|
19
|
-
// numeric month
|
|
13
|
+
if (/^\d+$/.test(p.value))
|
|
20
14
|
pattern += p.value.length === 2 ? 'MM' : 'M';
|
|
21
|
-
|
|
22
|
-
else {
|
|
23
|
-
// textual month
|
|
15
|
+
else
|
|
24
16
|
pattern += p.value.length > 3 ? 'MMMM' : 'MMM';
|
|
25
|
-
}
|
|
26
17
|
break;
|
|
27
18
|
case 'day':
|
|
28
19
|
pattern += p.value.length === 2 ? 'DD' : 'D';
|
|
29
20
|
break;
|
|
30
21
|
case 'weekday':
|
|
31
|
-
// You can refine this if you care about full vs short
|
|
32
22
|
pattern += p.value.length > 3 ? 'dddd' : 'ddd';
|
|
33
23
|
break;
|
|
34
24
|
case 'hour':
|
|
35
|
-
|
|
36
|
-
// 12-hour clock
|
|
37
|
-
pattern += p.value.length === 2 ? 'hh' : 'h';
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
// 24-hour clock
|
|
41
|
-
pattern += p.value.length === 2 ? 'HH' : 'H';
|
|
42
|
-
}
|
|
25
|
+
pattern += is12h ? (p.value.length === 2 ? 'hh' : 'h') : (p.value.length === 2 ? 'HH' : 'H');
|
|
43
26
|
break;
|
|
44
27
|
case 'minute':
|
|
45
28
|
pattern += 'II';
|
|
@@ -48,14 +31,14 @@ function partsToPattern(parts, hourCycle) {
|
|
|
48
31
|
pattern += 'SS';
|
|
49
32
|
break;
|
|
50
33
|
case 'dayPeriod':
|
|
51
|
-
// AM/PM
|
|
52
34
|
pattern += 'A';
|
|
53
35
|
break;
|
|
54
36
|
case 'timeZoneName':
|
|
55
|
-
// You may want 'z' or 'Z' depending on your conventions
|
|
56
37
|
pattern += 'z';
|
|
57
38
|
break;
|
|
58
39
|
case 'literal':
|
|
40
|
+
pattern += p.value;
|
|
41
|
+
break;
|
|
59
42
|
default:
|
|
60
43
|
pattern += p.value;
|
|
61
44
|
break;
|
|
@@ -64,69 +47,92 @@ function partsToPattern(parts, hourCycle) {
|
|
|
64
47
|
return pattern;
|
|
65
48
|
}
|
|
66
49
|
/**
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* - Date formats: dateStyle only (full/long/medium/short)
|
|
70
|
-
* - Time formats: timeStyle only
|
|
71
|
-
* - Datetime formats: all combinations of dateStyle × timeStyle
|
|
72
|
-
*
|
|
73
|
-
* Returns tokens using your convention: YYYY, MM, DD, HH, II, SS, A, etc.
|
|
50
|
+
* Returns all common date/time/datetime formats + weekday formats for a given locale.
|
|
74
51
|
*/
|
|
75
|
-
|
|
52
|
+
function getCommonFormatsForLocale(lng) {
|
|
76
53
|
const dateStyles = ['full', 'long', 'medium', 'short'];
|
|
77
54
|
const timeStyles = ['full', 'long', 'medium', 'short'];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
55
|
+
const sample = new Date(Date.UTC(2000, 0, 2, 13, 45, 30));
|
|
56
|
+
const result = {
|
|
57
|
+
date: [],
|
|
58
|
+
time: [],
|
|
59
|
+
datetime: [],
|
|
60
|
+
dateWithWeekday: [],
|
|
61
|
+
datetimeWithWeekday: []
|
|
62
|
+
};
|
|
63
|
+
// --- Date only ---
|
|
85
64
|
for (const ds of dateStyles) {
|
|
86
65
|
const options = { dateStyle: ds };
|
|
87
66
|
const fmt = new Intl.DateTimeFormat(lng, options);
|
|
88
|
-
const parts = fmt.formatToParts(
|
|
89
|
-
|
|
90
|
-
date.push({
|
|
67
|
+
const parts = fmt.formatToParts(sample);
|
|
68
|
+
result.date.push({
|
|
91
69
|
style: ds,
|
|
92
|
-
pattern,
|
|
93
|
-
sample: fmt.format(
|
|
70
|
+
pattern: partsToPattern(parts, fmt.resolvedOptions().hourCycle),
|
|
71
|
+
sample: fmt.format(sample),
|
|
94
72
|
options
|
|
95
73
|
});
|
|
96
74
|
}
|
|
97
|
-
// --- Time
|
|
75
|
+
// --- Time only ---
|
|
98
76
|
for (const ts of timeStyles) {
|
|
99
77
|
const options = { timeStyle: ts };
|
|
100
78
|
const fmt = new Intl.DateTimeFormat(lng, options);
|
|
101
|
-
const parts = fmt.formatToParts(
|
|
102
|
-
|
|
103
|
-
time.push({
|
|
79
|
+
const parts = fmt.formatToParts(sample);
|
|
80
|
+
result.time.push({
|
|
104
81
|
style: ts,
|
|
105
|
-
pattern,
|
|
106
|
-
sample: fmt.format(
|
|
82
|
+
pattern: partsToPattern(parts, fmt.resolvedOptions().hourCycle),
|
|
83
|
+
sample: fmt.format(sample),
|
|
107
84
|
options
|
|
108
85
|
});
|
|
109
86
|
}
|
|
110
|
-
// --- Date +
|
|
87
|
+
// --- Date + Time ---
|
|
111
88
|
for (const ds of dateStyles) {
|
|
112
89
|
for (const ts of timeStyles) {
|
|
113
|
-
const options = {
|
|
114
|
-
dateStyle: ds,
|
|
115
|
-
timeStyle: ts
|
|
116
|
-
};
|
|
90
|
+
const options = { dateStyle: ds, timeStyle: ts };
|
|
117
91
|
const fmt = new Intl.DateTimeFormat(lng, options);
|
|
118
|
-
const parts = fmt.formatToParts(
|
|
119
|
-
|
|
120
|
-
datetime.push({
|
|
92
|
+
const parts = fmt.formatToParts(sample);
|
|
93
|
+
result.datetime.push({
|
|
121
94
|
dateStyle: ds,
|
|
122
95
|
timeStyle: ts,
|
|
123
|
-
pattern,
|
|
124
|
-
sample: fmt.format(
|
|
96
|
+
pattern: partsToPattern(parts, fmt.resolvedOptions().hourCycle),
|
|
97
|
+
sample: fmt.format(sample),
|
|
125
98
|
options
|
|
126
99
|
});
|
|
127
100
|
}
|
|
128
101
|
}
|
|
129
|
-
|
|
102
|
+
// --- Date with Weekday (long + short) ---
|
|
103
|
+
for (const ds of dateStyles) {
|
|
104
|
+
for (const w of ["long", "short"]) {
|
|
105
|
+
const options = { dateStyle: ds, weekday: w };
|
|
106
|
+
const fmt = new Intl.DateTimeFormat(lng, options);
|
|
107
|
+
const parts = fmt.formatToParts(sample);
|
|
108
|
+
result.dateWithWeekday.push({
|
|
109
|
+
style: ds,
|
|
110
|
+
weekday: w,
|
|
111
|
+
pattern: partsToPattern(parts, fmt.resolvedOptions().hourCycle),
|
|
112
|
+
sample: fmt.format(sample),
|
|
113
|
+
options
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// --- Date + Time + Weekday ---
|
|
118
|
+
for (const ds of dateStyles) {
|
|
119
|
+
for (const ts of timeStyles) {
|
|
120
|
+
for (const w of ["long", "short"]) {
|
|
121
|
+
const options = { dateStyle: ds, timeStyle: ts, weekday: w };
|
|
122
|
+
const fmt = new Intl.DateTimeFormat(lng, options);
|
|
123
|
+
const parts = fmt.formatToParts(sample);
|
|
124
|
+
result.datetimeWithWeekday.push({
|
|
125
|
+
dateStyle: ds,
|
|
126
|
+
timeStyle: ts,
|
|
127
|
+
weekday: w,
|
|
128
|
+
pattern: partsToPattern(parts, fmt.resolvedOptions().hourCycle),
|
|
129
|
+
sample: fmt.format(sample),
|
|
130
|
+
options
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
130
136
|
}
|
|
131
137
|
export default function buildLocaleFromIntl() {
|
|
132
138
|
if (numProperties(bbn.dt.locales)) {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guess a date format string for the given input.
|
|
3
|
+
*
|
|
4
|
+
* - If `formats` is provided, it will try those formats in order and return
|
|
5
|
+
* the first one that successfully parses.
|
|
6
|
+
* - If `formats` is not provided, it will try a set of built-in common formats
|
|
7
|
+
* (MySQL, ISO/JS, EU/US, full-text using bbn.dt.locales).
|
|
8
|
+
* - Returns `null` if no format matches.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: It relies on `this.parse(input, format)` NOT throwing when the format is correct.
|
|
11
|
+
*/
|
|
12
|
+
export default function guessFormat(input: string, formats?: string[] | string): string | null;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import buildLocaleFromIntl from './buildLocaleFromIntl.js';
|
|
2
|
+
import parse from './parse.js';
|
|
3
|
+
/**
|
|
4
|
+
* Guess a date format string for the given input.
|
|
5
|
+
*
|
|
6
|
+
* - If `formats` is provided, it will try those formats in order and return
|
|
7
|
+
* the first one that successfully parses.
|
|
8
|
+
* - If `formats` is not provided, it will try a set of built-in common formats
|
|
9
|
+
* (MySQL, ISO/JS, EU/US, full-text using bbn.dt.locales).
|
|
10
|
+
* - Returns `null` if no format matches.
|
|
11
|
+
*
|
|
12
|
+
* NOTE: It relies on `this.parse(input, format)` NOT throwing when the format is correct.
|
|
13
|
+
*/
|
|
14
|
+
export default function guessFormat(input, formats) {
|
|
15
|
+
var _a;
|
|
16
|
+
const str = input.trim();
|
|
17
|
+
if (!str) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const tryFormats = (formatsToTry) => {
|
|
21
|
+
for (const fmt of formatsToTry) {
|
|
22
|
+
try {
|
|
23
|
+
// We only care that it parses without throwing
|
|
24
|
+
parse(str, fmt);
|
|
25
|
+
return fmt;
|
|
26
|
+
}
|
|
27
|
+
catch (_a) {
|
|
28
|
+
// ignore
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
33
|
+
// If user provided formats, restrict to those only
|
|
34
|
+
if (formats) {
|
|
35
|
+
const list = Array.isArray(formats) ? formats : [formats];
|
|
36
|
+
return tryFormats(list);
|
|
37
|
+
}
|
|
38
|
+
// -------- Autodetection mode (no user-provided formats) --------
|
|
39
|
+
const lower = str.toLowerCase();
|
|
40
|
+
// Access locales for full-text formats (months / weekdays)
|
|
41
|
+
buildLocaleFromIntl();
|
|
42
|
+
const loc = ((_a = bbn === null || bbn === void 0 ? void 0 : bbn.dt) === null || _a === void 0 ? void 0 : _a.locales) || {};
|
|
43
|
+
const monthsLong = loc.monthsLong || [];
|
|
44
|
+
const monthsShort = loc.monthsShort || [];
|
|
45
|
+
const weekdaysLong = loc.weekdaysLong || [];
|
|
46
|
+
const weekdaysShort = loc.weekdaysShort || [];
|
|
47
|
+
const timeFormats = loc.time || [];
|
|
48
|
+
const dateFormats = loc.date || [];
|
|
49
|
+
const datetimeFormats = loc.datetime || [];
|
|
50
|
+
const hasMonthName = monthsLong.some(m => lower.includes(m.toLowerCase())) ||
|
|
51
|
+
monthsShort.some(m => lower.includes(m.toLowerCase()));
|
|
52
|
+
const hasWeekdayName = weekdaysLong.some(w => lower.includes(w.toLowerCase())) ||
|
|
53
|
+
weekdaysShort.some(w => lower.includes(w.toLowerCase()));
|
|
54
|
+
const hasLetterTZ = /gmt|utc|[+-]\d{2}:?\d{2}|z$/i.test(str);
|
|
55
|
+
const looksISO = /^\d{4}-\d{2}-\d{2}t\d{2}:\d{2}:\d{2}(\.\d+)?(z|[+\-]\d{2}:?\d{2})?$/i.test(str);
|
|
56
|
+
const looksMySQLDateTime = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(:\d{2}(\.\d+)?)?$/i.test(str);
|
|
57
|
+
const looksMySQLDate = /^\d{4}-\d{2}-\d{2}$/.test(str);
|
|
58
|
+
const looksTimeOnly = /^\d{2}:\d{2}(:\d{2}(\.\d+)?)?$/.test(str);
|
|
59
|
+
// Start building candidate formats (most specific first)
|
|
60
|
+
const candidates = [
|
|
61
|
+
...datetimeFormats.map(f => f.pattern),
|
|
62
|
+
];
|
|
63
|
+
// --- Full-text / locale-based formats ---
|
|
64
|
+
if (hasMonthName || hasWeekdayName) {
|
|
65
|
+
// e.g. "Monday 15 January 2024"
|
|
66
|
+
candidates.push('dddd, DD MMMM YYYY HH:II:SSZ', 'dddd, DD MMMM YYYY HH:II:SS', 'dddd, DD MMMM YYYY', 'DD MMMM YYYY HH:II:SSZ', 'DD MMMM YYYY HH:II:SS', 'DD MMMM YYYY', 'ddd, DD MMM YYYY HH:II:SSZ', 'ddd, DD MMM YYYY HH:II:SS', 'ddd, DD MMM YYYY');
|
|
67
|
+
// JS Date.toString() / toUTCString()-like
|
|
68
|
+
// "Tue Oct 29 2024 14:30:00 GMT+0200"
|
|
69
|
+
candidates.push('ddd MMM DD YYYY HH:II:SSZ', 'ddd, DD MMM YYYY HH:II:SS z');
|
|
70
|
+
}
|
|
71
|
+
// --- ISO / JS-like default formats ---
|
|
72
|
+
if (looksISO || str.includes('T')) {
|
|
73
|
+
candidates.push('YYYY-MM-DDTHH:II:SS.msZ', 'YYYY-MM-DDTHH:II:SSZ', 'YYYY-MM-DDTHH:II:SS.ms', 'YYYY-MM-DDTHH:II:SS', 'YYYY-MM-DDTHH:II:Z', 'YYYY-MM-DDTHH:II');
|
|
74
|
+
}
|
|
75
|
+
// --- MySQL classic formats ---
|
|
76
|
+
if (looksMySQLDateTime) {
|
|
77
|
+
candidates.push('YYYY-MM-DD HH:II:SS.msZ', 'YYYY-MM-DD HH:II:SSZ', 'YYYY-MM-DD HH:II:SS.ms', 'YYYY-MM-DD HH:II:SS', 'YYYY-MM-DD HH:II');
|
|
78
|
+
}
|
|
79
|
+
if (looksMySQLDate) {
|
|
80
|
+
candidates.push('YYYY-MM-DD');
|
|
81
|
+
}
|
|
82
|
+
// --- Time-only strings ---
|
|
83
|
+
if (looksTimeOnly) {
|
|
84
|
+
candidates.push(...timeFormats.map(f => f.pattern), 'HH:II:SS.msZ', 'HH:II:SS.ms', 'HH:II:SS', 'HH:II');
|
|
85
|
+
}
|
|
86
|
+
// --- Common EU / US formats ---
|
|
87
|
+
candidates.push(...dateFormats.map(f => f.pattern),
|
|
88
|
+
// European style
|
|
89
|
+
'DD/MM/YYYY HH:II:SSZ', 'DD/MM/YYYY HH:II:SS', 'DD/MM/YYYY HH:II', 'DD/MM/YYYY', 'DD-MM-YYYY HH:II:SSZ', 'DD-MM-YYYY HH:II:SS', 'DD-MM-YYYY HH:II', 'DD-MM-YYYY',
|
|
90
|
+
// US style
|
|
91
|
+
'MM/DD/YYYY HH:II:SSZ', 'MM/DD/YYYY HH:II:SS', 'MM/DD/YYYY HH:II', 'MM/DD/YYYY',
|
|
92
|
+
// Dot-separated
|
|
93
|
+
'YYYY.MM.DD HH:II:SSZ', 'YYYY.MM.DD HH:II:SS', 'YYYY.MM.DD',
|
|
94
|
+
// MySQL-ish (if we haven't already pushed them by detection)
|
|
95
|
+
'YYYY-MM-DD HH:II:SSZ', 'YYYY-MM-DD HH:II:SS', 'YYYY-MM-DD HH:II', 'YYYY-MM-DD');
|
|
96
|
+
// If we see clear timezone indicators, prioritize formats with Z / z
|
|
97
|
+
if (hasLetterTZ) {
|
|
98
|
+
const withTZ = candidates.filter(f => f.includes('Z') || f.includes('z'));
|
|
99
|
+
const withoutTZ = candidates.filter(f => !f.includes('Z') && !f.includes('z'));
|
|
100
|
+
const reordered = [...withTZ, ...withoutTZ];
|
|
101
|
+
const fmt = tryFormats(reordered);
|
|
102
|
+
if (fmt) {
|
|
103
|
+
return fmt;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const fmt = tryFormats(candidates);
|
|
108
|
+
if (fmt) {
|
|
109
|
+
return fmt;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// --- Last resort: native JS parsing ---
|
|
113
|
+
const jsDate = new Date(str);
|
|
114
|
+
if (!isNaN(jsDate.getTime())) {
|
|
115
|
+
// You can treat "native" as a special keyword meaning:
|
|
116
|
+
// "use Date/Temporal to parse directly".
|
|
117
|
+
return 'native';
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
package/dist/dt.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import parse from './dt/functions/parse.js';
|
|
2
|
+
import guessFormat from './dt/functions/guessFormat.js';
|
|
2
3
|
declare const dt: {
|
|
3
4
|
(value: any, inputFormat?: null | String): void;
|
|
4
5
|
locales: any;
|
|
5
6
|
parse: typeof parse;
|
|
7
|
+
guessFormat: typeof guessFormat;
|
|
6
8
|
time(): void;
|
|
7
9
|
date(): void;
|
|
8
10
|
dateTime(): void;
|
package/dist/dt.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import parse from './dt/functions/parse.js';
|
|
2
|
+
import guessFormat from './dt/functions/guessFormat.js';
|
|
2
3
|
const patterns = [
|
|
3
4
|
// MariaDB DATETIME "YYYY-MM-DD HH:MM:SS"
|
|
4
5
|
{
|
|
@@ -191,6 +192,7 @@ const dt = (value, inputFormat = null) => {
|
|
|
191
192
|
};
|
|
192
193
|
dt.locales = Object.create(null);
|
|
193
194
|
dt.parse = parse;
|
|
195
|
+
dt.guessFormat = guessFormat;
|
|
194
196
|
dt.time = () => { };
|
|
195
197
|
dt.date = () => { };
|
|
196
198
|
dt.dateTime = () => { };
|