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
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
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).