fix: timezone parsing issues for calendars (#21665)

* fix timezone parsing issues for calendars

* fix: maintain backward compatibility with other timezone specification methods

accepting the cubic-dev-ai as implement backward compatibility with different event ICS event types

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* Update Cubic comment

* Fix timezone handling in ICS feed calendar service

- Fix VTIMEZONE selection to find matching timezone instead of using first one
- Improve TZID parameter extraction from DTSTART property using jCal array
- Fix precedence: DTSTART TZID > standalone TZID > UTC > fallback
- Resolves issue where Europe/Brussels events showed wrong times in Europe/Paris

* remove comments

* remove comments

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2025-09-04 04:36:07 +00:00
1 parent 0fadbe1ce9
commit 80250d32bd
2 files changed
+41 -8

No files matched your search

@@ -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,
});
});
});
+20 -4
View File
@@ -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,
});
});
});