You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Our calendar feed sends event times without saying what timezone they're in, and writes out every single occurrence of a repeating event one by one. Modern calendar apps expect a timezone block at the top and a "repeat weekly" rule for series, so subscribers in other timezones get the right local time and don't see 52 cluttering entries for a weekly meeting.
Detailed proposal
Extend web/_core/Ical.php (shipped via #271) to emit standards-compliant VTIMEZONE and RRULE blocks instead of the current floating-time, fully-expanded approach.
Concretely:
Walk the result set, collect the distinct TZID values from tblEvents.timezone (column landed with Dynamic per-event/per-calendar timezone support with auto-detection from location #238's dynamic detection work) and prepend one VTIMEZONE component per distinct zone before any VEVENT — generated from PHP's DateTimeZone::getTransitions() so DST offsets are correct.
For events bound to a series via tblEventSeries + tblEventRecurrence, emit a single VEVENT carrying DTSTART;TZID=..., RRULE:FREQ=...;INTERVAL=...;BYDAY=...;COUNT=... / UNTIL=... mapped from tblEventRecurrence columns, rather than one VEVENT per occurrence.
Reserve an EXDATE;TZID=... line for skipped instances — wired up later when per-occurrence overrides (the sibling gap issue) land.
Add a Ical::emitVTimezone(string $tzid): string helper and a Ical::emitRecurrenceRule(array $rrule): string helper; keep the current per-row path as a fallback for one-off events.
No schema additions needed (columns exist); no new routes; no UI surface — feed consumers benefit transparently. Brand-aware: feed title/PRODID still pulled from Site::branding() as today.
Pros / Cons
Pros
Correct local time for subscribers in any timezone (Apple Calendar / Outlook / Google all honour VTIMEZONE).
Feed payload shrinks dramatically — a weekly year-long series goes from 52 VEVENTs to 1.
Brings us to parity with TEC's iCal output, the reference implementation in the WordPress space.
Foundation for EXDATE once per-occurrence overrides land.
Cons
VTIMEZONE generation from DateTimeZone::getTransitions() is fiddly to get byte-exact across DST boundaries.
RRULE mapping has edge cases (BYSETPOS, monthly-by-weekday, COUNT vs UNTIL precedence).
Existing subscribers may see one-off re-sync churn the first time the feed format changes.
How necessary?
Medium — feed users (cross-timezone speakers, congregants subscribing on iOS/Android) hit the bug today but workarounds exist.
Acceptance criteria
VTIMEZONE block emitted once per distinct TZID in the feed, with correct standard/daylight transitions.
Series-bound events emit a single VEVENT + RRULE mapped from tblEventRecurrence.
DTSTART / DTEND carry TZID=... (no more floating time).
One-off events continue to emit as today (regression-safe).
Round-trip test: subscribe in Apple Calendar from a non-UK timezone, confirm local time renders correctly and series shows as recurring.
EXDATE emission point stubbed for the per-occurrence-override follow-up.
Estimated effort
~6-8 hours focused work (VTIMEZONE helper is the bulk; RRULE mapping is mechanical; existing Ical.php is small).
Filed during The Events Calendar competitive analysis on 2026-06-16; decision pending.
ELI5
Our calendar feed sends event times without saying what timezone they're in, and writes out every single occurrence of a repeating event one by one. Modern calendar apps expect a timezone block at the top and a "repeat weekly" rule for series, so subscribers in other timezones get the right local time and don't see 52 cluttering entries for a weekly meeting.
Detailed proposal
Extend
web/_core/Ical.php(shipped via #271) to emit standards-compliantVTIMEZONEandRRULEblocks instead of the current floating-time, fully-expanded approach.Concretely:
TZIDvalues fromtblEvents.timezone(column landed with Dynamic per-event/per-calendar timezone support with auto-detection from location #238's dynamic detection work) and prepend oneVTIMEZONEcomponent per distinct zone before anyVEVENT— generated from PHP'sDateTimeZone::getTransitions()so DST offsets are correct.tblEventSeries+tblEventRecurrence, emit a singleVEVENTcarryingDTSTART;TZID=...,RRULE:FREQ=...;INTERVAL=...;BYDAY=...;COUNT=.../UNTIL=...mapped fromtblEventRecurrencecolumns, rather than oneVEVENTper occurrence.EXDATE;TZID=...line for skipped instances — wired up later when per-occurrence overrides (the sibling gap issue) land.Ical::emitVTimezone(string $tzid): stringhelper and aIcal::emitRecurrenceRule(array $rrule): stringhelper; keep the current per-row path as a fallback for one-off events.Site::branding()as today.Pros / Cons
Pros
VTIMEZONE).VEVENTs to 1.EXDATEonce per-occurrence overrides land.Cons
VTIMEZONEgeneration fromDateTimeZone::getTransitions()is fiddly to get byte-exact across DST boundaries.How necessary?
Medium — feed users (cross-timezone speakers, congregants subscribing on iOS/Android) hit the bug today but workarounds exist.
Acceptance criteria
VTIMEZONEblock emitted once per distinctTZIDin the feed, with correct standard/daylight transitions.VEVENT+RRULEmapped fromtblEventRecurrence.DTSTART/DTENDcarryTZID=...(no more floating time).EXDATEemission point stubbed for the per-occurrence-override follow-up.Estimated effort
~6-8 hours focused work (VTIMEZONE helper is the bulk; RRULE mapping is mechanical; existing
Ical.phpis small).Filed during The Events Calendar competitive analysis on 2026-06-16; decision pending.