kyd-shared-badge 0.3.18 → 0.3.19
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/package.json +1 -1
- package/src/utils/date.ts +68 -0
package/package.json
CHANGED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export type DateLike = string | number | Date | null | undefined;
|
|
2
|
+
|
|
3
|
+
const hasTimezoneDesignator = (value: string): boolean => {
|
|
4
|
+
return /[zZ]$/.test(value) || /[+-]\d{2}:?\d{2}$/.test(value);
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const truncateExcessFraction = (value: string): string => {
|
|
8
|
+
// Keep at most 3 fractional digits for milliseconds
|
|
9
|
+
return value.replace(/(\.\d{3})\d+/, '$1');
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const parseUtcDate = (input: DateLike): Date => {
|
|
13
|
+
if (input == null) return new Date(Number.NaN);
|
|
14
|
+
if (input instanceof Date) return new Date(input.getTime());
|
|
15
|
+
if (typeof input === 'number') {
|
|
16
|
+
const millis = input < 1e12 ? input * 1000 : input;
|
|
17
|
+
return new Date(millis);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const raw = String(input).trim();
|
|
21
|
+
if (!raw) return new Date(Number.NaN);
|
|
22
|
+
|
|
23
|
+
// Numeric strings (epoch seconds or millis)
|
|
24
|
+
if (/^\d{10}$/.test(raw)) return new Date(Number(raw) * 1000);
|
|
25
|
+
if (/^\d{13}$/.test(raw)) return new Date(Number(raw));
|
|
26
|
+
|
|
27
|
+
// Already has timezone; trust the browser parser
|
|
28
|
+
if (hasTimezoneDesignator(raw)) return new Date(raw);
|
|
29
|
+
|
|
30
|
+
// Normalize common forms: "YYYY-MM-DD HH:MM:SS[.fff...]" -> ISO-like
|
|
31
|
+
let norm = raw.replace(' ', 'T');
|
|
32
|
+
norm = truncateExcessFraction(norm);
|
|
33
|
+
|
|
34
|
+
// Date-only string -> treat as UTC midnight
|
|
35
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(norm)) {
|
|
36
|
+
return new Date(`${norm}T00:00:00Z`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// If ISO-like without timezone, explicitly mark as UTC
|
|
40
|
+
if (/^\d{4}-\d{2}-\d{2}T/.test(norm) && !hasTimezoneDesignator(norm)) {
|
|
41
|
+
return new Date(`${norm}Z`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fallback to native parsing
|
|
45
|
+
return new Date(norm);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const formatLocalDate = (value: DateLike, options?: Intl.DateTimeFormatOptions): string => {
|
|
49
|
+
const d = value instanceof Date ? value : parseUtcDate(value);
|
|
50
|
+
if (Number.isNaN(d.getTime())) return 'N/A';
|
|
51
|
+
return d.toLocaleDateString(undefined, options);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const formatLocalDateTime = (value: DateLike, options?: Intl.DateTimeFormatOptions): string => {
|
|
55
|
+
const d = value instanceof Date ? value : parseUtcDate(value);
|
|
56
|
+
if (Number.isNaN(d.getTime())) return 'N/A';
|
|
57
|
+
const base: Intl.DateTimeFormatOptions = {
|
|
58
|
+
year: 'numeric',
|
|
59
|
+
month: 'long',
|
|
60
|
+
day: 'numeric',
|
|
61
|
+
hour: 'numeric',
|
|
62
|
+
minute: '2-digit',
|
|
63
|
+
timeZoneName: 'short',
|
|
64
|
+
};
|
|
65
|
+
return d.toLocaleString(undefined, { ...base, ...(options || {}) });
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
|