diff --git a/packages/app-store/ics-feedcalendar/lib/CalendarService.ts b/packages/app-store/ics-feedcalendar/lib/CalendarService.ts index 7a09df9761..a7261cc364 100644 --- a/packages/app-store/ics-feedcalendar/lib/CalendarService.ts +++ b/packages/app-store/ics-feedcalendar/lib/CalendarService.ts @@ -159,11 +159,17 @@ export default class ICSFeedCalendarService implements Calendar { // if (vevent?.getFirstPropertyValue("transp") === "TRANSPARENT") return; const event = new ICAL.Event(vevent); + const dtstartProperty = vevent.getFirstProperty("dtstart"); + const tzidFromDtstart = dtstartProperty ? (dtstartProperty as any).jCal[1].tzid : undefined; + const dtstart: { [key: string]: string } | undefined = vevent?.getFirstPropertyValue("dtstart"); const timezone = dtstart ? dtstart["timezone"] : undefined; // We check if the dtstart timezone is in UTC which is actually represented by Z instead, but not recognized as that in ICAL.js as UTC const isUTC = timezone === "Z"; - const tzid: string | undefined = vevent?.getFirstPropertyValue("tzid") || isUTC ? "UTC" : timezone; + + // Fix precedence: prioritize TZID from DTSTART property, then standalone TZID, then UTC, then fallback + const tzid: string | undefined = + tzidFromDtstart || 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")) { const timezoneToUse = tzid || userTimeZone; @@ -192,7 +198,16 @@ export default class ICSFeedCalendarService implements Calendar { console.error("No timezone found"); } } - const vtimezone = vcalendar.getFirstSubcomponent("vtimezone"); + + let vtimezone = null; + if (tzid) { + const allVtimezones = vcalendar.getAllSubcomponents("vtimezone"); + vtimezone = allVtimezones.find((vtz) => vtz.getFirstPropertyValue("tzid") === tzid); + } + + if (!vtimezone) { + vtimezone = vcalendar.getFirstSubcomponent("vtimezone"); + } // mutate event to consider travel time applyTravelDuration(event, getTravelDurationInSeconds(vevent)); @@ -263,9 +278,11 @@ export default class ICSFeedCalendarService implements Calendar { event.endDate = event.endDate.convertToZone(zone); } + const finalStartISO = dayjs(event.startDate.toJSDate()).toISOString(); + const finalEndISO = dayjs(event.endDate.toJSDate()).toISOString(); return events.push({ - start: dayjs(event.startDate.toJSDate()).toISOString(), - end: dayjs(event.endDate.toJSDate()).toISOString(), + start: finalStartISO, + end: finalEndISO, }); }); }); diff --git a/packages/lib/CalendarService.ts b/packages/lib/CalendarService.ts index 11dd86d8c7..7b738e69de 100644 --- a/packages/lib/CalendarService.ts +++ b/packages/lib/CalendarService.ts @@ -393,11 +393,16 @@ export default abstract class BaseCalendarService implements Calendar { if (vevent?.getFirstPropertyValue("transp") === "TRANSPARENT") return; const event = new ICAL.Event(vevent); + const dtstartProperty = vevent.getFirstProperty("dtstart"); + const tzidFromDtstart = dtstartProperty ? (dtstartProperty as any).jCal[1].tzid : undefined; const dtstart: { [key: string]: string } | undefined = vevent?.getFirstPropertyValue("dtstart"); const timezone = dtstart ? dtstart["timezone"] : undefined; // We check if the dtstart timezone is in UTC which is actually represented by Z instead, but not recognized as that in ICAL.js as UTC const isUTC = timezone === "Z"; - const tzid: string | undefined = vevent?.getFirstPropertyValue("tzid") || isUTC ? "UTC" : timezone; + + // Fix precedence: prioritize TZID from DTSTART property, then standalone TZID, then UTC, then fallback + const tzid: string | undefined = + tzidFromDtstart || 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")) { const timezoneToUse = tzid || userTimeZone; @@ -426,7 +431,16 @@ export default abstract class BaseCalendarService implements Calendar { console.error("No timezone found"); } } - const vtimezone = vcalendar.getFirstSubcomponent("vtimezone"); + + let vtimezone = null; + if (tzid) { + const allVtimezones = vcalendar.getAllSubcomponents("vtimezone"); + vtimezone = allVtimezones.find((vtz) => vtz.getFirstPropertyValue("tzid") === tzid); + } + + if (!vtimezone) { + vtimezone = vcalendar.getFirstSubcomponent("vtimezone"); + } // mutate event to consider travel time applyTravelDuration(event, getTravelDurationInSeconds(vevent, this.log)); @@ -498,9 +512,11 @@ export default abstract class BaseCalendarService implements Calendar { event.endDate = event.endDate.convertToZone(zone); } + const finalStartISO = dayjs(event.startDate.toJSDate()).toISOString(); + const finalEndISO = dayjs(event.endDate.toJSDate()).toISOString(); return events.push({ - start: dayjs(event.startDate.toJSDate()).toISOString(), - end: dayjs(event.endDate.toJSDate()).toISOString(), + start: finalStartISO, + end: finalEndISO, }); }); });