Skip to content

fix: reject invalid server timezone (e.g. Etc/Unknown) before it reac…#910

Merged
ceotjoe merged 2 commits intoaccius:Stagingfrom
ceotjoe:fix/invalid-timezone-etc-unknown
Apr 14, 2026
Merged

fix: reject invalid server timezone (e.g. Etc/Unknown) before it reac…#910
ceotjoe merged 2 commits intoaccius:Stagingfrom
ceotjoe:fix/invalid-timezone-etc-unknown

Conversation

@ceotjoe
Copy link
Copy Markdown
Collaborator

@ceotjoe ceotjoe commented Apr 14, 2026

What does this PR do?

Summary

  • On minimal Linux Docker containers without a TZ environment variable set, Node's Intl.DateTimeFormat().resolvedOptions().timeZone returns "Etc/Unknown". This string is accepted by Node but rejected by browsers with RangeError: invalid time zone: Etc/Unknown, crashing the entire dashboard.
  • Server fix (server/routes/config-routes.js): validate the resolved timezone with Intl.DateTimeFormat in 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.
  • Client fix (src/hooks/app/useTimeState.js): apply the same validation guard before passing the timezone to toLocaleTimeString/toLocaleDateString, so a bad value can never crash the dashboard even if it somehow slips through in the future.

Reproduction

  1. Deploy via Docker/Portainer without a TZ environment variable set
  2. Open the app — dashboard fails immediately with RangeError: invalid time zone: Etc/Unknown

Workaround 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

  • Deploy via Docker without TZ set — dashboard loads, local time shows browser timezone
  • Deploy via Docker with TZ=America/New_York — local time reflects that timezone correctly
  • Open Settings → Timezone, manually select a timezone — still works as before
  • Verify no regression on bare-metal / non-Docker deployments

Type of change

  • Bug fix
  • New feature
  • Performance improvement
  • Refactor / code cleanup
  • Documentation
  • Translation
  • Map layer plugin

Checklist

  • App loads without console errors
  • Tested in Dark, Light, and Retro themes
  • Responsive at different screen sizes (desktop + mobile)
  • If touching server.js: caches have TTLs and size caps (we serve 2,000+ concurrent users)
  • If adding an API route: includes caching and error handling
  • If adding a panel: wired into Modern, Classic, and Dockable layouts
  • No hardcoded colors — uses CSS variables (var(--accent-cyan), etc.)
  • No .bak, .old, console.log debug lines, or test scripts included

…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>
Copy link
Copy Markdown
Owner

@accius accius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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):

  1. Memoize the client-side validation. useTimeState runs its try { Intl.DateTimeFormat(...) } catch on 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 safeTimezone below.

  2. 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.

  3. Intl.DateTimeFormat('en-US', …) — the locale is irrelevant for the validation; new Intl.DateTimeFormat(undefined, { timeZone: tz }) reads a little more clearly.

  4. Consider documenting in the Docker/self-hosting README that TZ is 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>
@ceotjoe
Copy link
Copy Markdown
Collaborator Author

ceotjoe commented Apr 14, 2026

Thanks for the thorough review — all four points addressed in the follow-up commit (d045c41).

  • Memoized validation — wrapped in useMemo([timezone]) so the Intl.DateTimeFormat probe only fires when the timezone value actually changes.
  • console.warn on fallback — server now logs the bad value with a hint to set TZ, so self-hosters have something actionable in their container logs.
  • Locale removed — both the server and client now use new Intl.DateTimeFormat(undefined, …).
  • Docs — added a note to docs/DOCKER.md that TZ is optional after this fix but recommended for server-side timestamp accuracy (logs, cache TTLs).

Docker without TZ runs as expected in my case.

@ceotjoe ceotjoe merged commit 92f38f3 into accius:Staging Apr 14, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] RangeError: invalid time zone: Etc/Unknown

2 participants