Skip to content

Timezones

Purpose

How the backend determines a place's timezone and how times are rendered in it. Read this before touching opening hours, event scheduling, or any date shown in a venue's local time.

Mental model

Timezones are IANA names (e.g. Asia/Tokyo) everywhere, and a place's timezone is derived from its coordinates, never stored.

lat / lng
TimezoneFromCoordstzf, in-process
IANA namein the response
frontendformatInTimezone
One source of truth (coordinates + tzdata polygons); nothing is persisted.

Surface area

Piece Where Behavior
TimezoneFromCoords(lat, lng) backend/internal/utils/timezone.go Point-in-polygon lookup via ringsaturn/tzf against tzdata boundaries. Returns the IANA name, or "UTC" on miss. In-process, offline, sub-millisecond.
Response injection event, maps, discovery handlers/services Each response that carries a venue time computes Timezone from the row's coordinates at serialize time (e.g. maps/service.go, discovery/service.go, event_handler.go).
GET /api/v1/events/timezone event domain Resolves an IANA name from a lat/lng pair for the client.
getUserTimezone() frontend/utils/timezone.ts The device IANA timezone (Intl.DateTimeFormat().resolvedOptions().timeZone).
formatInTimezone(utcISO, tz, …) frontend/utils/timezone.ts Renders a UTC instant in a given IANA timezone for display.

Invariants & gotchas

  • Never store timezone on a row. It is a pure function of coordinates; storing it duplicates TimezoneFromCoords and goes stale if coordinates are corrected. models.Location has no timezone column for this reason.
  • IANA names only. Every timezone value crossing the wire is an IANA zone id; the frontend consumes an optional timezone string on place/event payloads.
  • Opening hours are timezone-relative. Location.OpeningHours stores the structured periods only (no pre-formatted English text). "Open now" and hour display are computed from periods + the venue timezone (from coordinates) + the viewer's locale, on the client.
  • Times are stored in UTC, formatted into a timezone only at display.
  • "UTC" fallback is returned when a coordinate falls outside all tz polygons (open ocean, bad coords); callers can treat it as "unknown, assume UTC".
  • tzdata updates ship by bumping the tzf dependency; no data lives in the DB.