PostgreSQL: Aliases for "timestamp with time zone" and "at time zone" - postgresql

I happen to have tables with timestamp without time zone column in my database. Now I need to compare them with respect to time zone.
I can do it like:
select my_timestamp::timestamp with time zone at time zone 'EST5EDT'
from my_table
where
my_timestamp >= '2010-07-01'::timestamp at time zone 'EST5EDT'
and my_timestamp < '2010-08-01'::timestamp at time zone 'EST5EDT'
This is getting pretty ugly. Is there a shorter way that does not require me to type timestamp with time zone as type and at time zone as conversion?

timestamptz is an alias for timestamp with time zone, timestamp is an alias for timestamp without time zone.
I don't think at time zone has an alias.

Related

PostgreSQL "date at time zone" unexpected behaviour

The behavior of the at time zone operator on the date type is not as expected in PostgreSQL.
set time zone 'UTC';
select
'2021-01-03'::date at time zone 'America/Chicago' as "value",
pg_typeof('2021-01-03'::date at time zone 'America/Chicago') as "type";
x
value
type
Expected
2021-01-03 06:00:00+00
timestamp with time zone
Actual
2021-01-02 18:00:00
timestamp without time zone
DB Fiddle
Tested on PostgreSQL server versions 10, 11, 12, and 13.
This behavior is very strange to me, and I have not been able to find anywhere it is described or discussed. It seems PSQL is implicitly casting date to timestamptz (using the system time zone) and then applying the at time zone operator. I know that the at time zone operator is only defined (in the docs) for the timestamp(tz) and time(tz) types. However, I would have assumed PSQL would implicitly cast date to timestamp and not timestamptz because:
It is a "safe" conversion (no need to assume a time zone). Simply append 00:00:00.
It results in a timestamptz which one would expect when using the at time zone operator on a type that does not have a time zone.
The current behavior simply doesn't make sense to me and I can't think of a use case that would benefit from it. You are asking for the timestamp with time zone for a date at a particular time zone and instead you get a timestamp of the date at the system time zone localized to the given time zone.
Of course, a simple workaround is to cast to timestamp first:
set time zone 'UTC';
select '2021-01-03'::date::timestamp at time zone 'America/Chicago';
I was hoping someone could shed some light on this. What am I missing? Perhaps some overarching rule about how date is implicitly cast to timestamp(tz)? Is this behavior documented somewhere?
Thanks!
Mike
You ask "why". The reason is that AT TIME ZONE does not operate on date, so PostgreSQL invokes an implicit type cast to convert it to a different data type. Lacking an explicit directive, PostgreSQL opts for the preferred data type for datetime, which happens to be timestamp with time zone.
The data type resolution rules for operators are documented here, and the preferred type for a type category can be found in the typispreferred and typcategory columns of the pg_type system catalog.
As per the Postgresql documentation:
The AT TIME ZONE converts time stamp without time zone to/from time stamp with time zone, and time values to different time zones
Here in first query, you are trying to apply at time zone to date type, not to time stamp
set time zone 'UTC';
select
'2021-01-03'::date at time zone 'America/Chicago' as "value",
pg_typeof('2021-01-03'::date at time zone 'America/Chicago') as "type";
which is eventually gets ignored, and you will get a timestamp without timezone
But in second query,
set time zone 'UTC';
select '2021-01-03'::date::timestamp at time zone 'America/Chicago';
You are casting the date to time stamp first and then converting it to desired time zone,
which gives you the expected result of time stamp with time zone.
Hope this clears your doubt.

What is difference between 4 ways convert timezone in postgresql

I don't know What is difference between 4 ways convert timezone in postgresql:
SELECT (timestamp '2018-01-20 00:00:00' at time zone 'Asia/Saigon') at time zone 'UTC';
SELECT CAST('2018-01-20 00:00:00' as timestamp without time zone) at time zone 'Asia/Saigon' at time zone 'UTC'
SELECT (TO_TIMESTAMP('2018-01-20 00:00:00', 'YYYY-MM-DD HH24:MI:SS') at time zone 'Asia/Saigon') at time zone 'UTC'
SELECT ('2018-01-20 00:00:00' at time zone 'Asia/Saigon') at time zone 'UTC';
The results are different. Why?
The first two statements do the same thing.
The difference is the way in which a constant of type timestamp without time zone is created, but the result is the same in both cases.
The third statement creates a timestamp with time zone using to_timestamp, where the string is interpreted in your session time zone. This is then converted to a timestamp without time zone as the wall clock in Saigon would show, and then converted to a timestamp with time zone imagining the wall clock were teleported to UTC.
The fourth statement does the same as the third, because the string is implicitly cast to timestamp with time zone. There is an ambiguity here because AT TIME ZONE can also be applied to timestamp without time zone, but in case of doubt the preferred type of its category is used, which is timestamp with time zone.
The SQL standard differentiates timestamp without time zone and timestamp with time zone literals by the presence of a "+" or "-" symbol and time zone offset after the time. Hence, according to the standard
Also you can see below articles:
Section 8.5.1.3. Time Stamps
Time zone

