e-cal-recur: Incorrect timezone used for DTEND from DURATION
This is a simple valid recurring event (in this case produced by Fastmail's CalDAV server):
BEGIN:VEVENT
SEQUENCE:0
UID:a1e62dea-3171-4160-a358-ef2dedd11478
DTSTART;TZID=Australia/Melbourne:20200212T100000
DURATION:PT30M
CREATED:20200212T111400Z
DTSTAMP:20200212T111400Z
PRIORITY:0
SUMMARY:testing recurring event
RRULE:FREQ=WEEKLY
STATUS:CONFIRMED
TRANSP:OPAQUE
END:VEVENT
Once imported by the e-d-s CalDAV backend, clients present the these instances in my local timezone (Australia/Melbourne, UTC+1100) as starting at 10:00am, ending at 21:30am. So it appears the end timezone isn't being set properly.
I've traced the problem, and it seems to come down to the very odd ensure_timezone
function and how it plays with DTEND
. e_cal_recur_generate_instances_sync
does something like:
ICalTime *dtend = i_cal_component_get_dtend(comp);
ensure_timezone (comp, dtend, I_CAL_DTEND_PROPERTY, ...);
The bug stems from the call to icalcomponent_get_dtend
. If the event does not have a DTEND
, but has DTSTART
and DURATION
, it will synthesize an end datetime.
ensure_timezone
then resets the timezone to UTC, and then looks for the original DTEND
property to get its timezone parameter so it can adjust the time. But of course DTEND
doesn't exist, so no adjustment is made.
I can reproduce this reliably with any recurring event with no DTEND
, but I have not been able to reduce it to a standalone test case. I did a test by patching ensure_timezone
as follows:
diff --git src/calendar/libecal/e-cal-recur.c src/calendar/libecal/e-cal-recur.c
index 52613cb18..2f0e6311e 100644
--- src/calendar/libecal/e-cal-recur.c
+++ src/calendar/libecal/e-cal-recur.c
@@ -225,9 +225,11 @@ ensure_timezone (ICalComponent *comp,
if (!prop)
prop = i_cal_component_get_first_property (comp, prop_kind);
- else
+ if (!prop && prop_kind == I_CAL_DTEND_PROPERTY)
+ prop = i_cal_component_get_first_property (comp, I_CAL_DTSTART_PROPERTY);
+ if (prop)
g_object_ref (prop);
- if (!prop)
+ else
return TRUE;
param = i_cal_property_get_first_parameter (prop, I_CAL_TZID_PARAMETER);
This is of course not a correct general patch, but did seem to verify the theory, as now the end times appear as I expect.
I'm not entirely sure what the correct fix would be, mostly because I haven't been able to figure out why ensure_timezone
actually looks for the timezone this way. It does seem odd to me that it doesn't just use the timezone inside the ICalTime
but I expect I'm missing some knowledge.
(Note: this was tested with e-d-s 3.28.5 as shipped with Ubuntu 18.04.3 LTS as using the upstream version isn't convenient. However reading the current HEAD suggests the behaviour has likely not changed since then).