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
TimezoneFromCoordsand goes stale if coordinates are corrected.models.Locationhas 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
timezonestring on place/event payloads. - Opening hours are timezone-relative.
Location.OpeningHoursstores the structuredperiodsonly (no pre-formatted English text). "Open now" and hour display are computed fromperiods+ 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
tzfdependency; no data lives in the DB.
Cross-links¶
- Places carrying coordinates: Locations.
- Catalog-seeded places also derive timezone this way: Catalog seeding.