tangled
alpha
login
or
join now
timtinkers.online
/
lemoncalendar
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
UTC time zone fix in SVG?
timtinkers.online
1 year ago
5a2d53de
f3141223
+63
-13
3 changed files
expand all
collapse all
unified
split
components
WeeklySchedule.tsx
routes
api
fetch-calendar-text.ts
utils
calendarUtils.ts
+11
-1
components/WeeklySchedule.tsx
···
1
1
import satori from "https://esm.sh/satori@0.12.1";
2
2
import format from "https://deno.land/x/date_fns@v2.22.1/format/index.js";
3
3
import { Event } from "../utils/calendarUtils.ts";
4
4
+
import { toZonedTime } from "npm:date-fns-tz";
4
5
5
6
// Define theme interface for styling options
6
7
interface Theme {
···
307
308
color: theme.eventTextColor,
308
309
}}
309
310
>
310
310
-
{hasEvent ? format(event.start, "ha", []) : ""}
311
311
+
{hasEvent
312
312
+
? format(
313
313
+
toZonedTime(
314
314
+
event.start,
315
315
+
event.timezone!,
316
316
+
),
317
317
+
"ha",
318
318
+
[],
319
319
+
)
320
320
+
: ""}
311
321
</div>
312
322
</div>
313
323
);
+11
-4
routes/api/fetch-calendar-text.ts
···
1
1
import { Handlers } from "$fresh/server.ts";
2
2
import { processCalendarRequest } from "../../utils/calendarUtils.ts";
3
3
+
import { toZonedTime } from "npm:date-fns-tz";
3
4
4
5
export const handler: Handlers = {
5
6
async GET(req) {
···
10
11
11
12
let formattedText = "";
12
13
for (const event of events) {
13
13
-
const startDate = new Date(event.start);
14
14
+
const startDate = toZonedTime(
15
15
+
new Date(event.start),
16
16
+
event.timezone!,
17
17
+
);
14
18
const unixTimestamp = Math.floor(startDate.getTime() / 1000);
15
19
formattedText +=
16
20
`\n- <t:${unixTimestamp}:F> | **${event.summary}**`;
···
19
23
}
20
24
}
21
25
22
22
-
return new Response(JSON.stringify({ result: formattedText.trim() }), {
23
23
-
headers: { "Content-Type": "application/json" },
24
24
-
});
26
26
+
return new Response(
27
27
+
JSON.stringify({ result: formattedText.trim() }),
28
28
+
{
29
29
+
headers: { "Content-Type": "application/json" },
30
30
+
},
31
31
+
);
25
32
} catch (error) {
26
33
console.error("Error processing URL:", error);
27
34
return new Response(
+41
-8
utils/calendarUtils.ts
···
5
5
import parseISO from "https://deno.land/x/date_fns@v2.22.1/parseISO/index.js";
6
6
import startOfDay from "https://deno.land/x/date_fns@v2.22.1/startOfDay/index.ts";
7
7
import endOfDay from "https://deno.land/x/date_fns@v2.22.1/endOfDay/index.ts";
8
8
+
import { fromZonedTime } from "npm:date-fns-tz";
8
9
9
10
export interface Event {
10
11
start: Date;
···
12
13
summary?: string;
13
14
location?: string;
14
15
description?: string;
16
16
+
timezone?: string;
15
17
}
16
18
17
19
export interface TwitchEvent {
···
56
58
57
59
// deno-lint-ignore no-explicit-any
58
60
function parseICalEvents(icalEvents: Record<string, any>): Event[] {
61
61
+
// Extract calendar-level timezone if available
62
62
+
const calendarTz =
63
63
+
Object.values(icalEvents).find((item) => item.type === "VCALENDAR")
64
64
+
?.["x-wr-timezone"] || "UTC";
65
65
+
59
66
return Object.values(icalEvents)
60
67
.filter((event) => event.type === "VEVENT")
61
61
-
.map((event) => ({
62
62
-
start: event.start,
63
63
-
end: event.end,
64
64
-
location: event.location || "",
65
65
-
summary: event.summary || "",
66
66
-
description: event.description || "",
67
67
-
}));
68
68
+
.map((event) => {
69
69
+
// Use event-specific timezone if available, otherwise fallback to calendar timezone
70
70
+
const eventTz = event.startZone || calendarTz;
71
71
+
72
72
+
// Convert dates to UTC for internal comparisons
73
73
+
const start = normalizeDate(event.start, eventTz);
74
74
+
const end = normalizeDate(event.end, eventTz);
75
75
+
76
76
+
return {
77
77
+
start,
78
78
+
end,
79
79
+
location: event.location || "",
80
80
+
summary: event.summary || "",
81
81
+
description: event.description || "",
82
82
+
timezone: eventTz,
83
83
+
};
84
84
+
});
85
85
+
}
86
86
+
87
87
+
function normalizeDate(date: Date, timezone: string): Date {
88
88
+
if (!date) return date;
89
89
+
90
90
+
try {
91
91
+
// Convert the date to UTC while respecting the original timezone
92
92
+
return fromZonedTime(date, timezone);
93
93
+
} catch (e) {
94
94
+
console.warn(`Failed to parse timezone ${timezone}:`, e);
95
95
+
return date;
96
96
+
}
68
97
}
69
98
70
99
export function filterEvents(
···
72
101
weekStart: Date,
73
102
weekEnd: Date,
74
103
): Event[] {
104
104
+
// Ensure weekStart and weekEnd are in UTC for comparison
105
105
+
const utcWeekStart = new Date(weekStart.toISOString());
106
106
+
const utcWeekEnd = new Date(weekEnd.toISOString());
107
107
+
75
108
const weeklyEvents = events.filter((event) =>
76
76
-
isAfter(event.start, weekStart) && isBefore(event.start, weekEnd)
109
109
+
isAfter(event.start, utcWeekStart) && isBefore(event.start, utcWeekEnd)
77
110
);
78
111
79
112
return weeklyEvents;