What's the best way to model recurring events in a calendar application?

I would use a 'link' concept for all future recurring events. They are dynamically displayed in the calendar and link back to a single reference object. When events have taken place the link is broken and the event becomes a standalone instance. If you attempt to edit a recurring event then prompt to change all future items (i.e. change single linked reference) or change just that instance (in which case convert this to a standalone instance and then make change). The latter cased is slightly problematic as you need to keep track in your recurring list of all future events that were converted to single instance. But, this is entirely do-able.

So, in essence, have 2 classes of events - single instances and recurring events.


Martin Fowler - Recurring Events for Calendars contains some interesting insights and patterns.

Runt gem implements this pattern.


There can be many problems with recurring events, let me highlight a few that I know of.

Solution 1 - no instances

Store original appointment + recurrence data, do not store all the instances.

Problems:

  • You'll have to calculate all the instances in a date window when you need them, costly
  • Unable to handle exceptions (ie. you delete one of the instances, or move it, or rather, you can't do this with this solution)

Solution 2 - store instances

Store everything from 1, but also all the instances, linked back to the original appointment.

Problems:

  • Takes a lot of space (but space is cheap, so minor)
  • Exceptions must be handled gracefully, especially if you go back and edit the original appointment after making an exception. For instance, if you move the third instance one day forward, what if you go back and edit the time of the original appointment, re-insert another on the original day and leave the moved one? Unlink the moved one? Try to change the moved one appropriately?

Of course, if you're not going to do exceptions, then either solution should be fine, and you basically choose from a time/space trade off scenario.