From 8ffb5529e6c08771217a50d6c6242572bed98b4f Mon Sep 17 00:00:00 2001 From: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Date: Wed, 10 May 2023 14:14:15 +0530 Subject: [PATCH] Use user profile timezone from DB as fallback for CalDAV all-day events (#8755) * adds timezone pull from user profile in DB * minor fixes * fixes promise return type * comments and minor fixes * minor improvement * fix return type * changes for help in debugging --- packages/lib/CalendarService.ts | 77 +++++++++++++++++++++++++-------- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/packages/lib/CalendarService.ts b/packages/lib/CalendarService.ts index d4c0f62049..b2f6676833 100644 --- a/packages/lib/CalendarService.ts +++ b/packages/lib/CalendarService.ts @@ -276,6 +276,38 @@ export default abstract class BaseCalendarService implements Calendar { } } + /** + * getUserTimezoneFromDB() retrieves the timezone of a user from the database. + * + * @param {number} id - The user's unique identifier. + * @returns {Promise} - A Promise that resolves to the user's timezone or "Europe/London" as a default value if the timezone is not found. + */ + getUserTimezoneFromDB = async (id: number): Promise => { + const prisma = await import("@calcom/prisma").then((mod) => mod.default); + const user = await prisma.user.findUnique({ + where: { + id, + }, + select: { + timeZone: true, + }, + }); + return user?.timeZone; + }; + + /** + * getUserId() extracts the user ID from the first calendar in an array of IntegrationCalendars. + * + * @param {IntegrationCalendar[]} selectedCalendars - An array of IntegrationCalendars. + * @returns {number | null} - The user ID associated with the first calendar in the array, or null if the array is empty or the user ID is not found. + */ + getUserId = (selectedCalendars: IntegrationCalendar[]): number | null => { + if (selectedCalendars.length === 0) { + return null; + } + return selectedCalendars[0].userId || null; + }; + isValidFormat = (url: string): boolean => { const allowedExtensions = ["eml", "ics"]; const urlExtension = getFileExtension(url); @@ -299,6 +331,10 @@ export default abstract class BaseCalendarService implements Calendar { dateTo, headers: this.headers, }); + + const userId = this.getUserId(selectedCalendars); + // we use the userId from selectedCalendars to fetch the user's timeZone from the database primarily for all-day events without any timezone information + const userTimeZone = userId ? await this.getUserTimezoneFromDB(userId) : "Europe/London"; const events: { start: string; end: string }[] = []; objects.forEach((object) => { if (object.data == null || JSON.stringify(object.data) == "{}") return; @@ -322,26 +358,31 @@ export default abstract class BaseCalendarService implements Calendar { const isUTC = timezone === "Z"; const tzid: string | undefined = vevent?.getFirstPropertyValue("tzid") || isUTC ? "UTC" : timezone; // In case of icalendar, when only tzid is available without vtimezone, we need to add vtimezone explicitly to take care of timezone diff - if (!vcalendar.getFirstSubcomponent("vtimezone") && tzid) { - try { - const timezoneComp = new ICAL.Component("vtimezone"); - timezoneComp.addPropertyWithValue("tzid", tzid); - const standard = new ICAL.Component("standard"); + if (!vcalendar.getFirstSubcomponent("vtimezone")) { + const timezoneToUse = tzid || userTimeZone; + if (timezoneToUse) { + try { + const timezoneComp = new ICAL.Component("vtimezone"); + timezoneComp.addPropertyWithValue("tzid", timezoneToUse); + const standard = new ICAL.Component("standard"); - // get timezone offset - const tzoffsetfrom = dayjs(event.startDate.toJSDate()).tz(tzid).format("Z"); - const tzoffsetto = dayjs(event.endDate.toJSDate()).tz(tzid).format("Z"); + // get timezone offset + const tzoffsetfrom = dayjs(event.startDate.toJSDate()).tz(timezoneToUse).format("Z"); + const tzoffsetto = dayjs(event.endDate.toJSDate()).tz(timezoneToUse).format("Z"); - // set timezone offset - standard.addPropertyWithValue("tzoffsetfrom", tzoffsetfrom); - standard.addPropertyWithValue("tzoffsetto", tzoffsetto); - // provide a standard dtstart - standard.addPropertyWithValue("dtstart", "1601-01-01T00:00:00"); - timezoneComp.addSubcomponent(standard); - vcalendar.addSubcomponent(timezoneComp); - } catch (e) { - // Adds try-catch to ensure the code proceeds when Apple Calendar provides non-standard TZIDs - console.log("error in adding vtimezone", e); + // set timezone offset + standard.addPropertyWithValue("tzoffsetfrom", tzoffsetfrom); + standard.addPropertyWithValue("tzoffsetto", tzoffsetto); + // provide a standard dtstart + standard.addPropertyWithValue("dtstart", "1601-01-01T00:00:00"); + timezoneComp.addSubcomponent(standard); + vcalendar.addSubcomponent(timezoneComp); + } catch (e) { + // Adds try-catch to ensure the code proceeds when Apple Calendar provides non-standard TZIDs + console.log("error in adding vtimezone", e); + } + } else { + console.error("No timezone found"); } } const vtimezone = vcalendar.getFirstSubcomponent("vtimezone");