Skip to content

Timezone handling

Genie stores timestamps in UTC and converts them to each user’s local zone for display and input. Every request carries an effective session time zone; the engine exposes it to your SQL so that date logic can be tenant- and user-correct without hard-coding an offset.

  • Storage is UTC. DateTimeOffset is used for timezone-correct persistence.
  • The UI converts. The React client formats UTC values into the user’s zone for display and converts local input back to UTC before sending — the backend never renders a localized string.
  • SQL gets the zone as parameters (below), so server-side date expressions can localize too.

SessionTimeZoneService (ISessionTimeZoneService) resolves the effective IANA zone id in priority order, always validating the id before use:

  1. Session cookie — an explicit override set via the API (SessionTimeZone cookie).
  2. User preference — the persisted TimeZoneId on the user record.
  3. X-TimeZone request header — the browser’s auto-detected zone, sent by the client.
  4. UTC — the default fallback.

An invalid id at any level is skipped rather than trusted.

Two parameters are available to any view/entity/badge SQL, populated per request from the resolved zone (see Entities → session parameters for the full list):

Parameter Value
@SessionTimeZone the caller’s effective IANA zone (e.g. Asia/Karachi)
@SessionTimeZoneOffset that zone’s current UTC offset, in minutes

Localize a UTC column dialect-neutrally via the offset, or — for full DST-correctness across arbitrary dates on PostgreSQL — via the named zone:

-- SQL Server
DATEADD(MINUTE, @SessionTimeZoneOffset, SYSUTCDATETIME())
-- PostgreSQL (offset)
now() + (@SessionTimeZoneOffset || ' minutes')::interval
-- PostgreSQL (DST-correct for any date)
utcColumn AT TIME ZONE @SessionTimeZone
Method Route Purpose
POST /api/v1/genie/timezone/switch set the session zone ({ TimeZoneId }); writes the cookie and persists it as the user’s preference
GET /api/v1/genie/timezone/current get the current effective IANA zone id

The switch cookie is HttpOnly + Secure + SameSite=Strict with a one-year expiry, so the override survives across sessions until changed. A Profile time-zone picker in the UI drives this endpoint.