Calendar Service Low-Level Design: Event Model, Recurring Events, and Conflict Detection

Event Schema

The event is the core entity. It supports single and recurring events, multiple attendees, and rich metadata:

events {
  event_id            UUID PK
  calendar_id         UUID FK
  title               VARCHAR(255)
  description         TEXT
  start_time          TIMESTAMP WITH TIME ZONE  -- stored in UTC
  end_time            TIMESTAMP WITH TIME ZONE
  timezone            VARCHAR(64)   -- e.g. "America/New_York"
  location            VARCHAR(500)
  recurrence_rule     TEXT nullable  -- RRULE string per RFC 5545
  recurrence_exceptions DATE[]       -- dates where normal occurrence is skipped
  organizer_user_id   UUID FK
  visibility          ENUM(public, private)
  status              ENUM(CONFIRMED, TENTATIVE, CANCELLED)
  created_at          TIMESTAMP
  updated_at          TIMESTAMP
}

event_attendees {
  event_id  UUID FK
  user_id   UUID FK nullable
  email     VARCHAR(255)
  status    ENUM(NEEDS_ACTION, ACCEPTED, DECLINED, TENTATIVE)
}

Timezone Storage

All times are stored in UTC in the database. The timezone field records the event's originating timezone. This is critical for recurring events: RRULE evaluation must happen in the event's timezone to correctly handle Daylight Saving Time transitions. A weekly event on Monday at 9am ET stays at 9am ET through DST changes — UTC offset varies, but local time is stable.

Recurring Events

Recurring events store a single master record with an RRULE string rather than pre-generating all instances. Example:

RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=12
RRULE:FREQ=MONTHLY;BYDAY=1MO;UNTIL=20241231T000000Z

Instances are generated on-demand when the calendar view requests a date range. The server evaluates the RRULE in the event's timezone, filters to the requested window, and returns the list of occurrence timestamps. Libraries like python-dateutil or rrule.js handle RRULE parsing.

Recurring Instance Modification

Two types of modifications to a recurring series:

  • Single occurrence edit: add the occurrence date to recurrence_exceptions on the master event; create a standalone modified_instance record with the exception date and changed fields. The master event's RRULE skips this date; the modified instance is served instead.
  • This and all future: set UNTIL on the original RRULE to end before the split date; create a new master event starting at the split date with the updated fields and a new RRULE.

Conflict Detection

To detect scheduling conflicts for a user, query their events for time overlap with the proposed slot:

SELECT e.event_id, e.title
FROM events e
JOIN event_attendees a ON e.event_id = a.event_id
WHERE a.user_id = :user_id
  AND e.status != 'CANCELLED'
  AND e.start_time < :new_end_time
  AND e.end_time   > :new_start_time

This interval overlap condition (start < other_end AND end > other_start) catches all overlapping cases. For recurring events, generated instances are checked against the proposed slot during RRULE evaluation.

Multi-Attendee Scheduling

Finding a free slot for multiple attendees works by computing the intersection of all their free windows:

  1. Query all events for each attendee within the search window
  2. Build a busy-time list per attendee
  3. Compute free intervals: subtract busy times from the full search window
  4. Intersect free intervals across all attendees
  5. Filter resulting slots by minimum duration and business hours
  6. Return ranked slot suggestions

iCal Import (RFC 5545)

Uploaded .ics files are parsed component by component. VEVENT components map to event records. VTIMEZONE components define timezone rules used to convert local times to UTC. Recurrence rules (RRULE, EXDATE) are stored as-is. ATTENDEE properties map to event_attendees rows. Duplicate detection uses the UID field from the iCal spec — if an event with that UID already exists, the import updates rather than duplicates it.

iCal Export and Calendar Subscription

Events are exported as RFC 5545 compliant .ics files. For calendar subscriptions, a user's calendar is served at a stable URL:

GET /calendars/{calendar_id}/feed.ics?token={private_token}

External calendar apps (Google Calendar, Apple Calendar, Outlook) poll this URL on a schedule to sync events. The feed includes all events in the calendar serialized as VEVENT blocks, with proper RRULE fields for recurring events.

Attendee Invitations

When an event is created with attendees, invitation emails are sent with Accept / Decline / Tentative links. Each link carries a signed token encoding {event_id, attendee_email, response}. Clicking the link updates the attendee's status and redirects to a confirmation page. The organizer sees real-time RSVP status updates as attendees respond.

Calendar Sharing

Calendars can be shared between users with two permission levels:

  • View-only: shared user sees events but cannot create or edit
  • Edit: shared user can create, modify, and delete events on the calendar
calendar_shares { calendar_id, shared_with_user_id, permission ENUM(view, edit) }

Private events (visibility = private) are shown as “Busy” blocks to view-only users, hiding title and description.

See also: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Apple Interview Guide 2026: iOS Systems, Hardware-Software Integration, and iCloud Architecture

See also: Atlassian Interview Guide

See also: Scale AI Interview Guide 2026: Data Infrastructure, RLHF Pipelines, and ML Engineering

Scroll to Top