Files
ServerKit/backend/tests/test_server_time_timezone.py
Juan DenisandClaude Opus 4.8 e3249fbd52 fix(system): report IANA timezone_id; guard chart formatting against bad zones
SystemService.get_server_time fell through to Python's time.tzname on hosts
without /etc/timezone or timedatectl (e.g. Windows dev), yielding a display
name like "Eastern Daylight Time" — not a valid IANA/Intl key. The dashboard
MetricsGraph then crashed with RangeError: Invalid time zone specified.

- Backend: add a cross-platform tzlocal fallback + an IANA-validity guard so
  timezone_id is always an IANA key or null (display name stays in
  timezone_name). Declare tzlocal in requirements.
- Frontend: MetricsGraph validates the zone and falls back to local time
  instead of throwing (defense in depth).
- Test: test_server_time_timezone.py.

ServerKit's timezone feature (get/list/set via /api/v1/system/timezone) is
unchanged — this only fixes the value reported on non-Linux hosts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 22:55:11 -04:00

36 lines
1.5 KiB
Python

"""SystemService.get_server_time must only ever expose an IANA timezone_id.
Regression for a Windows-dev crash: Python's time.tzname yields a platform
*display* name ("Eastern Daylight Time") which is not a valid IANA/Intl zone key
and crashed the panel's time-formatting. timezone_id must be IANA-or-null; the
human display name lives in timezone_name.
"""
from app.services.system_service import SystemService
def test_is_iana_timezone():
assert SystemService._is_iana_timezone('America/New_York') is True
assert SystemService._is_iana_timezone('Europe/London') is True
assert SystemService._is_iana_timezone('UTC') is True
# Windows display names are rejected.
assert SystemService._is_iana_timezone('Eastern Daylight Time') is False
assert SystemService._is_iana_timezone('Pacific Standard Time') is False
assert SystemService._is_iana_timezone('') is False
assert SystemService._is_iana_timezone(None) is False
def test_get_server_time_timezone_id_is_iana_or_null():
t = SystemService.get_server_time()
tzid = t.get('timezone_id')
# Never a spaced display name; either a real IANA key or null.
assert tzid is None or SystemService._is_iana_timezone(tzid), tzid
# The display name is still available separately for the UI clock.
assert 'timezone_name' in t
def test_get_server_time_shape(app):
t = SystemService.get_server_time()
for key in ('current_time', 'current_time_formatted', 'utc_time',
'timezone_name', 'timezone_id', 'utc_offset'):
assert key in t