From c06d15be5f44694e3b2470e15ef969f331f363a3 Mon Sep 17 00:00:00 2001 From: viettranx Date: Sat, 18 Apr 2026 15:09:46 +0700 Subject: [PATCH] fix(ui): canonicalize timezone dropdown to drop deprecated aliases Resolve each IANA name via Intl.DateTimeFormat().resolvedOptions() so aliases like Asia/Saigon collapse into their canonical form (Asia/Ho_Chi_Minh). Prevents cron/heartbeat pickers from saving a deprecated alias that diverges from the Go-side canonical name. Closes #760 --- ui/web/src/lib/timezone-utils.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ui/web/src/lib/timezone-utils.ts b/ui/web/src/lib/timezone-utils.ts index 762fd717..1c71c8df 100644 --- a/ui/web/src/lib/timezone-utils.ts +++ b/ui/web/src/lib/timezone-utils.ts @@ -40,19 +40,34 @@ const FALLBACK_TIMEZONES: string[] = [ "Pacific/Auckland", ]; +/** Resolve to canonical IANA name (drops deprecated aliases like Asia/Saigon → Asia/Ho_Chi_Minh). */ +function canonicalizeTimezone(tz: string): string | null { + try { + return new Intl.DateTimeFormat("en-US", { timeZone: tz }).resolvedOptions().timeZone; + } catch { + return null; + } +} + /** All IANA timezones with dynamic UTC offsets, sorted by offset then name. */ export function getAllIanaTimezones(): TzOption[] { if (_cachedTimezones) return _cachedTimezones; let tzNames: string[]; try { - + tzNames = (Intl as any).supportedValuesOf("timeZone") as string[]; } catch { tzNames = FALLBACK_TIMEZONES; } // Chrome may omit "UTC" from the list if (!tzNames.includes("UTC")) tzNames = ["UTC", ...tzNames]; - const entries = tzNames.map((tz: string) => { + // Canonicalize + dedupe so deprecated aliases (e.g. Asia/Saigon) collapse into their canonical form. + const canonical = new Set(); + for (const tz of tzNames) { + const c = canonicalizeTimezone(tz); + if (c) canonical.add(c); + } + const entries = Array.from(canonical).map((tz: string) => { const offset = formatUtcOffset(tz); return { value: tz, label: `${tz} (${offset})`, _offset: parseOffsetMinutes(offset) }; });