|
1 | 1 | import type { DateAdapter } from "@/adapters/date-adapter"; |
2 | 2 |
|
3 | | -interface DateFnsDate { |
| 3 | +export interface ZonedDateFnsDate { |
4 | 4 | date: Date; |
5 | 5 | timeZone: string; |
6 | 6 | } |
7 | 7 |
|
| 8 | +export type DateFnsDate = Date | ZonedDateFnsDate; |
| 9 | + |
| 10 | +function isZonedDate(date: DateFnsDate): date is ZonedDateFnsDate { |
| 11 | + return ( |
| 12 | + typeof date === "object" && |
| 13 | + date !== null && |
| 14 | + "date" in date && |
| 15 | + date.date instanceof Date && |
| 16 | + typeof date.timeZone === "string" |
| 17 | + ); |
| 18 | +} |
| 19 | + |
| 20 | +function getNativeDate(date: DateFnsDate): Date { |
| 21 | + return isZonedDate(date) ? date.date : date; |
| 22 | +} |
| 23 | + |
| 24 | +function getSystemTimeZone(): string { |
| 25 | + return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC"; |
| 26 | +} |
| 27 | + |
| 28 | +function getTimeZone(date: DateFnsDate): string { |
| 29 | + return isZonedDate(date) ? date.timeZone : getSystemTimeZone(); |
| 30 | +} |
| 31 | + |
| 32 | +function cloneWithTimeZone(date: Date, timeZone: string): ZonedDateFnsDate { |
| 33 | + return { date, timeZone }; |
| 34 | +} |
| 35 | + |
| 36 | +function preserveInputShape(input: DateFnsDate, date: Date): DateFnsDate { |
| 37 | + return isZonedDate(input) ? cloneWithTimeZone(date, input.timeZone) : date; |
| 38 | +} |
| 39 | + |
8 | 40 | export async function createDateFnsAdapter(): Promise<DateAdapter<DateFnsDate>> { |
9 | 41 | let addMinutes: typeof import("date-fns").addMinutes; |
10 | 42 | let addDays: typeof import("date-fns").addDays; |
@@ -42,43 +74,27 @@ export async function createDateFnsAdapter(): Promise<DateAdapter<DateFnsDate>> |
42 | 74 | } |
43 | 75 |
|
44 | 76 | return { |
45 | | - getYear: (dateFns) => getYear(dateFns.date), |
46 | | - getMonth: (dateFns) => getMonth(dateFns.date) + 1, |
47 | | - getDay: (dateFns) => getDate(dateFns.date), |
48 | | - getHour: (dateFns) => getHours(dateFns.date), |
49 | | - getMinute: (dateFns) => getMinutes(dateFns.date), |
50 | | - getSecond: (dateFns) => getSeconds(dateFns.date), |
51 | | - getZoneName: (dateFns) => dateFns.timeZone, |
52 | | - plusMinutes: (dateFns, minutes) => ({ |
53 | | - date: addMinutes(dateFns.date, minutes), |
54 | | - timeZone: dateFns.timeZone, |
55 | | - }), |
56 | | - plusDays: (dateFns, days) => ({ |
57 | | - date: addDays(dateFns.date, days), |
58 | | - timeZone: dateFns.timeZone, |
59 | | - }), |
60 | | - minusDays: (dateFns, days) => ({ |
61 | | - date: subDays(dateFns.date, days), |
62 | | - timeZone: dateFns.timeZone, |
63 | | - }), |
64 | | - toUTC: (dateFns) => ({ |
65 | | - date: fromZonedTime(dateFns.date, dateFns.timeZone), |
66 | | - timeZone: "UTC", |
67 | | - }), |
68 | | - toISO: (dateFns) => formatISO(dateFns.date), |
69 | | - toMillis: (dateFns) => dateFns.date.getTime(), |
70 | | - fromMillis: (millis, zone) => ({ |
71 | | - date: new Date(millis), |
72 | | - timeZone: zone, |
73 | | - }), |
74 | | - createUTC: (year, month, day, hour, minute, second) => ({ |
75 | | - date: new Date(Date.UTC(year, month - 1, day, hour, minute, second)), |
76 | | - timeZone: "UTC", |
77 | | - }), |
78 | | - setZone: (dateFns, zoneName) => ({ |
79 | | - date: toZonedTime(dateFns.date, zoneName), |
80 | | - timeZone: zoneName, |
81 | | - }), |
82 | | - isGreaterThanOrEqual: (date1, date2) => date1.date >= date2.date, |
| 77 | + getYear: (dateFns) => getYear(getNativeDate(dateFns)), |
| 78 | + getMonth: (dateFns) => getMonth(getNativeDate(dateFns)) + 1, |
| 79 | + getDay: (dateFns) => getDate(getNativeDate(dateFns)), |
| 80 | + getHour: (dateFns) => getHours(getNativeDate(dateFns)), |
| 81 | + getMinute: (dateFns) => getMinutes(getNativeDate(dateFns)), |
| 82 | + getSecond: (dateFns) => getSeconds(getNativeDate(dateFns)), |
| 83 | + getZoneName: (dateFns) => getTimeZone(dateFns), |
| 84 | + plusMinutes: (dateFns, minutes) => |
| 85 | + preserveInputShape(dateFns, addMinutes(getNativeDate(dateFns), minutes)), |
| 86 | + plusDays: (dateFns, days) => preserveInputShape(dateFns, addDays(getNativeDate(dateFns), days)), |
| 87 | + minusDays: (dateFns, days) => |
| 88 | + preserveInputShape(dateFns, subDays(getNativeDate(dateFns), days)), |
| 89 | + toUTC: (dateFns) => |
| 90 | + cloneWithTimeZone(fromZonedTime(getNativeDate(dateFns), getTimeZone(dateFns)), "UTC"), |
| 91 | + toISO: (dateFns) => formatISO(getNativeDate(dateFns)), |
| 92 | + toMillis: (dateFns) => getNativeDate(dateFns).getTime(), |
| 93 | + fromMillis: (millis, zone) => cloneWithTimeZone(new Date(millis), zone), |
| 94 | + createUTC: (year, month, day, hour, minute, second) => |
| 95 | + cloneWithTimeZone(new Date(Date.UTC(year, month - 1, day, hour, minute, second)), "UTC"), |
| 96 | + setZone: (dateFns, zoneName) => |
| 97 | + cloneWithTimeZone(toZonedTime(getNativeDate(dateFns), zoneName), zoneName), |
| 98 | + isGreaterThanOrEqual: (date1, date2) => getNativeDate(date1) >= getNativeDate(date2), |
83 | 99 | }; |
84 | 100 | } |
0 commit comments