Skip to content
25 changes: 21 additions & 4 deletions packages/app-store/ics-feedcalendar/lib/CalendarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
});
});
});
Expand Down
24 changes: 20 additions & 4 deletions packages/lib/CalendarService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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,
});
});
});
Expand Down
Loading