Calendar and scheduling systems are among the most deceptively complex applications in software engineering. Google Calendar, Outlook, and Calendly handle recurring events, timezone conversions, availability computation, and conflict detection at massive scale. This guide walks through the architecture of a production calendar system from the perspective of a senior engineer preparing for system design interviews.
Core Data Model for Calendar Events
The foundation of any calendar system is the event data model. A single event stores: event_id (UUID), calendar_id (which calendar it belongs to), title, description, start_time (UTC timestamp), end_time (UTC timestamp), timezone (IANA timezone string like America/New_York), recurrence_rule (RFC 5545 RRULE string), location, organizer_id, created_at, and updated_at. The critical design decision is storing all times in UTC and converting to the user display timezone at read time. This prevents ambiguity when users in different timezones view the same event.
Recurring Events with RFC 5545 RRULE
Recurring events are the hardest part of calendar design. The iCalendar standard (RFC 5545) defines RRULE syntax for recurrence patterns. Examples: FREQ=WEEKLY;BYDAY=MO,WE,FR means every Monday, Wednesday, Friday. FREQ=MONTHLY;BYMONTHDAY=15 means the 15th of every month. FREQ=YEARLY;BYMONTH=3;BYDAY=2SU means the second Sunday of March every year (US daylight saving time start). The naive approach of materializing all recurring event instances into the database is wasteful — a weekly meeting creates 52 rows per year, and a daily standup creates 365. Instead, store the RRULE on the master event and expand instances at query time. When a user views March 2026, the backend expands the RRULE to compute which dates fall within the view window. Libraries like python-dateutil (Python), rrule.js (JavaScript), or iCal4j (Java) handle RRULE expansion.
Exception Handling for Recurring Events
Users frequently modify or cancel individual instances of a recurring event. The iCalendar standard handles this with EXDATE (excluded dates) and RDATE (additional dates). When a user deletes a single occurrence, add an EXDATE entry. When a user modifies a single occurrence (change time or title), create an exception record linked to the master event via recurrence_id. The exception record overrides the computed instance for that specific date. This pattern keeps the master RRULE intact while allowing per-instance customization. Google Calendar calls this “this event” vs “this and following events” vs “all events” when editing a recurring series.
Timezone Handling and Daylight Saving Time
Timezone handling is notoriously error-prone. Key rules: (1) Store all timestamps in UTC. Never store local times in the database. (2) Store the IANA timezone identifier (America/New_York, not EST or UTC-5) because UTC offsets change with daylight saving time. (3) For recurring events, the timezone determines when the event occurs in local time. A 9 AM daily standup in America/New_York happens at 14:00 UTC during winter (EST, UTC-5) and 13:00 UTC during summer (EDT, UTC-4). The RRULE expansion must use the original timezone to compute correct UTC times across DST transitions. (4) When a timezone rule changes (governments change DST rules periodically), update the IANA timezone database (tzdata) and recompute affected events. (5) Display times in the viewer timezone, not the event timezone, unless explicitly requested. A New York user viewing a Tokyo meeting should see it in Eastern Time.
Availability and Free/Busy Computation
Availability computation answers: “when is this person free?” This powers scheduling features like Calendly and Google Calendar “find a time.” Algorithm: (1) Fetch all events for the target user within the query window. (2) Expand recurring events into individual instances. (3) Merge overlapping busy intervals into a consolidated busy timeline. (4) Subtract busy intervals from the query window to produce free slots. (5) Apply working hours constraints (only show availability between 9 AM and 6 PM in the user timezone). For group scheduling (find a time when all 5 attendees are free), compute the intersection of all attendees free slots. Optimization: pre-compute and cache the free/busy timeline per user per day. Invalidate the cache when any event in that day range is created, modified, or deleted. Google Calendar exposes free/busy data via the CalDAV protocol and the Google Calendar API freebusy endpoint.
Conflict Detection and Resolution
When a user creates or moves an event, check for conflicts with existing events. Conflict detection query: SELECT * FROM events WHERE calendar_id = ? AND start_time < new_end_time AND end_time > new_start_time. This finds all events that overlap with the proposed time range. For recurring events, expand instances in the relevant window before checking overlaps. Conflict resolution strategies: warn the user (show conflicting events and let them decide), auto-decline (reject the new event if it conflicts), or double-book (allow the conflict but mark it visually). For resource scheduling (meeting rooms), conflicts must be strictly prevented — use SELECT FOR UPDATE or an optimistic locking mechanism to prevent two users from booking the same room at the same time.
Syncing with CalDAV and iCalendar
CalDAV (RFC 4791) is the standard protocol for calendar synchronization. It extends WebDAV with calendar-specific operations: MKCALENDAR (create a calendar), PUT (create/update an event as an .ics file), DELETE (remove an event), REPORT (query events within a date range). Each event is stored as an iCalendar (.ics) file containing VEVENT components. Sync protocol: the client sends a REPORT request with a sync-token (last known state). The server returns all changes since that token — new events, modified events, deleted events. The client applies changes and stores the new sync-token. This incremental sync avoids downloading the entire calendar on each sync. For push notifications (instant sync), use WebSub or server-sent events to notify clients when changes occur. Google Calendar uses a proprietary push notification system alongside CalDAV support.
Scaling the Calendar Backend
At Google Calendar scale (billions of events, hundreds of millions of users), the architecture requires: (1) Shard events by calendar_id (user). Each shard contains all events for a subset of users. Cross-user queries (group scheduling) require scatter-gather across shards. (2) Use a time-partitioned index for efficient range queries — most calendar queries are “show me events for this week.” (3) Cache expanded recurring event instances for the current and next month per user. Cache invalidation on any event change in the window. (4) Separate read and write paths: reads are 100x more frequent than writes (users view their calendar constantly but create events occasionally). Read replicas handle view queries; the primary handles writes. (5) Event notifications (email reminders, push notifications) are processed by an async job queue that fires at the scheduled reminder time. A cron-like scheduler checks every minute for events with reminders due in the next minute and enqueues notification jobs.