“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? 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.
Keep sharpening your system design:
