calendar: recurrence overlap-windowing + unify is-recurring predicate (low) #216

Open
opened 2026-07-29 22:35:43 +00:00 by KofTwentyTwo · 0 comments
KofTwentyTwo commented 2026-07-29 22:35:43 +00:00 (Migrated from github.com)

Low-severity correctness follow-ups from the M1-S2 (#186) review — non-blocking, deferred.

F2 — recurrence uses occurrence-START windowing, not interval-overlap. expand_occurrences keeps occurrences whose START is in [start_window,end_window], while list_calendar_events (non-recurring path) uses interval-overlap (an event starting before the window but still ongoing is returned, db.rs:944-947). So a recurring occurrence that starts just before start_window but whose duration extends into the window is dropped, whereas an equivalent non-recurring event is returned. #186's Goal says 'instances that overlap that window.' Fix: compute occurrence end = start + (master.end - master.start) and use overlap semantics; add a straddle-start test.

F3 — the two layers use different 'is-recurring' predicates. list_recurring_calendar_events uses SQL rrule IS NOT NULL AND rrule != ''; the passthrough keeps rrule.is_none(). Not exact inverses: an event with rrule = Some("") is dropped from BOTH sets (silently); Some(" ") is classed recurring by SQL but the engine early-returns the raw clone. Unify: trim consistently and treat empty-after-trim as non-recurring in both places.

Both are narrow edges (short events/wide windows; malformed CalDAV RRULE). Fold into the next calendar touch.

Low-severity correctness follow-ups from the M1-S2 (#186) review — non-blocking, deferred. **F2 — recurrence uses occurrence-START windowing, not interval-overlap.** `expand_occurrences` keeps occurrences whose START is in `[start_window,end_window]`, while `list_calendar_events` (non-recurring path) uses interval-**overlap** (an event starting before the window but still ongoing is returned, db.rs:944-947). So a recurring occurrence that starts just before `start_window` but whose duration extends into the window is dropped, whereas an equivalent non-recurring event is returned. #186's Goal says 'instances that overlap that window.' Fix: compute occurrence end = start + (master.end - master.start) and use overlap semantics; add a straddle-start test. **F3 — the two layers use different 'is-recurring' predicates.** `list_recurring_calendar_events` uses SQL `rrule IS NOT NULL AND rrule != ''`; the passthrough keeps `rrule.is_none()`. Not exact inverses: an event with `rrule = Some("")` is dropped from BOTH sets (silently); `Some(" ")` is classed recurring by SQL but the engine early-returns the raw clone. Unify: trim consistently and treat empty-after-trim as non-recurring in both places. Both are narrow edges (short events/wide windows; malformed CalDAV RRULE). Fold into the next calendar touch.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: KofTwentyTwo/nuncio#216