What Is an Event Calendar System?
An event calendar allows users to create, manage, and share events with support for recurring schedules, attendee coordination, and conflict detection. Systems like Google Calendar and Outlook Calendar are canonical examples. At the low level, the challenge lies in handling RRULE-based recurrence, timezone-aware rendering, and attendee state without performance degradation.
Requirements
Functional Requirements
- Create, read, update, and delete single and recurring events
- Support RRULE recurrence rules (daily, weekly, monthly, custom)
- Invite attendees and track RSVP status (accepted, declined, tentative)
- Detect scheduling conflicts for a user or shared resource
- Display events in correct local timezone for each viewer
- Support event exceptions (modify or cancel one instance of a recurring series)
Non-Functional Requirements
- Low-latency calendar fetch (under 200ms for a month view)
- Support millions of users with dense recurring event data
- Timezone correctness across DST boundaries
- Consistency for concurrent edits on shared calendars
Data Model
The core entity is the Event record. Store the base event in UTC and attach recurrence metadata separately to keep expansion lazy.
- events: event_id, calendar_id, creator_id, title, description, start_utc, end_utc, timezone, location, status, created_at, updated_at
- recurrence_rules: rule_id, event_id, rrule_string (RFC 5545 RRULE), until_utc, count, exception_dates (JSON array)
- event_instances: instance_id, event_id, original_start_utc, override_start_utc, override_end_utc, override_title, is_cancelled — used for modified or cancelled occurrences
- attendees: attendee_id, event_id, user_id, email, rsvp_status, notified_at
- calendars: calendar_id, owner_id, name, color, visibility, share_acl (JSON)
Store all datetimes in UTC. The client timezone is recorded per event for display purposes and DST-safe expansion.
Core Algorithms
RRULE Expansion
Use an RFC 5545-compliant library (e.g., python-dateutil rrulestr or rrule.js) to expand recurrence rules into occurrence timestamps. Never pre-materialize all occurrences; expand on demand within a query window. Apply exception dates and modified instances after expansion.
Algorithm: given a time window [start, end], call rrule.between(start, end), then subtract cancelled instance dates and merge overridden instances.
Conflict Detection
Conflict detection checks whether two events overlap for the same user or resource. Two intervals [s1,e1] and [s2,e2] overlap when s1 < e2 AND s2 < e1. For recurring events, expand both series within a reasonable horizon (e.g., 90 days) before comparing. Index by user_id and time range using a partial index on start_utc and end_utc to keep queries fast.
Timezone-Aware Rendering
Store start and end in UTC. On read, convert to the viewer’s IANA timezone. For all-day events, store a date string without time component to avoid DST shifting. Use the VTIMEZONE component from RFC 5545 when exporting to iCal format.
API Design
- POST /events — create event; body includes rrule if recurring
- GET /calendars/{id}/events?start=&end=&tz= — fetch expanded occurrences in window
- PATCH /events/{id}/instances/{date} — modify a single occurrence (creates event_instances row)
- DELETE /events/{id}/instances/{date} — cancel single occurrence
- POST /events/{id}/attendees — invite attendees
- PATCH /events/{id}/attendees/{user_id} — update RSVP status
- GET /users/{id}/conflicts?start=&end= — return overlapping event pairs
Scalability Considerations
Recurring event expansion is CPU-intensive. Cache expanded occurrence lists in Redis with a TTL tied to the recurrence frequency (daily events: 1-day TTL, weekly: 7-day TTL). Invalidate on exception or rule change.
For high-read calendars (shared company calendars), use a read replica and cache month-view results keyed by calendar_id + month + timezone. Shard the events table by calendar_id. Use a message queue (Kafka or SQS) to fan out invite notifications to attendees asynchronously, decoupling the write path from email/push delivery.
Conflict detection at scale can use a secondary index or a separate events_by_user table sorted by start_utc, allowing O(log n) range scans per user per day.
Summary
A well-designed event calendar separates base event storage from recurrence expansion, keeps all times in UTC, and handles exceptions as first-class rows. Conflict detection relies on interval overlap math applied to lazily expanded occurrences. Caching expansion results and fanning out notifications asynchronously keeps the system performant under load.
See also: Apple Interview Guide 2026: iOS Systems, Hardware-Software Integration, and iCloud Architecture
See also: Atlassian Interview Guide