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],