Repository navigation
Expand recurring series in the zone their invites are published in - #455
Merged
Merged
Conversation
Series slots were stepped in naive UTC while the .ics anchors the RRULE in the coach's zone, so after a DST change every occurrence was stored an hour off the calendar and per-occurrence overrides and cancels named instances no client held. - Expand on local wall time in the series' zone and resolve each slot to UTC per RFC 5545 (skipped time uses the pre-gap offset, repeated time its first pass). - Validate weekdays and month days locally, so evening series no longer 422. - Reject monthly series starting after day 28 and starts in the second pass of a repeated hour. - Persist the published zone on SeriesRule (backfilled from the coach) and use it for every invite under the series UID. - Emit WKST=MO. - Check slots against the rrule crate's expansion of the emitted invite.
jhodapp
marked this pull request as ready for review
October 20, 2026 21:09
Contributor
[Medium impact] The changes affect recurring coaching schedules and their calendar updates. The PR still needs the stored monthly-series resend fix completed before merging.
|
Snapshots expanded stored rules through the publish-time validation, so a monthly series on day 29-31, or one accepted under the old UTC weekday check, could no longer be resent. Stored rules now expand with structural checks only, and months without the start day are skipped as RFC 5545 clients skip them. Also orders the new test files' imports external-first.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Recurring series were expanded in naive UTC, while the
.icsanchors the series in the coach's zone (DTSTART;TZID+RRULE). Calendars keep the local wall time across DST, so after a transition every occurrence was stored an hour off the calendar. Single-occurrence reschedules and cancels then named aRECURRENCE-IDno client held, and Google dropped them (production repro: Nov 5 occurrence of series67d9d4a5).This expands series on local wall time in the zone the series was published in, exactly as RFC 5545 clients do, and stores that zone on the series so later invites and expansions cannot split apart.
GitHub Issue: Fixes #456
Changes
expand_recurrence(start_at, rule, zone)steps on local wall time and resolves each slot to UTC per RFC 5545 §3.3.5 (a skipped time uses the offset before the gap, a repeated time its first pass).untilis compared against the resolved instant.SeriesRule.timezone: the zone actually emitted (UTC when noVTIMEZONEexists), set at create and reschedule. Every invite under the series UID (master, occurrence move and cancel, snapshot resend, series cancel) uses it, even if the coach later changes zone.rule.timezoneon existing series from the coach's current zone (unknown names become UTC).downremoves the key.WKST=MO.Testing Strategy
untilboundary, gap, overlap and the new 422s.rrulecrate (already in the tree viaicalendar, added as a dev-dependency) expands the exactDTSTART+RRULEwe emit across 20 cases, and must equal our slots. Mutation-checked: it fails if expansion reverts to UTC or the overlap rule changes.RECURRENCE-ID/EXDATE/TZIDon occurrence move, occurrence cancel and the snapshot.docs/test-plans/ics-calendar-invites-manual-test-plan.md, run with a Chicago coach against Google Calendar.Concerns