# Design a Mobile Calendar: Sync, Conflicts, and Recurrence

Source: https://www.techinterview.org/post/3233475021/design-mobile-calendar-sync-recurrence/
Updated: 2026-07-26 · techinterview.org

"Design Google Calendar mobile" is a deceptively simple-sounding question. Until the interviewer asks: how do you handle a daylight-saving boundary in a recurring event? How do you [reconcile when two devices edited the same event offline](/post/3233460997/system-design-collaborative-editing/)? What is the right way to show a meeting that crosses midnight in two time zones?

## Functional requirements

- View calendar by day, week, month. Interviewers probe how each view renders efficiently: the month grid needs only lightweight per-day summaries, while the day view shows full detail and must lay out overlapping events into side-by-side columns.

- Create, edit, delete events. The interesting part is editing a recurring event — does the change apply to this instance, this and all future instances, or the whole series? Make that choice explicit in both the UI and the data model.

- Recurring events (daily, weekly, monthly, custom). Support the common cadences plus custom rules like "every third Tuesday." Store the rule itself, not a pre-computed list of occurrences.

- Multi-calendar support (work, personal, shared). Each calendar carries its own color, visibility toggle, and sync account. An event belongs to exactly one calendar, and a shared calendar adds permission checks for who is allowed to edit.

- Time zone awareness. A user who creates an event in New York and opens the app in Tokyo should still see it at the correct wall-clock time. Decide per event whether it floats or is pinned to a specific zone.

- Reminders and notifications. Users expect a reminder to fire even with no network, so notifications are scheduled locally on the device rather than relying on a server push at the exact moment.

- Offline event creation. Someone on a plane should be able to add an event and trust it syncs later. This requirement is what forces the local-UUID and later reconciliation design.

## Architecture

Local SQLite as source of truth for offline use, sync engine for two-way reconciliation, notification scheduler for reminders.

## Event model

An event has:

- Server ID (assigned on first sync). Until that first sync completes, the event exists only on the device and has no server-side identity.

- Local UUID (for offline creation). This lets the client create an event and reference it — from a reminder or an exception — before the server has ever seen it.

- Start, end, time zone, all-day flag. All-day events are a special case: they carry no time zone and should not shift when the user travels across zones.

- RRULE (recurrence rule, RFC 5545). Keep the rule string as the source of truth instead of materializing every occurrence up front.

- EXDATE (excluded dates). These are the dates removed from a series — for example, a weekly standup skipped for a holiday.

- Recurrence-ID (for modifications to specific instances). It pins an exception to the single occurrence it overrides so the rest of the series is untouched.

- Updated-at, sequence number. These are the two fields the sync engine compares to decide which version of an event is newer.

## Recurrence

RFC 5545 RRULE is the standard. Examples: `RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20260901`.

To render a calendar, expand the recurrence into instances within the visible window. Cache the expansion. Recompute on edit.

Modifying a single instance creates an "exception" event with the same Recurrence-ID. The original series continues unchanged.

## Time zones

Always store events in UTC + IANA time zone identifier (e.g., "America/New_York"). Render in the user's local time zone, but for events explicitly in another zone (e.g., a flight from JFK), render in the event's zone.

Daylight saving transitions can shift recurring events by an hour. Be deliberate: usually preserve "8am every weekday in NYC time" rather than "13:00 UTC."

## Sync

iCalendar / Google Calendar API. Each event has a sequence number and an updated-at. On sync conflict (same event modified on two devices), the higher sequence wins.

For deletes: tombstones with TTL. Allows offline devices to learn about deletions when they reconnect.

## Reminders

Local notification scheduling. iOS: `UNUserNotificationCenter` with up to 64 pending notifications. Android: `AlarmManager` with exact alarm permission.

For events with email/SMS reminders, the server is responsible — mobile only handles local push.

## Offline event creation

Events created offline have a local UUID. On sync, the server assigns a real ID and the client maps the UUID to the new ID. References (e.g., a recurring exception) are remapped.

## Battery and data

- Sync only when foreground or push-triggered

- Calendar widgets refresh every 15–30 minutes max

- Recurrence expansion is cached, not re-computed every render

## Frequently Asked Questions

### How do you handle the meeting-crosses-midnight case?

Render the event spanning two days. The cell renderer needs to be aware that an event can occupy parts of multiple day boxes.

### What if two devices edit the same event offline?

Last-writer-wins by sequence number. Show the user a UI cue if their write was overridden.

### How do you handle a 100-year recurring event?

Expand only within the visible window (typically 1 month). Lazy-expand on demand. Never materialize all 36,500 instances.
