diff --git a/src/components/upload-zone.tsx b/src/components/upload-zone.tsx
index 359158c..37907e5 100644
--- a/src/components/upload-zone.tsx
+++ b/src/components/upload-zone.tsx
@@ -75,9 +75,9 @@ export function UploadZone() {
// Remove the redirect flag after restoring
sessionStorage.removeItem("schedulesync_oauth_redirect");
}
- // eslint-disable-next-line react-hooks/exhaustive-deps
// Intentionally only depend on isSignedIn. We check preview/fileName/events for null
// to determine if restoration is needed, but don't want to re-run when they change.
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSignedIn]);
// Save state to sessionStorage before OAuth redirect
@@ -324,7 +324,7 @@ export function UploadZone() {
{analyzeSchedule.isPending ? (
<>
- Analyzing schedule...
+ Analyzing schedule... Don't close the page.
>
) : (
<>
diff --git a/src/server/api/routers/schedule.ts b/src/server/api/routers/schedule.ts
index 17502aa..80227ee 100644
--- a/src/server/api/routers/schedule.ts
+++ b/src/server/api/routers/schedule.ts
@@ -82,15 +82,17 @@ export const scheduleRouter = createTRPCRouter({
}
// Create a new calendar
+ const timezone = input.timezone ?? "UTC";
const calendarId = await createCalendar(
accessToken,
input.calendarName,
+ timezone,
);
// Add events to the calendar
await addEventsToCalendar(accessToken, calendarId, input.events, {
repeatWeeks: input.repeatWeeks,
- timezone: input.timezone ?? "UTC",
+ timezone,
startDate: input.startDate,
});
diff --git a/src/server/services/google-calendar.ts b/src/server/services/google-calendar.ts
index 871af2a..b13a519 100644
--- a/src/server/services/google-calendar.ts
+++ b/src/server/services/google-calendar.ts
@@ -2,12 +2,27 @@ import { google } from "googleapis";
import type { ScheduleEvent } from "./schedule-analyzer";
import { parse, addWeeks } from "date-fns";
+/**
+ * Formats a Date as a local datetime string without timezone suffix.
+ * Google Calendar will interpret this time using the provided timeZone parameter.
+ */
+function formatDateTimeLocal(date: Date): string {
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ const hours = String(date.getHours()).padStart(2, "0");
+ const minutes = String(date.getMinutes()).padStart(2, "0");
+ const seconds = String(date.getSeconds()).padStart(2, "0");
+ return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
+}
+
/**
* Creates a new Google Calendar
*/
export async function createCalendar(
accessToken: string,
calendarName: string,
+ timezone = "UTC",
): Promise {
const calendar = google.calendar({ version: "v3" });
@@ -18,7 +33,7 @@ export async function createCalendar(
auth,
requestBody: {
summary: calendarName,
- timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
+ timeZone: timezone,
},
});
@@ -95,11 +110,11 @@ async function createOneTimeEvent(
location: event.location,
description: buildEventDescription(event),
start: {
- dateTime: startDateTime.toISOString(),
+ dateTime: formatDateTimeLocal(startDateTime),
timeZone: timezone,
},
end: {
- dateTime: endDateTime.toISOString(),
+ dateTime: formatDateTimeLocal(endDateTime),
timeZone: timezone,
},
},
@@ -162,11 +177,11 @@ async function createRecurringEvent(
location: event.location,
description: buildEventDescription(event),
start: {
- dateTime: startDateTime.toISOString(),
+ dateTime: formatDateTimeLocal(startDateTime),
timeZone: timezone,
},
end: {
- dateTime: endDateTime.toISOString(),
+ dateTime: formatDateTimeLocal(endDateTime),
timeZone: timezone,
},
recurrence: [rrule],