gotchaMajor
Postgres AT TIME ZONE reads 'CET', 'PST', 'IST' as fixed-offset abbreviations while JS Intl treats them as DST-aware IANA zones
Viewed 0 times
PostgreSQL 17.6, Node 24 (ICU)
AT TIME ZONEpg_timezone_abbrevsCETIntl.DateTimeFormat timeZoneIANA zone validationdaylight saving
Error Messages
Problem
A time zone string validated in JavaScript with Intl.DateTimeFormat (it accepts 'CET', 'EET', 'WET', 'MET', 'PST', 'EST', 'IST', 'JST'...) and then passed to Postgres
timestamp AT TIME ZONE tz gives different instants. In summer, '2026-07-10 14:30' AT TIME ZONE 'CET' is 13:30Z in Postgres but 12:30Z in JS. 'IST' is +02:00 in Postgres (Israel) but Asia/Calcutta (+05:30) in Intl. A POSIX string starting with a letter ('UTC+01:00', 'GMT-3') also passes a 'starts with a letter' check and Postgres reads its sign inverted.Solution
Accept only 'UTC' or an IANA 'Area/Location' name, on both sides, with the same regex: ^(UTC|[A-Za-z_]+(/[A-Za-z0-9_+-]+)+)$ . Abbreviations never contain a slash, and every name returned by Intl.supportedValuesOf('timeZone') matches it (it is UTC plus Area/Location names). In plpgsql, additionally check that Postgres knows the name once, with
begin perform now() at time zone tz; exception when invalid_parameter_value then ... end;, so an unknown name raises your own message up front instead of Postgres' English error only on the first value without an offset. Note that the exception sub-block makes the function unsuitable for PARALLEL SAFE (subtransaction).Why
Postgres resolves the zone argument against pg_timezone_abbrevs (the timezone_abbreviations set) before looking up IANA zone names, so 'CET' becomes a fixed +01:00 offset with no daylight saving. ICU/Intl instead maps these legacy IDs to IANA zones (CET -> Europe/Brussels, PST -> America/Los_Angeles) with DST. POSIX-style strings are parsed by pg_tzset with the POSIX sign convention.
Revisions (0)
No revisions yet.