Timestamp format with offset in postgres - postgresql

This is really stumping me and it doesn't seem like it should be that difficult, but in postgres 9.6, I'm trying to format a timestamp with the offset.
Here's the closest I've gotten:
SELECT to_char('2017-11-06 00:00:00'::TIMESTAMP WITH TIME ZONE AT TIME ZONE 'America/Vancouver', 'MM/DD/YYYY HH24:MI:SS (OF)');
The above example gets the right date, but the offset is +00, which is incorrect.
Any ideas?
EDIT:
Additionally, how do I set this in a function? The following doesn't work:
DECLARE
_tz text = 'PST8PDT';
BEGIN
SET LOCAL TIME ZONE _tz;
...

First you set the time zone of the session, then you set the timezone that the timestamp is in. Like so:
SET TIME ZONE 'America/Vancouver';
SELECT to_char('2017-11-06 00:00:00'::TIMESTAMP AT TIME ZONE 'UTC', 'MM/DD/YYYY HH24:MI:SS (OF)');

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.

Convert timestamp to interval in postgresql

I am trying to convert a timestamp to interval & expecting an out of hh:mm only.
My code is like below
SELECT to_timestamp('2020-01-10 08:00', 'YYYY-MM-DD hh24:mi')::timestamp without time zone
at time zone 'Asia/Calcutta'
at time zone 'Etc/UTC'
Actual purpose of the code is change the time zone into utc for a given date time value.
It seems you are trying to get the timedifference (interval) between two timezones at a specific time.
This can be done like this for example:
SELECT to_timestamp('2020-01-10 08:00', 'YYYY-MM-DD hh24:mi')::timestamp without time zone
at time zone 'Asia/Calcutta'
-
to_timestamp('2020-01-10 08:00', 'YYYY-MM-DD hh24:mi')::timestamp without time zone
at time zone 'Etc/UTC'
This returns an interval = -05:30:00
You can of course convert it to hours and minutes with the to_char function, but that returns string, not interval.
Best regards,
Bjarni

Postgresql select output date with missing timezone offset

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?

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

Now() without timezone

I have a column added_at of type timestamp without time zone. I want it's default value to be the current date-time but without time zone. The function now() returns a timezone as well.
How do I solve that problem?
SELECT now()::timestamp;
The cast converts the timestamptz returned by now() to the corresponding timestamp in your time zone - defined by the timezone setting of the session. That's also how the standard SQL function LOCALTIMESTAMP is implemented in Postgres.
If you don't operate in multiple time zones, that works just fine. Else switch to timestamptz for added_at. The difference?
Ignoring time zones altogether in Rails and PostgreSQL
BTW, this does exactly the same, just more noisy and expensive:
SELECT now() AT TIME ZONE current_setting('timezone');
Well you can do something like:
SELECT now() AT TIME ZONE current_setting('TimeZone');
SELECT now() AT TIME ZONE 'Europe/Paris';
SELECT now() AT TIME ZONE 'UTC';
Not sure how that makes any sense for a column "added_at". You almost always want an absolute timestamp (timestamp with time zone) not a floating one.
Edit responding to points below:
Yes, should use timestamp with time zone (absolute time) unless you have a good reason not to.
The client timezone is given by SHOW TimeZone or current_setting(...) as shown above.
Do take some time to skim the manuals - they cover all this quite well.
"Current Date/Time":
CURRENT_TIME and CURRENT_TIMESTAMP deliver values with time zone; LOCALTIME and LOCALTIMESTAMP deliver values without time zone.
New, and Native Answer in 2020
In PostgreSQL, If you only want the current date-time by calling CURRENT_TIMESTAMP() without time zone, and fractional digits in the seconds field which come after the decimal point of the seconds field?
(Tested on PostgreSQL v12.4)
Then use this:
SELECT CURRENT_TIMESTAMP(0)::TIMESTAMP WITHOUT TIME ZONE;
If you define your column's data type as timestamp (not as timestamptz), then you can store the timestamp without time zone, in that case you don't neet to add TIMESTAMP WITHOUT TIME ZONE
Like this:
CREATE TABLE foo (created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP(0))
In the above function, 0 is passed to get rid of the fractional digits in the seconds field.
If your application doesn't care about timezone, you can use SELECT LOCALTIMESTAMP for it.
Ex:
SELECT LOCALTIMESTAMP
-- Result: 2023-01-30 17:43:33.628952