"This event only" vs "all events": the edit that did nothing

A recurring event isn’t a list of events. It’s one row plus a rule. “Every Tuesday at 10:00” is a single event with an RRULE, and the individual Tuesdays don’t exist as events of their own. They’re expanded from the rule, and the provider keeps those expansions in its Instances table only as a cache it can throw away and rebuild. That’s why editing a recurring event is one of the sharpest edges in the calendar model, and why Calendula had a bug (#16) where choosing “this event only” and saving bounced you back to the edit screen without changing anything.

Three answers to one save

Tap save on a recurring event and calendar apps generally ask the same question: this event, this and following, or all events? Each answer is a different operation against the data model:

What “this event only” actually does

Under the hood, “this event only” doesn’t edit anything. It creates an exception. The iCalendar model calls it a RECURRENCE-ID override: a new component that says “for the occurrence that would have landed at this original time, use these values instead.” Android’s CalendarContract exposes this through the exceptions URI and an ORIGINAL_INSTANCE_TIME: you insert a one-off event pinned to the timestamp of the occurrence you’re replacing, and the provider excludes the original instance from the expansion in its place.

So the operation is: identify the exact original instance time, insert an exception bound to it, and let the provider stop expanding the rule at that slot. Get the original-time bookkeeping wrong and the provider has nothing valid to attach the exception to. The write doesn’t land, and the UI, having nothing to show for the save, falls back to the edit screen. That matches #16 exactly: the popup appeared correctly, the other two options worked, and “this event only” silently did nothing. The general save logic was fine. The bug was in the one branch that has to create a row instead of updating one.

Why I let the provider handle it

I could sidestep all of this by keeping my own event table and doing the expansion myself. I don’t. Calendula reads and writes through the system calendar provider, and the provider and the CalDAV sync adapter behind it already know how to round-trip recurrence exceptions. An exception I write lands as a proper RECURRENCE-ID override. DAVx5 pushes it to your server, and other clients that read the calendar see the same override. If I expanded rules into my own private rows, I’d own every one of these edge cases forever, and my “edits” would be invisible to everything else that reads your calendar.

The hard part stays hard either way. Recurrence has been accreting rules and corrections in RFC 5545 and its predecessor RFC 2445 since 1998, and I haven’t found a shortcut through it. Working inside the standard means the result is shared: when I get the original-instance bookkeeping right, the exception is correct in every client that reads the calendar.