I faced with the following issue this morning:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
returns me 2011-12-30 05:30:00+00
witch is wrong.
But next queries below:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'UTC-5';
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
i see right date 2011-12-29 19:30:00
Preventing your question about my local timezone:
SELECT current_setting('TIMEZONE');
current_setting
-----------------
UTC
(1 row)
Do anyone have answer why postgresql converts timestamp without time zone
some weird way and instead taking away 5 hours it adds instead?
Key things to understand
timestamp without time zone AT TIME ZONE
re-interprets atimestamp
as being in that time zone for the purpose of converting it to UTC.timestamp with time zone AT TIME ZONE
converts atimestamptz
into atimestamp
at the specified timezone.PostgreSQL uses ISO-8601 timezones, which specify that east of Greenwich is positive ... unless you use a POSIX timezone specifier, in which case it follows POSIX. Insanity ensues.
Why the first one produces an unexpected result
Timestamps and timezones in SQL are horrible. This:
inteprets the unknown-typed literal
'2011-12-30 00:30:00'
astimestamp without time zone
, which Pg assumes is in the local TimeZone unless told otherwise. When you useAT TIME ZONE
, it is (per the spec) re-interpreted as atimestamp with time zone
in the time zoneEST5EDT
then stored as an absolute time in UTC - so it's converted fromEST5EDT
to UTC, i.e the timezone offset gets subtracted.x - (-5)
isx + 5
.This timestamp, adjusted to UTC storage, is then adjusted for your server
TimeZone
setting for display so that it gets displayed in local time.If you instead wish to say "I have this timestamp in UTC time, and wish to see what the equivalent local time in EST5EDT is", if you want to be independent of the server TimeZone setting, you need to write something like:
This says "Given timestamp 2011-12-30 00:30:00, treat it as a timestamp in UTC when converting to timestamptz, then convert that timestamptz to a local time in EST5EDT".
Horrible, isn't it? I want to give a firm talking to whoever decided on the crazy semantics of
AT TIME ZONE
- it should really be something liketimestamp CONVERT FROM TIME ZONE '-5'
andtimestamptz CONVERT TO TIME ZONE '+5'
. Also,timestamp with time zone
should actually carry its timezone with it, not be stored in UTC and auto-converted to localtime.Why the second works (so long as TimeZone = UTC)
Your original "works" version:
will only be correct if TimeZone is set to UTC, because the text-to-timestamptz cast assumes TimeZone when one isn't specified.
Why the third one works
Two problems cancel each other out.
The other version that appears to work is TimeZone independent, but it only works because two problems cancel themselves out. First, as explained above,
timestamp without time zone AT TIME ZONE
re-interprets the timestamp as being in that time zone for conversion to a UTC timestamptz; this effectively subtracts the timezone offset.However, for reasons I beyond my ken, PostgreSQL uses timestamps with the reverse sign to what I'm used to seeing most places. See the documentation:
This means that
EST5EDT
is the same as+5
, not-5
. Which is why it works: because you're subtracting the tz offset not adding it, but you're subtracting a negated offset!What you'd need to get it correct is instead: