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.
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How do you expand RRULE recurring events efficiently?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Pre-expand recurring events into a materialized occurrence table up to a horizon (e.g., 2 years), storing each instance with its parent rule ID. On update to the master rule, delete future occurrences and re-expand. This avoids on-the-fly expansion at query time and keeps calendar range queries O(1) index scans.”
}
},
{
“@type”: “Question”,
“name”: “How do you handle attendee management at scale?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Store attendees in a separate attendee table keyed by (event_id, user_id) with an RSVP status enum. Use a capacity counter on the event row (decremented atomically with SELECT FOR UPDATE or compare-and-swap) to enforce limits. Batch-load attendee lists with pagination and emit domain events (AttendeeAdded, AttendeeRemoved) for downstream notifications.”
}
},
{
“@type”: “Question”,
“name”: “What algorithm detects scheduling conflicts?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Model each event as an interval [start, end). Conflict detection is an interval overlap query: new_start < existing_end AND new_end > existing_start. Index on (calendar_id, start_time, end_time) and run this as a bounded range scan. For room or resource conflicts, scope the query to the shared resource ID rather than a personal calendar.”
}
},
{
“@type”: “Question”,
“name”: “How do you implement timezone-aware event display?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Store all timestamps in UTC in the database. Attach the event's authoring timezone (IANA tz string, e.g., America/New_York) as metadata. On read, convert UTC to the viewer's local timezone using a tz database (e.g., java.time, pytz, Temporal API). Never store local wall-clock times — they break across DST transitions and tz rule changes.”
}
}
]
}
See also: Apple Interview Guide 2026: iOS Systems, Hardware-Software Integration, and iCloud Architecture
See also: Atlassian Interview Guide