Reputation: 5479
I have a time with time zone
column, but when the timezones are different the query does not behave has expected.
The table:
CREATE TABLE timezone_table (
id serial NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
"time" time with time zone NOT NULL,
CONSTRAINT timezone_table_pkey PRIMARY KEY (id)
);
Here the insert statement to populate:
insert into timezone_table (id, created_at, updated_at, "time")
values (1, now(), now(), '12:00:00 UTC');
The troublesome query (doesn't find the record as expected):
SELECT * FROM "timezone_table" WHERE "timezone_table"."time" = '08:00:00 -0400';
A query that works as expected (finds the record):
SELECT * FROM "timezone_table" WHERE "timezone_table"."time" = '12:00:00 -0000';
So the question is, why would the troublesome query not return the row as expected when 08:00:00 -0400
is equivalent to 12:00:00 UTC
(or at least I would expect it to be equivalent)?
The weird part is if the troublesome query uses a less-than operator, it finds the row no problem:
SELECT * FROM "timezone_table" WHERE "timezone_table"."time" < '08:00:00 -0400';
Upvotes: 1
Views: 525
Reputation: 658907
The reason is, as mentioned in the comments, that timetz
is broken by design. Don't use it. Postgres advises against it, per documentation:
The type
time with time zone
is defined by the SQL standard, but the definition exhibits properties which lead to questionable usefulness. In most cases, a combination ofdate
,time
,timestamp without time zone
, andtimestamp with time zone
should provide a complete range of date/time functionality required by any application.
Most importantly, it completely breaks with DST (daylight saving time).
It's up for debate which concept is more more thoroughly broken, DST or timetz
.
I suggest timestamptz
instead. Just cast to retrieve the time
component (according to the local time zone): ts_value::time
.
While stuck with your design, use the AT TIME ZONE
construct to make the query "work":
SELECT * FROM timezone_table
WHERE "time" = '08:00:00 -0400'::timetz AT TIME ZONE 'UTC';
Or, to make it work universally (more or less - remember, the concept is broken):
SELECT * FROM timezone_table
WHERE "time" AT TIME ZONE 'UTC' = '08:00:00 -0400'::timetz AT TIME ZONE 'UTC';
This little demo should help to explain:
SELECT t1, t2
,t1 = t2 AS test_fail
,t1 AT TIME ZONE 'UTC' = t2 AT TIME ZONE 'UTC' AS test_ok
FROM ( SELECT '12:00:00 UTC'::timetz AS t1
,'08:00:00 -0400'::timetz AS t2) sub;
Related answer:
Upvotes: 2