package service // Legacy IANA timezone aliases → canonical names. // // Browsers report Intl.DateTimeFormat().resolvedOptions().timeZone, and the // ICU/CLDR data they ship still uses several LEGACY ids as its canonical // form — Safari and Chrome genuinely say "Asia/Calcutta", not "Asia/Kolkata". // Go's time.LoadLocation resolves these aliases, so they pass validation and // get stored; but a Postgres built against tzdata without the "backward" // links (Debian/Alpine images with the tzdata-legacy split) rejects them in // AT TIME ZONE with SQLSTATE 22023. Everything we store or hand to SQL goes // through canonicalTZ first. var legacyTZ = map[string]string{ // Ids ICU/CLDR still reports as canonical (the ones browsers send). "Asia/Calcutta": "Asia/Kolkata", "Asia/Katmandu": "Asia/Kathmandu", "Asia/Rangoon": "Asia/Yangon", "Asia/Saigon": "Asia/Ho_Chi_Minh", "America/Buenos_Aires": "America/Argentina/Buenos_Aires", "America/Catamarca": "America/Argentina/Catamarca", "America/Cordoba": "America/Argentina/Cordoba", "America/Jujuy": "America/Argentina/Jujuy", "America/Mendoza": "America/Argentina/Mendoza", "America/Indianapolis": "America/Indiana/Indianapolis", "America/Louisville": "America/Kentucky/Louisville", "America/Godthab": "America/Nuuk", "Atlantic/Faeroe": "Atlantic/Faroe", "Pacific/Ponape": "Pacific/Pohnpei", "Pacific/Truk": "Pacific/Chuuk", "Europe/Kiev": "Europe/Kyiv", // Other common backward links, in case a zone arrives from an older // client or was set by hand. "Asia/Ashkhabad": "Asia/Ashgabat", "Asia/Chongqing": "Asia/Shanghai", "Asia/Chungking": "Asia/Shanghai", "Asia/Dacca": "Asia/Dhaka", "Asia/Harbin": "Asia/Shanghai", "Asia/Istanbul": "Europe/Istanbul", "Asia/Kashgar": "Asia/Urumqi", "Asia/Macao": "Asia/Macau", "Asia/Tel_Aviv": "Asia/Jerusalem", "Asia/Thimbu": "Asia/Thimphu", "Asia/Ujung_Pandang": "Asia/Makassar", "Asia/Ulan_Bator": "Asia/Ulaanbaatar", "Africa/Asmera": "Africa/Asmara", "America/Coral_Harbour": "America/Atikokan", "Australia/ACT": "Australia/Sydney", "Australia/Canberra": "Australia/Sydney", "Australia/NSW": "Australia/Sydney", "Australia/North": "Australia/Darwin", "Australia/Queensland": "Australia/Brisbane", "Australia/South": "Australia/Adelaide", "Australia/Tasmania": "Australia/Hobart", "Australia/Victoria": "Australia/Melbourne", "Australia/West": "Australia/Perth", "US/Alaska": "America/Anchorage", "US/Arizona": "America/Phoenix", "US/Central": "America/Chicago", "US/Eastern": "America/New_York", "US/Hawaii": "Pacific/Honolulu", "US/Mountain": "America/Denver", "US/Pacific": "America/Los_Angeles", "GB": "Europe/London", "GB-Eire": "Europe/London", "Eire": "Europe/Dublin", "Egypt": "Africa/Cairo", "Hongkong": "Asia/Hong_Kong", "Iceland": "Atlantic/Reykjavik", "Israel": "Asia/Jerusalem", "Japan": "Asia/Tokyo", "NZ": "Pacific/Auckland", "Poland": "Europe/Warsaw", "Portugal": "Europe/Lisbon", "PRC": "Asia/Shanghai", "ROC": "Asia/Taipei", "ROK": "Asia/Seoul", "Singapore": "Asia/Singapore", "Turkey": "Europe/Istanbul", } // canonicalTZ returns the canonical IANA name for tz, resolving legacy // aliases; unknown values pass through unchanged. func canonicalTZ(tz string) string { if c, ok := legacyTZ[tz]; ok { return c } return tz }