owlette docs
dashboard

timezones

Owlette tracks three independent timezone actors: each user picks which one renders absolute timestamps, and each site picks whether process launch windows follow the site's clock or each machine's own.


three actors

actorsourcewritten bydecides when schedules fire
machineThe agent's own Windows local timezoneAgent writes machine_timezone_iana into the machine's Firestore doc on every heartbeatAlways for scheduled reboots. For process launch windows too, unless the site has opted into site time
userThe dashboard viewer's preferred IANA timezoneUser sets timezone in Preferences (default: browser-detected)Never — display only
siteThe site's configured timezoneSet when the site is created (detected from the creator's browser, editable there) and changeable afterwards in Manage SitesProcess launch windows, but only once the site turns on schedulesFollowSiteTime (see process schedules)

All three are stored as IANA identifiers (e.g. America/New_York, Europe/Berlin). There is no "offset" or "abbreviation" field — offsets are resolved from the IANA name so that DST transitions work correctly.

The last column is the part that catches people out: a display mode never moves a schedule. Only the site's opt-in does, and only for process launch windows.


display modes

Each user picks one timeDisplayMode in preferences. That choice determines which actor's timezone is used to render absolute timestamps (heartbeats, activity logs, crash events, etc.) across the whole dashboard.

modebehaviorbest for
userAll times render in the user's timezoneSingle-operator setups where you always want your own local time
machine (default)Each machine's times render in that machine's own local tzDistributed kiosks across multiple timezones — "when did the Tokyo kiosk crash?" stays in JST
siteAll times render in the site's configured tzSingle-site teams where the site's local time is the shared reference

In machine mode, two cards on the same dashboard page may render times in two different timezones. A <TimezoneChip> next to each list of times tells you which actor delivered the timezone so the ambiguity is never silent.


fallback chain

When the primary source for the chosen mode is missing, Owlette falls back in a fixed order (see getDisplayTimezone() in web/lib/timeUtils.ts):

modefallback order
useruser's tz → browser → UTC
machinemachine tz → site tz → browser → UTC
sitesite tz → browser → UTC

machine falls back to site (not user) because if the agent hasn't reported its tz yet, the site's tz is the closest "this installation lives in X" approximation. Browser detection is always the last resort before UTC.


schedule editor

Schedule editors are unaffected by the user's display mode, but they do not all use the same timezone reference. Each scheduler uses the timezone that its feature expects:

featuretimezone usednotes
process launch windowsThe site's timezone if the site has opted into site time; each machine's local timezone otherwise (the default)Opt-in is per site — see process schedules below. The editor labels which clock a window will be read against. Its outside-window hint is computed in the site's timezone either way: on site time that is exactly what the agents do, on machine time it is a prediction of their clocks and the machine's clock is what decides.
scheduled restartsMachine local timezone, alwaysThe reboot dialog shows a machine timezone chip and the agent resolves each reboot entry against the machine's own Windows local timezone. A "03:00" reboot means 03:00 on that machine, even at a site that runs its process windows on site time. This is deliberate: keeping a box's maintenance window on the box's own clock is a different question from keeping a venue's opening hours on the venue's clock.
talon schedule triggersThe site's timezone, alwaysAutomations have always been site-scoped, so a schedule trigger fires on the site's clock (UTC when the site has no timezone set) regardless of schedulesFollowSiteTime.

All three are wall-clock schedules, not absolute UTC instants. Changing your dashboard display mode affects timestamp rendering only; it does not reinterpret saved schedule entries.


process schedules

A process window of "09:00-17:00 Mon-Fri" means 09:00 on whichever clock its site follows. Sites created since site time shipped follow the site's own timezone — the create-site dialog detects it from your browser and you can change it there before the site exists. Sites created before it evaluate windows on each machine's own clock, so a fleet spread across five cities opens at five different absolute instants, until a site admin answers the dashboard's one-time prompt.

The switch is a single field on the site, schedulesFollowSiteTime, and it has three states rather than two:

statewhat the machines dohow a site gets here
absentMachine-local windows — the original behavior, unchangedEvery site that predates site time (plus the rare new site whose browser reported no timezone at all). Site admins see a one-time banner on the dashboard asking which clock the site should use; it appears only when the site actually has a scheduled process
falseMachine-local windowsThe site was asked and kept machine clocks. Touring rigs and multi-city fleets usually want this
trueEvery machine evaluates its process windows in the site's timezoneThe site was created after site time shipped — the create-site dialog writes this flag and the timezone together — or it was asked and chose site time, or an admin turned it on afterwards

Absent and false behave identically on the machines. They are stored differently so the dashboard can tell whether the site has answered yet — that is the only thing the banner's presence depends on.

a site cannot follow site time without a timezone

Turning the flag on is rejected unless the site ends the write with a non-empty timezone. Set the timezone and the flag together in one edit — the dashboard banner does exactly that, and so should an API caller. Turning it back off never needs a timezone.

how the change reaches the machines

Agents ask the API which clock to use (GET /api/agent/site) on every connect and every 15 minutes after that, so a flip takes up to about a quarter hour to reach the fleet. Nothing is pushed, and no agent or service restart is involved.

The API returns a timezone only when the flag is exactly true and the site has one set; in every other case it returns null and the agent clears whatever it had cached. That is how switching site time back off drops the fleet to machine clocks on its own.

Agents older than 3.2.3 ignore the site's timezone and keep evaluating windows on their own clock. The dashboard flags those machines with an advisory note — it never blocks the setting, since one offline machine would otherwise hold an entire site back. Update them and they pick the site's clock up at their next refresh.

what a flip does not do

It does not rewrite saved schedule entries. The same stored "09:00" is simply read against a different clock, so a fleet that spans timezones will genuinely shift when the flag goes on — check the windows still mean what you want first. Scheduled reboots are not affected at all.


  • web/lib/timeUtils.ts — getDisplayTimezone(), getBrowserTimezone(), COMMON_TIMEZONES, searchable picker helpers
  • web/contexts/AuthContext.tsx — UserPreferences.timezone / timeDisplayMode / timeFormat
  • web/app/api/agent/site/route.ts — the projection agents read; gates timezone on schedulesFollowSiteTime === true
  • web/lib/actions/updateSite.server.ts — refuses true for a site that would end the write without a timezone
  • web/components/CreateSiteDialog.tsx — surfaces the detected browser timezone and creates the site with it and schedulesFollowSiteTime: true in one write
  • Agent-side: machine_timezone_iana is written alongside other heartbeat metrics; the cached schedule timezone is published in service_status.json as firebase.schedule_timezone

On this page