PostgreSQL Format Timestamp with Timezone Offset (Australia/Sydney) as JSON/String - postgresql

I would like to get a JSON object in Postgres that display timestamp with Australia/Sydney timezone offset (+10:00 or +11:00 during daylight saving), for example I would like json object returned with following values:
"2021-07-31T23:59:59.123456+10:00"
"2021-01-31T23:59:59.123456+11:00"
But when I use either to_json() or to_char() the timestamp value returned is UTC with offset +00:00
select to_json(current_timestamp::timestamptz),
to_char(current_timestamp::timestamptz, 'YYYY-MM-DD"T"HH24:MI:SS:MSOF')
"2021-01-31T07:47:22.895185+00:00"
2021-01-31T07:47:22:895+00
I have tried to add "at time zone 'AEDT'" but it shifts the timestamp value and keep the offset to +00:00.
Thanks.

to_json formats according to the current time zone setting of the session. I suggest that you set the session time zone to Australia/Sydney first.
set time zone 'Australia/Sydney';
select to_json('2021-01-31T07:47:22.895+00'::timestamptz);
Yields 2021-01-31T18:47:22.895+11:00 which I guess is what you need.

Related

Postgres: How can I update timezone of a timestamp without changing the time?

I am using django models. TIMEZONE in django settings is UTC.
and constructing a timestamp by doing some arithmetic.
return queryset.annotate(
**{f"server_time":RawSQL(
f'''
SELECT TO_TIMESTAMP((FLOOR((EXTRACT(EPOCH FROM "{gmdts_table}"."date_key" AT TIME ZONE %s) + %s) / %s)::INTEGER * %s) - %s)::TIMESTAMP
''',
params=[str(requested_time_zone), bucket_offset, time_interval, time_interval, bucket_offset]
)}
)
I have timestamp being returned as 2021-07-26 00:00:00 when I am using ::timestamp
If I use ::TIMESTAMPTZ, it becomes 2021-07-26 00:00:00+00:00 even though requested_time_zone is 'America/New_York'
I want the output to be 2021-07-26 00:00:00-04:00 ie. show the same time with offset of 'America/New_York appended'. Essentially I just want to append the offset.
I have derived this timestamp by making some calculations and it actually belongs to the timezone 'America/New_York' (it can be any time zone say calculated_time_zone).
How can I update the timezone of this timestamp without actually changing the time for the output, which is:
2021-07-26 00:00:00-04:00
When I use AS TIME ZONE 'America/New_York', I get the output as 2021-07-26 04:00:00+00 which is not what I want.
In python, it can be easily done with a <datetime_object>.replace(tzinfo=new_time_zone). I am looking for the same thing in postgresql.
Any manner in which it can be done with django database functions would be helpful too (I wasn't able to find any)
It's important to understand that the timestamptz type does not store a timezone! It simply stores the UTC timestamp. Which means that your timestamp isn't "in the wrong timezone" because it isn't in any timezone. It is, however, displayed to you in a certain timezone, defined by your session settings.
If you need to shift your timestamp by four hours, as the case may be, then you can add an interval of 4 hours to it. But if you need to "reinterpret" the UTC timestamp to be a timestamp of a different timezone (which means you probably did something wrong when building the timestamp initially), then you can do so by switching to a timezone-naive timestamp and back again:
SELECT timestamp AT TIME ZONE 'UTC' AT TIME ZONE 'America/New_York'
But don't be surprised if this new timestamp is displayed in the same timezone as before, because that's defined by your session settings, not by the timestamp itself.
Strip off the timezone, then add back the new timezone you want. This can be done with:
(myfield::timestamp || '+00:00')::timestamptz

Postgres times: wrong conversion with time zones

Let's assume that we have some date, for example 2018-06-25T06:00:00Z. So, in case I do the following I get the same date:
select '2018-06-25T06:00:00Z'::timestamp at time zone 'UTC';
Now, I want to get local time for this date and as I am located in Helsinki I do the following:
select '2018-06-25T06:00:00Z'::timestamp at time zone 'Europe/Helsinki';
As I know that Helsinki is currently +3 hours my expectation is to get 2018-06-25 09:00:00.000000, however, for some reason I get 2018-06-25 03:00:00.000000.
So, why it decided to deduct this 3 hours instead of adding them?
Note, that this select current_setting('timezone') returns UTC.
Your second statement interprets the given timestamp as timestamp in the time zone Europe/Helsinki and converts it to a TIMESTAMP WITH TIMEZONE. psql prints the result of the timestamp according the the SESSION TIMEZONE.
To convert your timestamp from UTC to Europe/Helsinki, you should use a timestamp with time zone:
SELECT '2018-06-25T06:00:00Z'::TIMESTAMPTZ AT TIME ZONE 'Europe/Helsinki';
This will create a timestamp and convert it to Europe/Helsinki. You may change the time zone for output with:
SET SESSION TIMEZONE TO 'Europe/Helsinki';

Postgresql date timezone issue

I have a pg database with a column type timestamp with time zone. I inserted the following date:
2016-08-01 00:00:00 GMT
However, in the database, it shows up as:
2016-07-31 20:00:00-04
Does anyone know what might be going on?
Thanks in advance!
Despite the name, TIMESTAMP WITH TIME ZONE doesn't actually store the time zone. It uses the session's time zone to normalize to UTC, and stores UTC. On retrieval it converts back from UTC to the session time zone.
You can change the session time zone by using the SET TIME ZONE command. Preferably, you should use the standard IANA time zone identifiers. For example:
SET TIME ZONE 'Europe/Paris'
or
SET TIME ZONE 'UTC'
Alternatively use the TIMESTAMP [WITHOUT TIME ZONE] type instead, which does no conversions.

PostgreSQL wrong converting from timestamp without time zone to timestamp with time zone

I faced with the following issue this morning:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
returns me 2011-12-30 05:30:00+00 witch is wrong.
But next queries below:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'UTC-5';
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
i see right date 2011-12-29 19:30:00
Preventing your question about my local timezone:
SELECT current_setting('TIMEZONE');
current_setting
-----------------
UTC
(1 row)
Do anyone have answer why postgresql converts timestamp without time zone some weird way and instead taking away 5 hours it adds instead?
Key things to understand
timestamp without time zone AT TIME ZONE re-interprets a timestamp as being in that time zone for the purpose of converting it to UTC.
timestamp with time zone AT TIME ZONE converts a timestamptz into a timestamp at the specified timezone.
PostgreSQL uses ISO-8601 timezones, which specify that east of Greenwich is positive ... unless you use a POSIX timezone specifier, in which case it follows POSIX. Insanity ensues.
Why the first one produces an unexpected result
Timestamps and timezones in SQL are horrible. This:
select '2011-12-30 00:30:00'::timestamp without time zone AT TIME ZONE 'EST5EDT';
inteprets the unknown-typed literal '2011-12-30 00:30:00' as timestamp without time zone, which Pg assumes is in the local TimeZone unless told otherwise. When you use AT TIME ZONE, it is (per the spec) re-interpreted as a timestamp with time zone in the time zone EST5EDT then stored as an absolute time in UTC - so it's converted from EST5EDT to UTC, i.e the timezone offset gets subtracted. x - (-5) is x + 5.
This timestamp, adjusted to UTC storage, is then adjusted for your server TimeZone setting for display so that it gets displayed in local time.
If you instead wish to say "I have this timestamp in UTC time, and wish to see what the equivalent local time in EST5EDT is", if you want to be independent of the server TimeZone setting, you need to write something like:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE 'EST5EDT';
This says "Given timestamp 2011-12-30 00:30:00, treat it as a timestamp in UTC when converting to timestamptz, then convert that timestamptz to a local time in EST5EDT".
Horrible, isn't it? I want to give a firm talking to whoever decided on the crazy semantics of AT TIME ZONE - it should really be something like timestamp CONVERT FROM TIME ZONE '-5' and timestamptz CONVERT TO TIME ZONE '+5'. Also, timestamp with time zone should actually carry its timezone with it, not be stored in UTC and auto-converted to localtime.
Why the second works (so long as TimeZone = UTC)
Your original "works" version:
select '2011-12-30 00:30:00' AT TIME ZONE 'EST5EDT';
will only be correct if TimeZone is set to UTC, because the text-to-timestamptz cast assumes TimeZone when one isn't specified.
Why the third one works
Two problems cancel each other out.
The other version that appears to work is TimeZone independent, but it only works because two problems cancel themselves out. First, as explained above, timestamp without time zone AT TIME ZONE re-interprets the timestamp as being in that time zone for conversion to a UTC timestamptz; this effectively subtracts the timezone offset.
However, for reasons I beyond my ken, PostgreSQL uses timestamps with the reverse sign to what I'm used to seeing most places. See the documentation:
Another issue to keep in mind is that in POSIX time zone names, positive offsets are used for locations west of Greenwich. Everywhere else, PostgreSQL follows the ISO-8601 convention that positive timezone offsets are east of Greenwich.
This means that EST5EDT is the same as +5, not -5. Which is why it works: because you're subtracting the tz offset not adding it, but you're subtracting a negated offset!
What you'd need to get it correct is instead:
select TIMESTAMP '2011-12-30 00:30:00' AT TIME ZONE 'UTC'
AT TIME ZONE '+5';

How to convert local time to UTC?

I need to convert local time to UTC using a function. The inputs I have is the local time, timezone of the local time (e.g. 'Pacific/Auckland'). What I need to get from the procedure is the UTC time for the local time based on given timezone.
Can someone please help?
I am using version 8.3
This is covered in the manual, but it's not always obvious how to actually work with dates/times. The SQL spec is a bit bizarre.
In the case of your question it isn't clear whether you want to store the time in UTC but display it in the server's local time (TimeZone), or whether you wan to ignore TimeZone and always display it as UTC. I'll assume the latter.
For timestamps you'd just use AT TIME ZONE twice, like:
SELECT TIMESTAMP '2013-08-13 00:00:00' AT TIME ZONE 'Australia/Sydney' AT TIME ZONE 'UTC';
You need to use AT TIME ZONE twice. Once to convert the input timestamp to timestamptz according to the argument timezone, then another to convert that to a timestamp at UTC.
Unfortunately because of the (IMO insane) way the SQL spec defines AT TIME ZONE for TIME, you can't do the same thing for TIME. You'll have to manipulate the TimeZone variable instead:
SET TimeZone = 'UTC';
SELECT TIME '07:00' AT TIME ZONE 'Australia/Sydney';
This still leaves you with a timetz not a time. So its display value changes with the timezone setting.
You can use select now() at time zone 'UTC' for postgresql.