Postgresql select output date with missing timezone offset - postgresql

I've got a hard time with postgresql and timezones.
The Postgresql server is in UTC time zone.
I want to perform a query that returns the date in another timezone with the timezone offset.
I can perform a query that returns the date in a timezone but I'm missing the timezone offset
base=# SET timezone to 'utc';
SET
base=# select now();
now
------------------------------
2018-04-20 14:58:22.68038+00
(1 row)
base=# SET timezone to 'Europe/Paris';
SET
base=# select now();
now
-------------------------------
2018-04-20 16:58:29.614383+02
(1 row)
base=# SET timezone to 'utc';
SET
base=# select now() AT TIME ZONE 'Europe/Paris';
timezone
----------------------------
2018-04-20 16:59:03.146917 -- missing timezone offset here
(1 row)
Expected result
base=# SET timezone to 'utc';
SET
base=# select now() AT TIME ZONE 'Europe/Paris'; --I'm missing something here, I guess
timezone
----------------------------
2018-04-20 16:59:03.146917+02 -- That's what I want
(1 row)
Do you have any idea how to do it?
Thanks

If you use AT TIME ZONE, PG returns a timestamp without time zone. If you cast it to back to a timestamp with time zone, you get your expected result:
SELECT (NOW() AT TIME ZONE 'Europe/Paris')::TIMESTAMPTZ;
Result:
2018-04-20 18:15:26.165+02
How do you get the +02 without setting the timezone? Here's the problem: NOW() AT TIME ZONE 'Europe/Paris' returns the current time in the specified time zone (without an offset), and casting it back to TIMESTAMPTZ gives you the offset of the current time zone (of your PG session) based on UTC. Since 'Europe/Paris' is +02 from UTC, if you've set your time zone to 'Europe/Paris', the offset you get in a TIMESTAMPTZ is +02. If your current time zone setting is UTC, your offset is +00.
How to get the offset no matter what your PG session's time zone setting, without explicitly setting the time zone? I would like to think there's a better way to get the offset, but without knowing one, here's one way: calculate the offset, format it, append it to the timestamp without time zone.
SELECT (NOW() AT TIME ZONE 'Europe/Paris')::TEXT || TO_CHAR(EXTRACT(hour FROM NOW() AT TIME ZONE 'Europe/Paris' - NOW() AT TIME ZONE 'UTC'), 'FMSG00')
Result: 2018-04-21 15:12:42.658+02, and I get the same result no matter the current timezone.

Some timezone have an offset in 30 or 45 minutes.
Using TO_CHAR(EXTRACT(hour FROM NOW() AT TIME ZONE 'Europe/Paris' - NOW() AT TIME ZONE 'UTC') exclude this use case.
Did you consider to use SET LOCAL TIME ZONE 'Europe/Paris'; in a dedicated function ? According to the documentation :
If SET LOCAL is used within a function that has a SET option for the same variable (see CREATE FUNCTION), the effects of the SET LOCAL command disappear at function exit
This other reply seem's fitting your requirement : How to convert timestamp field to ISO 8601 string in a given time zone?

Related

Query returning UTC timezone although using AT TIME ZONE

I have data in GMT timezone. I want to convert and display them in 'America/New_York'.
This is the code I use:
(d.start_time) AT TIME ZONE 'America/New_York'
It returns let's say 2021-10-25T09:30:00.000Z.
9:30 is the correct time. But why is it a UTC time zone timestamp instead of 'America/New_York'?
I tried various variations of with/without time zone and ::timestamp/::timestamptz but I probably doesn't understand something very basic in here...
What should I do differently so that it's valid 9:30am timestamp in 'America/New_York' timezone?
The result is correct: 2021-10-25T09:30:00.000 is a timestamp without time zone, and it is not a UTC timestamp, but local time in New York City.
If you want the timestamp to be displayed with the time zone offset of New York City, you have to do something different:
SET timezone = 'America/New_York';
SELECT current_timestamp;
current_timestamp
═══════════════════════════════
2021-10-11 12:45:49.881037-04
(1 row)
Then PostgreSQL will display timestamp with time zone values with the offset you want.

PostgreSQL timestamps do not convert properly into Unixtimestamps

I just started with PostgreSQL. Since we use a lot of Unixtimestamps I figured it would be best to just try a little example. So I made a table test with a field ID and a field timestamp of type timestamp without time zone using phpPgAdmin. There is valid data.
Now I just wanted to retrieve the data using a Unixtimestamp. And I run into really odd behavior.
I made this little test which selects data by just converting the first best timestamp into a unixtimestamp and back into a timestamp:
This will return the timestamp in the raw and unix value for the first entry
SELECT timestamp, extract(epoch FROM timestamp)
FROM test
WHERE id IN (SELECT id FROM test LIMIT 1);
Ok, that worked as intended.
This does only convert therefore it should return one entry
SELECT timestamp, extract(epoch FROM timestamp)
FROM test
WHERE timestamp IN (SELECT TO_TIMESTAMP(extract(epoch FROM timestamp))
FROM test LIMIT 1);
Nothing is returned!
I just can't wrap my head around this behavior. If I convert a timestamp into something else and then convert it back it should be the same timestamp, right?
I just want a way to cast timestamps into unixtime and back without the chance that information is lost.
Please note: this is just a test! I do not want to use this code. I just want to check if the SQL code behaves as expected!
Also, I can not do anything about the unixtimestamps! I just want a way to cast them properly!
The difference of not using timestamp type without time zone vs with:
select '09/12/2020 11:16 PDT'::timestamp, to_timestamp(extract(epoch from '09/12/2020 11:16 PDT'::timestamp));
timestamp | to_timestamp
---------------------+-------------------------
09/12/2020 11:16:00 | 09/12/2020 04:16:00 PDT
select '09/12/2020 11:16 PDT'::timestamptz, to_timestamp(extract(epoch from '09/12/2020 11:16 PDT'::timestamptz));
timestamptz | to_timestamp
-------------------------+-------------------------
09/12/2020 11:16:00 PDT | 09/12/2020 11:16:00 PDT
UPDATE. For more detail see. the important part is:
In a literal that has been determined to be timestamp without time zone, PostgreSQL will silently ignore any time zone indication. That is, the resulting value is derived from the date/time fields in the input value, and is not adjusted for time zone.
For timestamp with time zone, the internally stored value is always in UTC (Universal Coordinated Time, traditionally known as Greenwich Mean Time, GMT). An input value that has an explicit time zone specified is converted to UTC using the appropriate offset for that time zone. If no time zone is stated in the input string, then it is assumed to be in the time zone indicated by the system's TimeZone parameter, and is converted to UTC using the offset for the timezone zone.
When a timestamp with time zone value is output, it is always converted from UTC to the current timezone zone, and displayed as local time in that zone. To see the time in another time zone, either change timezone or use the AT TIME ZONE construct (see Section 9.9.3).
This means:
select '09/12/2020 11:16 PDT'::timestamp;
timestamp
---------------------
09/12/2020 11:16:00
select '09/12/2020 11:16'::timestamp at time zone 'UTC';
timezone
-------------------------
09/12/2020 04:16:00 PDT
select '09/12/2020 11:16 PDT'::timestamp at time zone 'UTC';
timezone
-------------------------
09/12/2020 04:16:00 PDT
select '09/12/2020 11:16 PDT'::timestamptz;
timestamptz
-------------------------
09/12/2020 11:16:00 PDT
select '09/12/2020 11:16 PDT '::timestamptz at time zone 'UTC';
timezone
---------------------
09/12/2020 18:16:00
So in the first case the time zone is ignored and the time is taken to be 09/12/2020 11:16 . In the second and third cases the timestamp is taken as being at UTC and the displayed value is rotated to my time zone PDT.In the third case I'm using timestamptz so the timestamp is correctly tagged with the time zone. This means when I ask to display at UTC it does the correct thing.
As documented in the manual to_timestamp() returns a timestamp WITH time zone, so obviously the comparison with a timestamp WITHOUT time zone won't work.
You will need to convert the result from to_timestamp() back to a timestamp without time zone, by telling Postgres which time zone it should take:
SELECT "timestamp",
extract(epoch FROM "timestamp"),
cast("timestamp" as timestamp with time zone),
TO_TIMESTAMP(extract(epoch FROM "timestamp")) at time zone 'UTC'
FROM test
where "timestamp" in (select to_timesatmp(extract(epoch FROM "timestamp")) at time zone 'UTC'
from test
limit 1)
Which is essentially the same as:
SELECT "timestamp",
extract(epoch FROM "timestamp"),
cast("timestamp" as timestamp with time zone),
TO_TIMESTAMP(extract(epoch FROM "timestamp")) at time zone 'UTC'
FROM test
where "timestamp" in (select "timestamp"
from test
limit 1)
But the whole converting back and forth between a number and a proper timestamp is useless - and error prone if you mix different data types (as you have just discovered). The best thing is to keep everything as a timestamp or better as a timestamptz.
For a short introduction why timestamptz (with time zone)` is in general a better choice, you might want to read this

Convert a UTC timezone in postgresql to EST (local time)

I am new to PostgreSQL and I was wondering if there is a direct way to just convert the timestamp values in a table to a different timezone using a function. In my case it is UTC to EST.
These are the values for example that I need to convert to EST (not just one value but all the values in the table)
date
-------------------
2015-10-24 16:38:46
2016-01-19 18:27:00
2016-01-24 16:14:34
2016-02-09 23:05:49
2016-02-11 20:46:26
Here in London, we are currently 1 hour ahead of UTC. So - if I take your timezone without timestamp and say it is in UTC I will get it printed for my local timezone.
richardh=> SELECT ((timestamp '2015-10-24 16:38:46') AT TIME ZONE 'UTC');
timezone
------------------------
2015-10-24 17:38:46+01
(1 row)
But you want "EST" which seems to be somewhere in the Americas, judging by the value returned. You can wrap the expression in a little SQL function if you wanted to.
richardh=> SELECT ((timestamp '2015-10-24 16:38:46') AT TIME ZONE 'UTC') AT TIME ZONE 'EST';
timezone
---------------------
2015-10-24 11:38:46
(1 row)
Edit: how to do it in a query
SELECT ((stored_timestamp AT TIME ZONE 'UTC') AT TIME ZONE 'EST') AS local_timestamp
FROM my_table;
Similarly
execute
SELECT '2015-10-24 16:38:46'::timestamp AT time zone 'EST';
timezone
------------------------
2015-10-24 21:38:46+00
(1 row)
I usually leave everything in UTC and convert when it is time to show.
I use something like:
SELECT my_date_utc AT time zone 'utc' at time zone 'est' From ....
If you have problem accessing with your zone, you can simply pass your zone interval also.
To convert timestamp from IST to UTC.
SELECT '2020-12-14 06:38:46'::timestamp AT time zone INTERVAL '+05:30';
timezone
------------------------
2015-10-24 11:38:46+00
(1 row)
To convert timestamp from UTC to IST.
SELECT '2020-12-14 06:38:46'::timestamp AT time zone INTERVAL '-05:30';
timezone
------------------------
2020-12-14 12:08:46+00
(1 row)
It is 12:22 here in Los Angeles now.
I find that I have to reverse the UST and america/los_angeles arguments:
ods=> SELECT NOW(),(NOW() AT TIME ZONE 'america/los_angeles') AT TIME ZONE 'utc';;
now | timezone
-------------------------------+-------------------------------
2022-04-22 19:22:35.943605+00 | 2022-04-22 12:22:35.943605+00
(1 row)
Am I missing something?
You should always store the main reference of a date in UTC and either convert it to a time zone in your queries or store the specific timezone version of the data in another column. The reason for this is that it is quick and easy to convert a date from UTC to another time zone as long as you know that the timezone that it is stored as is UTC. It takes the guess work out of it. Alternatively, you can store the date WITH the timezone.
If you have an operation that automatically populates the date with the system clock of your server, then you can either
A: Change the operation to use UTC time
B: Change the system clock on the server to UTC
I had the same problem, I am working with different regions and timezones, I need to just fix the timezone in the query the way it doesn't effect other customers around the regions and I havent changed the table structure or any thing(Open–closed principle) . What I did In my query:
SELECT TO_CHAR(current_timestamp at time zone 'Australia/Melbourne', 'DD/MM/YYYY hh24:mi AM') as date_of_extract
This worked for me and I could change the 'UTC' defult timezone for my postgressql to the 'Australia/Melbourne'(any time zone you are looking into). hope this is helpful.
Building off of #Leandro Castro's answer...
To get current time in in timezone, use the CURRENT_TIME function:
SELECT CURRENT_TIME(0) AT time zone 'utc' at time zone 'est';

Postgres AT TIME ZONE function shows wrong time?

I am using transformation to new timezone UTC+3 which is equal to EAT timezone, but Postgres (9.1) shows the wrong time
select '2015-01-13 08:40:00.0'::timestamp with time zone AT TIME ZONE 'UTC+03',
'2015-01-13 08:40:00.0'::timestamp with time zone AT TIME ZONE 'EAT';
(default timezone is Stockholm here)
The result is
"2015-01-13 04:40:00",
"2015-01-13 10:40:00"
Why?
it should be 2015-01-13 10:40:00
if using JodaTime with both timezones then it shows the same correct result '2015-01-13 10:40:00'.
From the Postgres documentation there is the option to use ::timestamptz instead of ::timestamp WITH TIME ZONE and I found preferred results when making the conversion; as it is the most concise of the available options while still being readable.
SELECT created_at
,created_at::timestamp AT TIME ZONE 'EDT' -- yields bad result
,created_at::timestamp WITH TIME ZONE AT TIME ZONE 'EDT'
,created_at AT TIME ZONE 'UTC' AT TIME ZONE 'EDT'
,created_at::timestamptz AT TIME ZONE 'EDT'
2019-03-29 18:49:25.250431 -- raw UTC data
2019-03-29 22:49:25.250431 -- erroneous result
2019-03-29 14:49:25.250431 -- accurate result
2019-03-29 14:49:25.250431 -- accurate result
2019-03-29 14:49:25.250431 -- accurate result
I had similar problem, it gave me the wrong date and time but this answer here gave me a clear understanding and fixed my problem. PostgreSQL wrong converting from timestamp without time zone to timestamp with time zone
So what I did was changing from
SELECT timestamp AT TIME ZONE '+08' FROM orders;
to
SELECT timestamp AT TIME ZONE 'UTC' AT TIME ZONE '+08' FROM orders;
A time zone name spelled like 'UTC+3:00' is a POSIX time zone specification.
In this style, zones west of GMT have a positive sign and those east have a negative sign in their name (e.g "Etc/GMT-14" is 14 hours ahead/east of GMT.)
See http://www.postgresql.org/docs/9.3/static/datatype-datetime.html#DATATYPE-TIMEZONES
EAT(East Africa Time) is three hours ahead of UTC (UTC+03:00).
It's represented as UTC+03:00 in ISO-8601 format.
But AT TIME ZONE only supports timezones represented in POSIX-style.
In POSIX-style, the positive sign is used for zones west of Greenwich. (Note that this is the opposite of the ISO-8601 sign convention used elsewhere in PostgreSQL.)
Format
PST
ART
W←UTC→E
EAT
HKT
ISO-8601
UTC-08
UTC-03
UTC+00
UTC+03
UTC+08
POSIX-style
UTC+08
UTC+03
UTC+00
UTC-03
UTC-08
In POSIX-style, the correct representation of EAT can be:
'UTC-03'
'UTC-3'
'-3'
-3
The result of the following SQL statement must be TRUE.
select now() at time zone -3 = now() at time zone 'EAT';
Confusingly, set time zone statement supports ISO-8601, not POSIX-style.
Execute the following SQL statements in sequence to find the difference.
#
SQL
Result
①
set time zone -3;
SET
②
select now();
2022-02-24 04:53:41.921391-03
③
select timestamp '2022-02-24 12:00' at time zone -3;
2022-02-24 06:00:00-03
④
select timestamp '2022-02-24 12:00' at time zone +3;
2022-02-24 12:00:00-03
⑤
set time zone 3;
SET
⑥
select now();
2022-02-24 10:54:10.283953+03
⑦
select timestamp '2022-02-24 12:00' at time zone -3;
2022-02-24 12:00:00+03
⑧
select timestamp '2022-02-24 12:00' at time zone +3;
2022-02-24 18:00:00+03
⑨
set time zone local;
SET
⑩
select now();
2022-02-24 15:54:51.79643+08
⑪
select now() at time zone -3;
2022-02-24 10:55:02.209886
⑫
select now() at time zone 3;
2022-02-24 04:55:09.498461
⚠️Please be careful about this.
References:
AT TIME ZONE
Time Zones
POSIX Time Zone Specifications
SET TIME ZONE

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