Convert timestamp in UTC to timestamptz

How do I convert a timestamp that is in UTC to timestamptz?
If my local time zone is GMT-1 and I run:
select '2017-01-01 00:00:00'::timestamptz
I get:
2017-01-01 00:00:00-01
but I want:
2017-01-01 01:00:00-01
Better:
SELECT timestamp '2017-01-01 00:00:00' AT TIME ZONE 'UTC';
No additional cast after the AT TIME ZONE construct. It returns timestamp with time zone (= timestamptz) for timestamp without time zone (= timestamp) input and vice versa.
For the given example, the shortest, most efficient way to provide a timestamp constant is timestamp '2017-01-01'. Or use a cast, almost as good: '2017-01-01'::timestamp. The time component 00:00:00 is assumed when missing.
There is no such thing as a "timestamp that is in UTC". A timestamp carries no time zone information. Only you know that it's supposed to be located in the UTC time zone.
The type name "timestamp with time zone" is a bit misleading. timestamptz does not carry any time zone information, either. The given time zone name, abbreviation or offset is used to compute the corresponding UTC time. Text output (display) is adapted to the current time zone setting of your session. Only the bare value of the corresponding UTC time is stored. The time zone itself is never stored. If you need it, store it additionally in another column. In your particular case, UTC happens to be the time zone used for input as well.
Detailed explanation:
Time zone storage in data type "timestamp with time zone"
Ignoring time zones altogether in Rails and PostgreSQL
Solution: select ('2017-01-01 00:00:00' at time zone 'utc')::timestamptz

How do I insert a universally coordinated time into a PostgreSQL TIMESTAMP column?

My database and client are currently in America/New_York, but could be moved anywhere.
SHOW TIMEZONE; -- America/New_York
CREATE TABLE times (
t TIMESTAMP WITHOUT TIME ZONE
);
INSERT INTO times VALUES
(NOW()),
(NOW() AT TIME ZONE 'UTC');
SELECT t FROM times;
-- 2017-06-13 14:53:17.766969
-- 2017-06-13 18:53:17.766969
This is unexpected. I thought my SELECT would return the same value for both records.
When I insert the current time into column t, I want it to mean the current time in any time zone (whether the database stores the underlying value as current time UTC or not). This way, regardless of the time zone the database is operating in, or the time zone the client is operating in, everybody can agree on the universally coordinated fixed point in time.
What is the proper way to INSERT a record so that everyone in the world knows I'm referencing a 'UTC' time?
First, it seems like SELECT assumes that timestamp without time zone fields are stored in the local time zone. Try SET TIME ZONE 'UTC+<n>. The output of SELECT does not change (i.e., it's not as if timestamp means UTC if no timezone is stored!)
When a field it of type timestamp with time zone, SELECT correctly converts the output to the current time zone.
So when you say AT TIME ZONE 'UTC' you convert the time to UTC, then discard the timezone making Postgres think you are dealing with local time.
You can re-introduce the UTC time zone by adding another AT TIME ZONE 'UTC' clause:
# select pg_typeof(now() at time zone 'utc');
timestamp without time zone
# select pg_typeof(now() at time zone 'utc' at time zone 'utc');
timestamp with time zone
Bottom line: I would either always use a TIME WITH TIME ZONE or always have the database time zone set to UTC if you insist on working without time zones. The storage size of both types seems identical (64 bit).

How to convert local timestamp (of a given time zone) to UTC?

I am working in a code where multiple time zones will be available. I need to create a function which takes the selected local timestamp as well as the local time zone. The return value of the function should the UTC time of the local time (local time for the given local time zone).
I am working on PostgreSQL 8.3
SELECT Now() AT TIME ZONE 'UTC';
This will return timestamp without time zone at specified time zone (UTC in my example) for given timestamp with time zone.
From PostgreSQL docs:
SELECT TIMESTAMP '2001-02-16 20:38:40' AT TIME ZONE 'MST';
Result: 2001-02-16 19:38:40-08
SELECT TIMESTAMP WITH TIME ZONE '2001-02-16 20:38:40-05' AT TIME ZONE 'MST';
Result: 2001-02-16 18:38:40
You can get list of time zones from pg_timezone_names:
select * from pg_timezone_names limit 5
name;abbrev;utc_offset;is_dst
----------------------
PRC;CST;08:00:00;f
Asia/Brunei;BNT;08:00:00;f
Asia/Ujung_Pandang;CIT;08:00:00;f
Asia/Ust-Nera;VLAT;11:00:00;f
Asia/Phnom_Penh;ICT;07:00:00;f