mui-temporal-pickers 1.0.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/LICENSE +22 -0
- package/README.md +130 -0
- package/dist/adapters/adapter-formats.d.ts +2 -0
- package/dist/adapters/adapter-formats.js +25 -0
- package/dist/adapters/base.d.ts +97 -0
- package/dist/adapters/base.js +216 -0
- package/dist/adapters/operations.d.ts +70 -0
- package/dist/adapters/operations.js +120 -0
- package/dist/adapters/plain-date-time.d.ts +12 -0
- package/dist/adapters/plain-date-time.js +46 -0
- package/dist/adapters/plain-date.d.ts +6 -0
- package/dist/adapters/plain-date.js +46 -0
- package/dist/adapters/plain-time.d.ts +6 -0
- package/dist/adapters/plain-time.js +46 -0
- package/dist/adapters/zoned-date-time.d.ts +12 -0
- package/dist/adapters/zoned-date-time.js +55 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/locale/format/formatter.d.ts +12 -0
- package/dist/locale/format/formatter.js +377 -0
- package/dist/locale/format/tokenizer.d.ts +9 -0
- package/dist/locale/format/tokenizer.js +83 -0
- package/dist/locale/format/tokens.d.ts +8 -0
- package/dist/locale/format/tokens.js +70 -0
- package/dist/locale/specs.d.ts +9 -0
- package/dist/locale/specs.js +30 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.js +5 -0
- package/dist/providers/plain-date-time.d.ts +5 -0
- package/dist/providers/plain-date-time.js +8 -0
- package/dist/providers/plain-date.d.ts +5 -0
- package/dist/providers/plain-date.js +8 -0
- package/dist/providers/plain-time.d.ts +5 -0
- package/dist/providers/plain-time.js +8 -0
- package/dist/providers/root.d.ts +13 -0
- package/dist/providers/root.js +7 -0
- package/dist/providers/zoned-date-time.d.ts +5 -0
- package/dist/providers/zoned-date-time.js +8 -0
- package/package.json +72 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AdapterOptions } from "@mui/x-date-pickers";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
export declare class AdapterTemporalPlainDateTime extends AdapterTemporalBase<Temporal.PlainDateTime> {
|
|
4
|
+
readonly formatTokenMap: {
|
|
5
|
+
[x: string]: import("@mui/x-date-pickers").FieldSectionType | {
|
|
6
|
+
sectionType: import("@mui/x-date-pickers").FieldSectionType;
|
|
7
|
+
contentType: import("@mui/x-date-pickers").FieldSectionContentType;
|
|
8
|
+
maxLength?: number;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
constructor({ locale, formats, }: AdapterOptions<Intl.Locale | string, never>);
|
|
12
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { dateFormatTokenMap, timeFormatTokenMap } from "../locale/format/tokens.js";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
import { defaultAdapterDateOperations, defaultAdapterTimeOperations, } from "./operations.js";
|
|
4
|
+
const conversionOperations = {
|
|
5
|
+
date: (value) => {
|
|
6
|
+
if (value === null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
if (!value) {
|
|
10
|
+
return Temporal.Now.plainDateTimeISO();
|
|
11
|
+
}
|
|
12
|
+
return Temporal.PlainDateTime.from(value);
|
|
13
|
+
},
|
|
14
|
+
toJsDate: (value) => {
|
|
15
|
+
return new Date(value.toZonedDateTime("UTC").epochMilliseconds);
|
|
16
|
+
},
|
|
17
|
+
parse: (value, format, localeSpecs) => {
|
|
18
|
+
return localeSpecs.formatter.parsePlainDateTime(value, format);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
const comparisonOperations = {
|
|
22
|
+
isEqual: (value, comparing) => value.equals(comparing),
|
|
23
|
+
isSameYear: (value, comparing) => value.year === comparing.year,
|
|
24
|
+
isSameMonth: (value, comparing) => value.toPlainDate().toPlainYearMonth().equals(comparing.toPlainDate().toPlainYearMonth()),
|
|
25
|
+
isSameDay: (value, comparing) => value.equals(comparing),
|
|
26
|
+
isSameHour: (value, comparing) => value.toPlainDate().equals(comparing.toPlainDate()) && value.hour === comparing.hour,
|
|
27
|
+
isAfter: (value, comparing) => Temporal.PlainDate.compare(value, comparing) > 0,
|
|
28
|
+
isAfterYear: (value, comparing) => value.year > comparing.year,
|
|
29
|
+
isAfterDay: (value, comparing) => Temporal.PlainDate.compare(value, comparing) > 0,
|
|
30
|
+
isBefore: (value, comparing) => Temporal.PlainDate.compare(value, comparing) < 0,
|
|
31
|
+
isBeforeYear: (value, comparing) => value.year < comparing.year,
|
|
32
|
+
isBeforeDay: (value, comparing) => Temporal.PlainDate.compare(value, comparing) < 0,
|
|
33
|
+
};
|
|
34
|
+
export class AdapterTemporalPlainDateTime extends AdapterTemporalBase {
|
|
35
|
+
formatTokenMap = { ...timeFormatTokenMap, ...dateFormatTokenMap };
|
|
36
|
+
constructor({ locale = new Intl.Locale("en-US"), formats, }) {
|
|
37
|
+
super({
|
|
38
|
+
locale,
|
|
39
|
+
formats,
|
|
40
|
+
conversionOperations,
|
|
41
|
+
comparisonOperations,
|
|
42
|
+
dateOperations: defaultAdapterDateOperations,
|
|
43
|
+
timeOperations: defaultAdapterTimeOperations,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AdapterOptions } from "@mui/x-date-pickers";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
export declare class AdapterTemporalPlainDate extends AdapterTemporalBase<Temporal.PlainDate> {
|
|
4
|
+
readonly formatTokenMap: import("@mui/x-date-pickers").FieldFormatTokenMap;
|
|
5
|
+
constructor({ locale, formats, }: AdapterOptions<Intl.Locale | string, never>);
|
|
6
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { dateFormatTokenMap } from "../locale/format/tokens.js";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
import { defaultAdapterDateOperations, } from "./operations.js";
|
|
4
|
+
const conversionOperations = {
|
|
5
|
+
date: (value) => {
|
|
6
|
+
if (value === null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
if (!value) {
|
|
10
|
+
return Temporal.Now.plainDateISO();
|
|
11
|
+
}
|
|
12
|
+
return Temporal.PlainDate.from(value);
|
|
13
|
+
},
|
|
14
|
+
toJsDate: (value) => {
|
|
15
|
+
return new Date(value.toPlainDateTime(Temporal.PlainTime.from({ hour: 0 })).toZonedDateTime("UTC")
|
|
16
|
+
.epochMilliseconds);
|
|
17
|
+
},
|
|
18
|
+
parse: (value, format, localeSpecs) => {
|
|
19
|
+
return localeSpecs.formatter.parsePlainDate(value, format);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const comparisonOperations = {
|
|
23
|
+
isEqual: (value, comparing) => value.equals(comparing),
|
|
24
|
+
isSameYear: (value, comparing) => value.year === comparing.year,
|
|
25
|
+
isSameMonth: (value, comparing) => value.toPlainYearMonth().equals(comparing.toPlainYearMonth()),
|
|
26
|
+
isSameDay: (value, comparing) => value.equals(comparing),
|
|
27
|
+
isSameHour: () => true,
|
|
28
|
+
isAfter: (value, comparing) => Temporal.PlainDate.compare(value, comparing) > 0,
|
|
29
|
+
isAfterYear: (value, comparing) => value.year > comparing.year,
|
|
30
|
+
isAfterDay: (value, comparing) => Temporal.PlainDate.compare(value, comparing) > 0,
|
|
31
|
+
isBefore: (value, comparing) => Temporal.PlainDate.compare(value, comparing) < 0,
|
|
32
|
+
isBeforeYear: (value, comparing) => value.year < comparing.year,
|
|
33
|
+
isBeforeDay: (value, comparing) => Temporal.PlainDate.compare(value, comparing) < 0,
|
|
34
|
+
};
|
|
35
|
+
export class AdapterTemporalPlainDate extends AdapterTemporalBase {
|
|
36
|
+
formatTokenMap = dateFormatTokenMap;
|
|
37
|
+
constructor({ locale = new Intl.Locale("en-US"), formats, }) {
|
|
38
|
+
super({
|
|
39
|
+
locale,
|
|
40
|
+
formats,
|
|
41
|
+
conversionOperations,
|
|
42
|
+
comparisonOperations,
|
|
43
|
+
dateOperations: defaultAdapterDateOperations,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AdapterOptions } from "@mui/x-date-pickers";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
export declare class AdapterTemporalPlainTime extends AdapterTemporalBase<Temporal.PlainTime> {
|
|
4
|
+
readonly formatTokenMap: import("@mui/x-date-pickers").FieldFormatTokenMap;
|
|
5
|
+
constructor({ locale, formats, }: AdapterOptions<Intl.Locale | string, never>);
|
|
6
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { timeFormatTokenMap } from "../locale/format/tokens.js";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
import { defaultAdapterTimeOperations, } from "./operations.js";
|
|
4
|
+
const conversionOperations = {
|
|
5
|
+
date: (value) => {
|
|
6
|
+
if (value === null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
if (!value) {
|
|
10
|
+
return Temporal.Now.plainTimeISO();
|
|
11
|
+
}
|
|
12
|
+
return Temporal.PlainTime.from(value);
|
|
13
|
+
},
|
|
14
|
+
toJsDate: (value) => {
|
|
15
|
+
return new Date(Temporal.PlainDate.from("2000-01-01").toPlainDateTime(value).toZonedDateTime("UTC")
|
|
16
|
+
.epochMilliseconds);
|
|
17
|
+
},
|
|
18
|
+
parse: (value, format, localeSpecs) => {
|
|
19
|
+
return localeSpecs.formatter.parsePlainTime(value, format);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
const comparisonOperations = {
|
|
23
|
+
isEqual: (value, comparing) => value.equals(comparing),
|
|
24
|
+
isSameYear: () => true,
|
|
25
|
+
isSameMonth: () => true,
|
|
26
|
+
isSameDay: () => true,
|
|
27
|
+
isSameHour: (value, comparing) => value.hour === comparing.hour,
|
|
28
|
+
isAfter: (value, comparing) => Temporal.PlainTime.compare(value, comparing) > 0,
|
|
29
|
+
isAfterYear: () => false,
|
|
30
|
+
isAfterDay: () => false,
|
|
31
|
+
isBefore: (value, comparing) => Temporal.PlainTime.compare(value, comparing) < 0,
|
|
32
|
+
isBeforeYear: () => false,
|
|
33
|
+
isBeforeDay: () => false,
|
|
34
|
+
};
|
|
35
|
+
export class AdapterTemporalPlainTime extends AdapterTemporalBase {
|
|
36
|
+
formatTokenMap = timeFormatTokenMap;
|
|
37
|
+
constructor({ locale = new Intl.Locale("en-US"), formats, }) {
|
|
38
|
+
super({
|
|
39
|
+
locale,
|
|
40
|
+
formats,
|
|
41
|
+
conversionOperations,
|
|
42
|
+
comparisonOperations,
|
|
43
|
+
timeOperations: defaultAdapterTimeOperations,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AdapterOptions } from "@mui/x-date-pickers";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
export declare class AdapterTemporalZonedDateTime extends AdapterTemporalBase<Temporal.ZonedDateTime> {
|
|
4
|
+
readonly formatTokenMap: {
|
|
5
|
+
[x: string]: import("@mui/x-date-pickers").FieldSectionType | {
|
|
6
|
+
sectionType: import("@mui/x-date-pickers").FieldSectionType;
|
|
7
|
+
contentType: import("@mui/x-date-pickers").FieldSectionContentType;
|
|
8
|
+
maxLength?: number;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
constructor({ locale, formats, }: AdapterOptions<Intl.Locale | string, never>);
|
|
12
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { dateFormatTokenMap, timeFormatTokenMap } from "../locale/format/tokens.js";
|
|
2
|
+
import { AdapterTemporalBase } from "./base.js";
|
|
3
|
+
import { defaultAdapterDateOperations, defaultAdapterTimeOperations, resolveTimeZoneId, } from "./operations.js";
|
|
4
|
+
const conversionOperations = {
|
|
5
|
+
date: (value, timezone) => {
|
|
6
|
+
if (value === null) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const plainDateTime = !value
|
|
10
|
+
? Temporal.Now.plainDateTimeISO()
|
|
11
|
+
: Temporal.PlainDateTime.from(value);
|
|
12
|
+
return plainDateTime.toZonedDateTime(resolveTimeZoneId(timezone));
|
|
13
|
+
},
|
|
14
|
+
toJsDate: (value) => {
|
|
15
|
+
return new Date(value.epochMilliseconds);
|
|
16
|
+
},
|
|
17
|
+
parse: (value, format, localeSpecs) => {
|
|
18
|
+
return (localeSpecs.formatter
|
|
19
|
+
.parsePlainDateTime(value, format)
|
|
20
|
+
?.toZonedDateTime(Temporal.Now.timeZoneId()) ?? null);
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const comparisonOperations = {
|
|
24
|
+
isEqual: (value, comparing) => value.equals(comparing.withTimeZone(value)),
|
|
25
|
+
isSameYear: (value, comparing) => value.year === comparing.withTimeZone(value).year,
|
|
26
|
+
isSameMonth: (value, comparing) => value
|
|
27
|
+
.toPlainDate()
|
|
28
|
+
.toPlainYearMonth()
|
|
29
|
+
.equals(comparing.withTimeZone(value).toPlainDate().toPlainYearMonth()),
|
|
30
|
+
isSameDay: (value, comparing) => value.equals(comparing.withTimeZone(value)),
|
|
31
|
+
isSameHour: (value, comparing) => {
|
|
32
|
+
const comparingSameZone = comparing.withTimeZone(value);
|
|
33
|
+
return (value.toPlainDate().equals(comparingSameZone.toPlainDate()) &&
|
|
34
|
+
value.hour === comparingSameZone.hour);
|
|
35
|
+
},
|
|
36
|
+
isAfter: (value, comparing) => Temporal.PlainDate.compare(value, comparing.withTimeZone(value)) > 0,
|
|
37
|
+
isAfterYear: (value, comparing) => value.year > comparing.withTimeZone(value).year,
|
|
38
|
+
isAfterDay: (value, comparing) => Temporal.PlainDate.compare(value, comparing.withTimeZone(value)) > 0,
|
|
39
|
+
isBefore: (value, comparing) => Temporal.PlainDate.compare(value, comparing.withTimeZone(value)) < 0,
|
|
40
|
+
isBeforeYear: (value, comparing) => value.year < comparing.withTimeZone(value).year,
|
|
41
|
+
isBeforeDay: (value, comparing) => Temporal.PlainDate.compare(value, comparing.withTimeZone(value)) < 0,
|
|
42
|
+
};
|
|
43
|
+
export class AdapterTemporalZonedDateTime extends AdapterTemporalBase {
|
|
44
|
+
formatTokenMap = { ...timeFormatTokenMap, ...dateFormatTokenMap };
|
|
45
|
+
constructor({ locale = new Intl.Locale("en-US"), formats, }) {
|
|
46
|
+
super({
|
|
47
|
+
locale,
|
|
48
|
+
formats,
|
|
49
|
+
conversionOperations,
|
|
50
|
+
comparisonOperations,
|
|
51
|
+
dateOperations: defaultAdapterDateOperations,
|
|
52
|
+
timeOperations: defaultAdapterTimeOperations,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./providers/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./providers/index.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type FormattableValue = Temporal.PlainTime | Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime;
|
|
2
|
+
export declare class Formatter {
|
|
3
|
+
private locale;
|
|
4
|
+
private formats;
|
|
5
|
+
constructor(locale: Intl.Locale);
|
|
6
|
+
format(value: FormattableValue, format: string): string;
|
|
7
|
+
parsePlainTime(value: string, format: string): Temporal.PlainTime | null;
|
|
8
|
+
parsePlainDate(value: string, format: string): Temporal.PlainDate | null;
|
|
9
|
+
parsePlainDateTime(value: string, format: string): Temporal.PlainDateTime | null;
|
|
10
|
+
expandFormat(format: string): string;
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { parseTokenString } from "./tokenizer.js";
|
|
2
|
+
import { knownTokens, metaTokens } from "./tokens.js";
|
|
3
|
+
const tokenFormatOptions = {
|
|
4
|
+
// Standard tokens
|
|
5
|
+
MMM: { month: "short" },
|
|
6
|
+
MMMM: { month: "long" },
|
|
7
|
+
ccc: { weekday: "short" },
|
|
8
|
+
cccc: { weekday: "long" },
|
|
9
|
+
ccccc: { weekday: "narrow" },
|
|
10
|
+
// Meta tokens
|
|
11
|
+
lfd: { day: "numeric", month: "short", year: "numeric" },
|
|
12
|
+
lkd: { year: "numeric", month: "2-digit", day: "2-digit" },
|
|
13
|
+
lsd: { day: "numeric", month: "short" },
|
|
14
|
+
lnd: { day: "numeric", month: "long" },
|
|
15
|
+
lndw: { weekday: "short", day: "numeric", month: "short" },
|
|
16
|
+
lfta: { hour: "2-digit", minute: "2-digit", hourCycle: "h11" },
|
|
17
|
+
lftd: { hour: "2-digit", minute: "2-digit", hourCycle: "h23" },
|
|
18
|
+
lkdta: {
|
|
19
|
+
year: "numeric",
|
|
20
|
+
month: "2-digit",
|
|
21
|
+
day: "2-digit",
|
|
22
|
+
hour: "2-digit",
|
|
23
|
+
minute: "2-digit",
|
|
24
|
+
hourCycle: "h12",
|
|
25
|
+
},
|
|
26
|
+
lkdtd: {
|
|
27
|
+
year: "numeric",
|
|
28
|
+
month: "2-digit",
|
|
29
|
+
day: "2-digit",
|
|
30
|
+
hour: "2-digit",
|
|
31
|
+
minute: "2-digit",
|
|
32
|
+
hourCycle: "h23",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const tokenToFieldMap = {
|
|
36
|
+
y: "year",
|
|
37
|
+
yy: "year",
|
|
38
|
+
M: "month",
|
|
39
|
+
MM: "month",
|
|
40
|
+
d: "day",
|
|
41
|
+
dd: "day",
|
|
42
|
+
H: "hour24",
|
|
43
|
+
HH: "hour24",
|
|
44
|
+
h: "hour12",
|
|
45
|
+
hh: "hour12",
|
|
46
|
+
m: "minute",
|
|
47
|
+
mm: "minute",
|
|
48
|
+
s: "second",
|
|
49
|
+
ss: "second",
|
|
50
|
+
a: "ampm",
|
|
51
|
+
};
|
|
52
|
+
const tokenRegexMap = {
|
|
53
|
+
yy: "\\d{1,4}",
|
|
54
|
+
yyyy: "\\d{2}",
|
|
55
|
+
M: "\\d{1,2}",
|
|
56
|
+
MM: "\\d{2}",
|
|
57
|
+
d: "\\d{1,2}",
|
|
58
|
+
dd: "\\d{2}",
|
|
59
|
+
H: "\\d{1,2}",
|
|
60
|
+
HH: "\\d{2}",
|
|
61
|
+
h: "\\d{1,2}",
|
|
62
|
+
hh: "\\d{2}",
|
|
63
|
+
m: "\\d{1,2}",
|
|
64
|
+
mm: "\\d{2}",
|
|
65
|
+
s: "\\d{1,2}",
|
|
66
|
+
ss: "\\d{2}",
|
|
67
|
+
a: "(AM|PM)",
|
|
68
|
+
};
|
|
69
|
+
export class Formatter {
|
|
70
|
+
locale;
|
|
71
|
+
formats;
|
|
72
|
+
constructor(locale) {
|
|
73
|
+
const dateTimeFormatter = new Intl.DateTimeFormat(locale);
|
|
74
|
+
if (dateTimeFormatter.resolvedOptions().numberingSystem !== "latn") {
|
|
75
|
+
throw new Error("Only latin numbering system is supported");
|
|
76
|
+
}
|
|
77
|
+
this.locale = locale;
|
|
78
|
+
this.formats = collectFormats(locale);
|
|
79
|
+
}
|
|
80
|
+
format(value, format) {
|
|
81
|
+
const formattable = value instanceof Temporal.ZonedDateTime ? value.toPlainDateTime() : value;
|
|
82
|
+
const tokens = parseTokenString(format);
|
|
83
|
+
let result = "";
|
|
84
|
+
for (const token of tokens) {
|
|
85
|
+
if (token.type === "literal") {
|
|
86
|
+
result += token.value;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const expandedToken = expandToken(token.value, value);
|
|
90
|
+
if (expandedToken) {
|
|
91
|
+
result += expandedToken;
|
|
92
|
+
}
|
|
93
|
+
else if (token.value in tokenFormatOptions) {
|
|
94
|
+
result += new Intl.DateTimeFormat(this.locale, tokenFormatOptions[token.value]).format(formattable);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
parsePlainTime(value, format) {
|
|
100
|
+
const tokens = parseTokenString(format);
|
|
101
|
+
const components = parseInputFromFormat(value, tokens);
|
|
102
|
+
if (!components) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
return Temporal.PlainTime.from({
|
|
106
|
+
hour: to24Hours(components),
|
|
107
|
+
minute: components.minute,
|
|
108
|
+
second: components.second,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
parsePlainDate(value, format) {
|
|
112
|
+
const tokens = parseTokenString(format);
|
|
113
|
+
const components = parseInputFromFormat(value, tokens);
|
|
114
|
+
if (!components) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return Temporal.PlainDate.from({
|
|
118
|
+
year: components.year ?? 2000,
|
|
119
|
+
month: components.month ?? 1,
|
|
120
|
+
day: components.day ?? 1,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
parsePlainDateTime(value, format) {
|
|
124
|
+
const tokens = parseTokenString(format);
|
|
125
|
+
const components = parseInputFromFormat(value, tokens);
|
|
126
|
+
if (!components) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
return Temporal.PlainDateTime.from({
|
|
130
|
+
year: components.year ?? 2000,
|
|
131
|
+
month: components.month ?? 1,
|
|
132
|
+
day: components.day ?? 1,
|
|
133
|
+
hour: to24Hours(components),
|
|
134
|
+
minute: components.minute,
|
|
135
|
+
second: components.second,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
expandFormat(format) {
|
|
139
|
+
const tokens = parseTokenString(format);
|
|
140
|
+
let result = "";
|
|
141
|
+
for (const token of tokens) {
|
|
142
|
+
if (token.type === "literal") {
|
|
143
|
+
result += escapeLiteral(token.value);
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
switch (token.value) {
|
|
147
|
+
case "lkd":
|
|
148
|
+
result += this.formats.keyboardDate;
|
|
149
|
+
break;
|
|
150
|
+
case "lfta":
|
|
151
|
+
result += this.formats.fullTime12h;
|
|
152
|
+
break;
|
|
153
|
+
case "lftd":
|
|
154
|
+
result += this.formats.fullTime24h;
|
|
155
|
+
break;
|
|
156
|
+
case "lkdta":
|
|
157
|
+
result += this.formats.keyboardDateTime12h;
|
|
158
|
+
break;
|
|
159
|
+
case "lkdtd":
|
|
160
|
+
result += this.formats.keyboardDateTime24h;
|
|
161
|
+
break;
|
|
162
|
+
case "lfd":
|
|
163
|
+
case "lsd":
|
|
164
|
+
case "lnd":
|
|
165
|
+
case "lndw":
|
|
166
|
+
throw new Error(`Format token '${token.value}' cannot be expanded`);
|
|
167
|
+
default:
|
|
168
|
+
result += token.value;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const expandToken = (token, value) => {
|
|
175
|
+
if (value instanceof Temporal.PlainDate ||
|
|
176
|
+
value instanceof Temporal.PlainDateTime ||
|
|
177
|
+
value instanceof Temporal.ZonedDateTime) {
|
|
178
|
+
switch (token) {
|
|
179
|
+
case "yy":
|
|
180
|
+
return value.toString().slice(-2);
|
|
181
|
+
case "yyyy":
|
|
182
|
+
return value.year.toString();
|
|
183
|
+
case "M":
|
|
184
|
+
return value.month.toString();
|
|
185
|
+
case "MM":
|
|
186
|
+
return value.month.toString().padStart(2, "0");
|
|
187
|
+
case "d":
|
|
188
|
+
return value.day.toString();
|
|
189
|
+
case "dd":
|
|
190
|
+
return value.day.toString().padStart(2, "0");
|
|
191
|
+
case "MMM":
|
|
192
|
+
case "MMMM":
|
|
193
|
+
case "ccc":
|
|
194
|
+
case "cccc":
|
|
195
|
+
case "ccccc":
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (value instanceof Temporal.PlainTime ||
|
|
200
|
+
value instanceof Temporal.PlainDateTime ||
|
|
201
|
+
value instanceof Temporal.ZonedDateTime) {
|
|
202
|
+
switch (token) {
|
|
203
|
+
case "a":
|
|
204
|
+
return value.hour < 12 ? "AM" : "PM";
|
|
205
|
+
case "H":
|
|
206
|
+
return value.hour.toString();
|
|
207
|
+
case "HH":
|
|
208
|
+
return value.hour.toString().padStart(2, "0");
|
|
209
|
+
case "h":
|
|
210
|
+
return to12Hours(value.hour).toString();
|
|
211
|
+
case "hh":
|
|
212
|
+
return to12Hours(value.hour).toString().padStart(2, "0");
|
|
213
|
+
case "m":
|
|
214
|
+
return value.minute.toString();
|
|
215
|
+
case "mm":
|
|
216
|
+
return value.minute.toString().padStart(2, "0");
|
|
217
|
+
case "s":
|
|
218
|
+
return value.second.toString();
|
|
219
|
+
case "ss":
|
|
220
|
+
return value.second.toString().padStart(2, "0");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (metaTokens.includes(token)) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
// MUI-X tries to format dates as times, even in the Date Picker; just ignore.
|
|
227
|
+
return "";
|
|
228
|
+
};
|
|
229
|
+
const to24Hours = (components) => {
|
|
230
|
+
if (components.hour24) {
|
|
231
|
+
return components.hour24;
|
|
232
|
+
}
|
|
233
|
+
if (!components.hour12) {
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
if (!components.ampm) {
|
|
237
|
+
throw new Error("Format is missing AM/PM");
|
|
238
|
+
}
|
|
239
|
+
if (components.ampm === "AM") {
|
|
240
|
+
return components.hour12 === 12 ? 0 : components.hour12;
|
|
241
|
+
}
|
|
242
|
+
return components.hour12 === 12 ? 12 : components.hour12 + 12;
|
|
243
|
+
};
|
|
244
|
+
const to12Hours = (hour24) => {
|
|
245
|
+
return hour24 % 12 === 0 ? 12 : hour24 % 12;
|
|
246
|
+
};
|
|
247
|
+
const buildRegexFromFormat = (tokens) => {
|
|
248
|
+
let pattern = "^";
|
|
249
|
+
const groupMap = [];
|
|
250
|
+
for (const token of tokens) {
|
|
251
|
+
if (token.type === "literal") {
|
|
252
|
+
pattern += token.value.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
if (!tokenRegexMap[token.value]) {
|
|
256
|
+
throw new Error(`Unsupported token: "${token.value}"`);
|
|
257
|
+
}
|
|
258
|
+
pattern += `(${tokenRegexMap[token.value]})`;
|
|
259
|
+
groupMap.push(token.value);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
pattern += "$";
|
|
263
|
+
return {
|
|
264
|
+
regex: new RegExp(pattern),
|
|
265
|
+
groupMap,
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
const parseInputFromFormat = (input, format) => {
|
|
269
|
+
const { regex, groupMap } = buildRegexFromFormat(format);
|
|
270
|
+
const match = input.match(regex);
|
|
271
|
+
if (!match) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const result = {};
|
|
275
|
+
groupMap.forEach((token, index) => {
|
|
276
|
+
const rawValue = match[index + 1];
|
|
277
|
+
const field = tokenToFieldMap[token];
|
|
278
|
+
if (field === "ampm") {
|
|
279
|
+
result.ampm = rawValue;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
result[field] = Number.parseInt(rawValue, 10);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
return result;
|
|
286
|
+
};
|
|
287
|
+
const collectFormats = (locale) => ({
|
|
288
|
+
keyboardDate: collectFormat(locale, {
|
|
289
|
+
year: "numeric",
|
|
290
|
+
month: "2-digit",
|
|
291
|
+
day: "2-digit",
|
|
292
|
+
}),
|
|
293
|
+
fullTime12h: collectFormat(locale, {
|
|
294
|
+
hour: "2-digit",
|
|
295
|
+
minute: "2-digit",
|
|
296
|
+
hourCycle: "h11",
|
|
297
|
+
}),
|
|
298
|
+
fullTime24h: collectFormat(locale, {
|
|
299
|
+
hour: "2-digit",
|
|
300
|
+
minute: "2-digit",
|
|
301
|
+
hourCycle: "h23",
|
|
302
|
+
}),
|
|
303
|
+
keyboardDateTime12h: collectFormat(locale, {
|
|
304
|
+
year: "numeric",
|
|
305
|
+
month: "2-digit",
|
|
306
|
+
day: "2-digit",
|
|
307
|
+
hour: "2-digit",
|
|
308
|
+
minute: "2-digit",
|
|
309
|
+
hourCycle: "h12",
|
|
310
|
+
}),
|
|
311
|
+
keyboardDateTime24h: collectFormat(locale, {
|
|
312
|
+
year: "numeric",
|
|
313
|
+
month: "2-digit",
|
|
314
|
+
day: "2-digit",
|
|
315
|
+
hour: "2-digit",
|
|
316
|
+
minute: "2-digit",
|
|
317
|
+
hourCycle: "h23",
|
|
318
|
+
}),
|
|
319
|
+
});
|
|
320
|
+
const collectFormat = (locale, options) => {
|
|
321
|
+
const parts = new Intl.DateTimeFormat(locale, { ...options, timeZone: "utc" }).formatToParts(Temporal.PlainDateTime.from({
|
|
322
|
+
year: 2018,
|
|
323
|
+
month: 1,
|
|
324
|
+
day: 1,
|
|
325
|
+
hour: 1,
|
|
326
|
+
minute: 1,
|
|
327
|
+
second: 1,
|
|
328
|
+
}));
|
|
329
|
+
let tokens = "";
|
|
330
|
+
for (const part of parts) {
|
|
331
|
+
switch (part.type) {
|
|
332
|
+
case "day":
|
|
333
|
+
tokens += part.value.length === 1 ? "d" : "dd";
|
|
334
|
+
break;
|
|
335
|
+
case "dayPeriod":
|
|
336
|
+
tokens += "a";
|
|
337
|
+
break;
|
|
338
|
+
case "era":
|
|
339
|
+
throw new Error("timeZoneName is not supported");
|
|
340
|
+
case "hour":
|
|
341
|
+
tokens += determineHourToken(part.value, options.hourCycle);
|
|
342
|
+
break;
|
|
343
|
+
case "minute":
|
|
344
|
+
tokens += part.value.length === 1 ? "m" : "mm";
|
|
345
|
+
break;
|
|
346
|
+
case "month":
|
|
347
|
+
tokens += part.value.length === 1 ? "M" : "MM";
|
|
348
|
+
break;
|
|
349
|
+
case "second":
|
|
350
|
+
tokens += part.value.length === 1 ? "s" : "ss";
|
|
351
|
+
break;
|
|
352
|
+
case "timeZoneName":
|
|
353
|
+
throw new Error("timeZoneName is not supported");
|
|
354
|
+
case "weekday":
|
|
355
|
+
return "c";
|
|
356
|
+
case "year":
|
|
357
|
+
tokens += part.value.length === 2 ? "yy" : "yyyy";
|
|
358
|
+
break;
|
|
359
|
+
case "literal":
|
|
360
|
+
tokens += escapeLiteral(part.value);
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return tokens;
|
|
365
|
+
};
|
|
366
|
+
const determineHourToken = (value, hourCycle) => {
|
|
367
|
+
const baseToken = hourCycle?.[1] === "1" ? "h" : "H";
|
|
368
|
+
return value.length === 1 ? baseToken : baseToken.repeat(2);
|
|
369
|
+
};
|
|
370
|
+
const tokenPattern = new RegExp(knownTokens.sort((a, b) => b.length - a.length).join("|"), "g");
|
|
371
|
+
const escapeLiteral = (literal) => {
|
|
372
|
+
const doubledQuotes = literal.replace(/'/g, "''");
|
|
373
|
+
if (!tokenPattern.test(doubledQuotes)) {
|
|
374
|
+
return doubledQuotes;
|
|
375
|
+
}
|
|
376
|
+
return `'${doubledQuotes}'`;
|
|
377
|
+
};
|