I've spent a lot of time reading about 'best practices' when it comes to handling timestamps in Postgres, and have got a lot of conflicting answers. When using the TIMESTAMPTZ
data type, I had assumed that NOW()
and NOW() at time zone 'utc'
would result in the same data being inserted in the database. Numerous comments online have suggested that internally the timezone isn't actually stored, but instead converted to UTC.
Why is it then, that when I run the following I don't get duplicate results?
CREATE TABLE testtime(
mytime TIMESTAMPTZ,
descr VARCHAR
);
INSERT INTO testtime VALUES
(NOW(), 'Now at NZST'),
(NOW() AT TIME ZONE 'utc', 'Now at UTC');
SELECT *
FROM testtime;
Result:
2016-02-17 02:08:30.845071 Now at NZST
2016-02-16 13:08:30.845071 Now at UTC
The reason is the implicit type cast from timestamp
to timestamptz
.
now()
returns data type timestamptz
.now() AT TIME ZONE 'UTC'
returns data type timestamp
.(now() AT TIME ZONE 'UTC')::timestamptz
casts timestamp
to timestamptz
, assuming your current time zone in the process. This is where the difference is introduced.And this is what happens when you INSERT
a timestamp
value into a timestamptz
column. Postgres has to assume some time zone. You seem to have expected that UTC would be assumed. The more reasonable default is the current time zone setting, though. If you set UTC in your session, you get the behavior you expected.
Demo:
With my time zone 'Europe/Vienna', which is currently 1 hour ahead of UTC (during winter time):
SET timezone = 'Europe/Vienna';
SELECT now() AS now1
, now() AT TIME ZONE 'UTC' AS now2
, (now() AT TIME ZONE 'UTC')::timestamptz AS now3;
now1 | now2 | now3 -------------------------------+----------------------------+------------------------------- 2016-02-16 14:30:07.243082+01 | 2016-02-16 13:30:07.243082 | 2016-02-16 13:30:07.243082+01
The same with 'UTC' as time zone setting for the session:
SET timezone = 'UTC';
SELECT now() AS now1
, now() AT TIME ZONE 'UTC' AS now2
, (now() AT TIME ZONE 'UTC')::timestamptz AS now3;
now1 | now2 | now3 -------------------------------+----------------------------+------------------------------- 2016-02-16 13:30:58.739772+00 | 2016-02-16 13:30:58.739772 | 2016-02-16 13:30:58.739772+00
Note how the first two columns have identical values - even though the text representation in now1
looks different, because that is adjusted to the time zone of the session, the value is the same.
The third column has a different value, because a different time zone was assumed for the type cast.
Basics here: