Component: CalendarLink
Version: v3.4.0
Problem
IcsBuilder converts every timed value to UTC and emits no VTIMEZONE
component and no TZID parameter:
private function formatUtc(\DateTimeInterface $dt): string
{
return \DateTimeImmutable::createFromInterface($dt)
->setTimezone(new \DateTimeZone('UTC'))
->format('Ymd\THis\Z');
}
For a single event this is fine — an absolute instant is an absolute instant.
It stops being fine once CalendarRecurrence is used, which the component
ships and documents.
A weekly 09:00 Europe/Paris event created in July serializes to
DTSTART:...T070000Z. Under RRULE:FREQ=WEEKLY, occurrences after the October
DST transition remain 07:00 UTC — which is 08:00 Paris. The series silently
shifts by an hour for half the year.
RFC 5545 §3.8.5.3 is explicit that recurrence rules are expanded in the time
zone of DTSTART, which is why recurring events are normally anchored with
DTSTART;TZID=Europe/Paris:... plus a matching VTIMEZONE.
Reproduction
$event = new CalendarEvent(
title: 'Weekly standup',
start: new \DateTimeImmutable('2026-07-01 09:00', new \DateTimeZone('Europe/Paris')),
end: new \DateTimeImmutable('2026-07-01 09:30', new \DateTimeZone('Europe/Paris')),
recurrence: CalendarRecurrence::weekly(until: new \DateTimeImmutable('2026-12-31')),
);
Occurrences before 2026-10-25 land at 09:00 local; occurrences after land at
08:00 local.
Suggested fix
Emit DTSTART;TZID=<zone> / DTEND;TZID=<zone> from the incoming
\DateTimeInterface's own zone and write the corresponding VTIMEZONE.
If shipping a full VTIMEZONE generator is judged out of scope, a narrower fix
would still help: document the constraint, and consider rejecting or warning on
a CalendarRecurrence whose DTSTART carries a zone with DST transitions,
rather than emitting a silently wrong series.
Component: CalendarLink
Version: v3.4.0
Problem
IcsBuilderconverts every timed value to UTC and emits noVTIMEZONEcomponent and no
TZIDparameter:For a single event this is fine — an absolute instant is an absolute instant.
It stops being fine once
CalendarRecurrenceis used, which the componentships and documents.
A weekly 09:00
Europe/Parisevent created in July serializes toDTSTART:...T070000Z. UnderRRULE:FREQ=WEEKLY, occurrences after the OctoberDST transition remain 07:00 UTC — which is 08:00 Paris. The series silently
shifts by an hour for half the year.
RFC 5545 §3.8.5.3 is explicit that recurrence rules are expanded in the time
zone of
DTSTART, which is why recurring events are normally anchored withDTSTART;TZID=Europe/Paris:...plus a matchingVTIMEZONE.Reproduction
Occurrences before 2026-10-25 land at 09:00 local; occurrences after land at
08:00 local.
Suggested fix
Emit
DTSTART;TZID=<zone>/DTEND;TZID=<zone>from the incoming\DateTimeInterface's own zone and write the correspondingVTIMEZONE.If shipping a full
VTIMEZONEgenerator is judged out of scope, a narrower fixwould still help: document the constraint, and consider rejecting or warning on
a
CalendarRecurrencewhoseDTSTARTcarries a zone with DST transitions,rather than emitting a silently wrong series.