Here’s a scenario: Users are saving time only in a database as a string, e.g. 20:00
. They expect something to happen every day at 8 PM. But 8 PM means a different time for different people given each lives in a different timezone, so the stored time is relative to each person.
Every hour, a server running in UTC runs a query to pull people for whom something should happen (we store their time zone separately).
The query boils down to comparing UTC server time (left side) with their relative stored time in the database (right side):
SELECT '20:00'::time = '12:00'::time AT TIME ZONE 'PST'
But there’s an issue on the right side: Although '12:00'::time
is sort of a timezone-less object, '12:00'::time IN TIME ZONE 'PST'
returns 04:00:00-08
as if we would be converting UTC time to PST time, which is not what we want.
Alternatively, I tried comparing these as strings using:
SELECT to_char('20:00'::time AT TIME ZONE 'PST', 'HH24:MI') = '12:00'
But this throws an error function to_char(time with time zone, unknown) does not exist
. It seems that to_char
cannot take time with zone?
So, trying to compare times or strings, I was not able to make either approach work. Any ideas on how to make either approach work? Any alternative approaches? Or any way to force postgres to treat 12:00
as 12 AM PST?
2
Answers
There is one so similar to your question. Please read the answer in this one
Strictly speaking you’re comparing a timezone unaware type on the left to timezone aware type on the right, which results in upcasting the one on the left by assuming the default
TimeZone
.It boils down to conversion rules and currently configured (default) settings:
You can use
at time zone
as many times as you need, each time flipping the input betweenwith
andwithout
thetime zone
. In this case you probably want just one more on either side of the equation:cast the left operand to a timezone-aware time in
'PST'
:or add another shift from
'PST'
to'UTC'
on the right to both shift it and drop its timezone after that:When you compare
time
totimetz
Postgres uses its type coercion/conversion rules to find a matching=
operator, because there’s no built-intime=timetz
:There’s only a
time=time
and atimetz=timetz
. If you try to mix them, those rules will pick the timezone-aware variant as the preference:As a result, you end up with
time::timetz=timetz
operation.When the cast
time::timetz
happens, Postgres assumes the defaultTimeZone
with zero regard for the context of this cast. Even though you clearly specified'PST'
on the right, the left will use whateverTimeZone
is set as the default at your transaction, session, user, database or system level right now – unless overriden.According to this, it would indicate that you’re one 9.6 or earlier. Latest supported version is 12, until November 14th 2024.