fix: reject invalid server timezone (e.g. Etc/Unknown) before it reac…#910
Conversation
…hes the browser On minimal Linux Docker containers without a TZ env var, Node's Intl.DateTimeFormat().resolvedOptions().timeZone can return "Etc/Unknown", which is accepted by Node but rejected by browsers with: RangeError: invalid time zone: Etc/Unknown Fix on two levels: - server/routes/config-routes.js: validate the resolved timezone with a try/catch Intl.DateTimeFormat call before sending it to the client; returns '' (browser default) for any invalid value. - src/hooks/app/useTimeState.js: validate the timezone before applying it to toLocaleTimeString/toLocaleDateString, so a bad value can never crash the dashboard even if it somehow slips through. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
accius
left a comment
There was a problem hiding this comment.
Review — reject invalid server timezone
Clean, targeted bug fix with defense-in-depth (validate on the server, validate again on the client). Correct diagnosis of the Etc/Unknown case and well-scoped.
Suggestions (non-blocking):
-
Memoize the client-side validation.
useTimeStateruns itstry { Intl.DateTimeFormat(...) } catchon every render. Not expensive, but trivially improved by:const safeTimezone = useMemo(() => { if (!timezone) return ''; try { new Intl.DateTimeFormat(undefined, { timeZone: timezone }); return timezone; } catch { return ''; } }, [timezone]);
and then use
safeTimezonebelow. -
Log at least once server-side when the fallback kicks in, so self-hosters see something useful in the logs rather than silently getting browser-local time. A single
console.warn('[config] Invalid resolved timezone "%s" — falling back to empty (client will use browser TZ). Set TZ env var to silence.', tz)is enough. -
Intl.DateTimeFormat('en-US', …)— the locale is irrelevant for the validation;new Intl.DateTimeFormat(undefined, { timeZone: tz })reads a little more clearly. -
Consider documenting in the Docker/self-hosting README that
TZis optional after this fix but recommended for accurate server-side time stamps (logs, cache TTLs, etc.).
Test plan items are not ticked — please run at least the "Docker without TZ" repro and confirm before merge.
Approved as-is if the useMemo nit is out of scope; happy to merge.
- Client: memoize timezone validation with useMemo so the
Intl.DateTimeFormat probe runs only when the timezone value changes,
not on every render.
- Client/server: switch from Intl.DateTimeFormat('en-US', …) to
new Intl.DateTimeFormat(undefined, …) — locale is irrelevant for
timezone validation.
- Server: emit a console.warn when an invalid timezone is detected so
self-hosters see a clear message in their container logs rather than
silently falling back.
- Docs: note in DOCKER.md that TZ is optional after this fix but
recommended for accurate server-side timestamps.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for the thorough review — all four points addressed in the follow-up commit (d045c41).
Docker without TZ runs as expected in my case. |
What does this PR do?
Summary
TZenvironment variable set, Node'sIntl.DateTimeFormat().resolvedOptions().timeZonereturns"Etc/Unknown". This string is accepted by Node but rejected by browsers withRangeError: invalid time zone: Etc/Unknown, crashing the entire dashboard.server/routes/config-routes.js): validate the resolved timezone withIntl.DateTimeFormatin a try/catch before sending it to the client. Any invalid value (including"Etc/Unknown") is replaced with'', which tells the client to fall back to the browser's own timezone.src/hooks/app/useTimeState.js): apply the same validation guard before passing the timezone totoLocaleTimeString/toLocaleDateString, so a bad value can never crash the dashboard even if it somehow slips through in the future.Reproduction
TZenvironment variable setRangeError: invalid time zone: Etc/UnknownWorkaround for existing deployments
Add
TZ=Europe/Berlin(or your local IANA timezone) to the Portainer stack environment variables. This fix makes that optional rather than required.Test plan
TZset — dashboard loads, local time shows browser timezoneTZ=America/New_York— local time reflects that timezone correctlyType of change
Checklist
server.js: caches have TTLs and size caps (we serve 2,000+ concurrent users)var(--accent-cyan), etc.).bak,.old,console.logdebug lines, or test scripts included