Oracle giving different times after changing timezone and converting to char - oracle-sqldeveloper

I have trying to convert the UTC time to local time in Oracle Developer as per query below. I needed it in a particular format after conversion but after conversion to character the time comes out to be completely different. Can anyone tell me what am I doing wrong here please.
select e.encntr_id
,to_char(e.reg_dt_tm,'YYYY-MM-DD hh24:mm') as reg_dt_tm
,to_char(from_tz (cast(e.reg_dt_tm as timestamp),'UTC') at time zone
'Australia/Sydney','YYYY-MM-DD hh24:mm') as aest_reg_char
,from_tz (cast(e.reg_dt_tm as timestamp),'UTC') at time zone 'Australia/Sydney' as aest_reg
from encounter e
where e.ENCNTR_ID in(123)
encntr_id reg_dt_tm aest_reg_char aest_reg
123 2022-03-03 05:03 2022-03-03 16:03 03/MAR/22 04:51:12.000000000 PM AUSTRALIA/SYDNEY

Use TO_CHAR:
TO_CHAR(
from_tz (cast(e.reg_dt_tm as timestamp),'UTC') at time zone 'Australia/Sydney',
'YYYY-MM-DD hh24:mm:ss.ff TZR'
)
Or change the default TIMESTAMP_TZ format in SQL Developer:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF TZR';
and then run the query.
db<>fiddle here

Related

Converting local datetime values to UTC datetime values in SQL Server and PostgreSQL

I am trying to understand the behavior of AT TIME ZONE and I get some strange behavioral differences between SQL Server and PostgreSQL in this regard.
For demonstration purposes I produced two queries, one for SQL Server and one for PostgreSQL.
The SQL Server query:
SELECT
[LocalTime],
[LocalTime] AT TIME ZONE 'W. Europe Standard Time' AS [LocalTimeWithOffset],
([LocalTime] AT TIME ZONE 'W. Europe Standard Time') AT TIME ZONE 'UTC' AS [UniversalTime]
FROM
(VALUES
(CAST('2020-06-20 12:34:56.789' AS DATETIME2)),
(CAST('2020-12-20 12:34:56.789' AS DATETIME2))) AS [X] ([LocalTime])
produces this result (in SQL Server Management Studio):
LocalTime LocalTimeWithOffset UniversalTime
--------- ------------------- ----------------
2020-06-20 12:34:56.7890000 2020-06-20 12:34:56.7890000 +02:00 2020-06-20 10:34:56.7890000 +00:00
2020-12-20 12:34:56.7890000 2020-12-20 12:34:56.7890000 +01:00 2020-12-20 11:34:56.7890000 +00:00
which seems fine to me, since I live in the Netherlands: in the summertime, we are 2 hours ahead of UTC (due to daylight saving) and in the wintertime we are only 1 hour ahead of UTC.
BUT... the PostgreSQL query:
SELECT
"LocalTime",
"LocalTime" AT TIME ZONE 'WET' AS "LocalTimeWithOffset",
("LocalTime" AT TIME ZONE 'WET') AT TIME ZONE 'UTC' AS "UniversalTime"
FROM
(VALUES
(timestamp '2020-06-20 12:34:56.789'),
(timestamp '2020-12-20 12:34:56.789')) AS "X" ("LocalTime")
produces the following result (in pgAdmin):
LocalTime LocalTimeWithOffset UniversalTime
timestamp without time zone timestamp with time zone timestamp without time zone
--------------------------- ------------------------ ---------------------------
2020-06-20 12:34:56.789 2020-06-20 14:34:56.789+02 2020-06-20 12:34:56.789
2020-12-20 12:34:56.789 2020-12-20 13:34:56.789+01 2020-12-20 12:34:56.789
which seems incorrect to me.
After struggling with this for a little while, I came up with another PostgreSQL query, which seems to yield the same results as the SQL Server query:
SELECT
"LocalTime",
"LocalTime"::timestamptz AS "LocalTimeWithOffset",
"LocalTime"::timestamptz AT TIME ZONE 'UTC' AS "UniversalTime"
FROM
(VALUES
(timestamp '2020-06-20 12:34:56.789'),
(timestamp '2020-12-20 12:34:56.789')) AS "X" ("LocalTime")
But this query seems to be dependent on the PostgreSQL server's time zone. :-(
Is there any (preferably elegant) way to mimic the SQL Server logic/behavior in PostgreSQL without a dependency on the server's time zone (and use an explicitly specified time zone in the query)?
Edit
I am aware that it is a good practice to store UTC date/time values in the database and convert them (preferably in/by the client) to local date/time values when presented to end users. I am intending to do so as well, of course, but my current scenario is a database migration from SQL Server to PostgreSQL (as part of a big application overhaul), where the DATETIME2 table columns in the SQL Server database contain local date/time values, which I want to convert to UTC date/time values in corresponding timestamp columns in the PostgreSQL tables. My migration script will work fine with the queries above, so actually I do not have a blocking issue. The reason for this question is that I am just curious about the apparent differences between SQL Server and PostgreSQL regarding the AT TIME ZONE behavior and the ways to overcome those behavioral differences.
In postgresql, AT TIME ZONE allows time zones to be added to date/time values that lack them (TIMESTAMP WITHOUT TIME ZONE, ::timestamp), and allows TIMESTAMP WITH TIME ZONE values (::timestamptz) to be shifted to non-local time zones and the time zone designation removed.
So, in the first query you've written for postgres, you're trying to cast '2020-06-20 12:34:56.789' to timestamp, and you've applied AT TIME ZONE on it for LocalTimeWithOffset. This is the case where postgres considered the input as timestamp in UTC and converted to the corresponding time zone, in your case WET, which is UTC+2:00 in Summer and UTC+1:00 in winter.
Now, in the third case, you're first applying the AT TIME ZONE 'WET' to timestamp '2020-06-20 12:34:56.789', hence the input is considered as UTC and converted to WET, the output is timstamptz, and this output is then fed again to AT TIME ZONE 'UTC', so as per behavior of AT TIME ZONE, it gets converted to UTC and the timezone tag is removed, which is, the input we given.
So, AT TIME ZONE did not behaved weird in postgresql rather did exactly what it was supposed to do, according to the documentation.
Hope your confusion is cleared.

PostgreSQL how to convert date with timezone offset to UTC?

I have a PostgreSQL table with date field in the following format
2017-09-07T17:24:33+08:00
and I want to convert it to UTC time.
I've looked around but found no way to do that with this specific time format. What am I missing?
Thanks
timezone definition (https://www.postgresql.org/docs/9.1/functions-datetime.html) : The function timezone(zone, timestamp) is equivalent to the SQL-conforming construct timestamp AT TIME ZONE zone
SELECT timezone('UTC','2017-09-07T17:24:33+08:00');
if selecting from column,
with t as (
SELECT '2017-09-07T17:24:33+08:00' as tm
)
SELECT timezone('UTC',tm::timestamptz) as ts
from t;
I have a PostgreSQL table with date field in the following format: 2017-09-07T17:24:33+08:00
This is incorrect. Per Postgres documentation,
All timezone-aware dates and times are stored internally in UTC. They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.
To display a TIMESTAMP WITH TIME ZONE attribute (which, again, is stored internally in UTC) in a specific time zone, you have a few options:
By default, “They are converted to local time in the zone specified by the TimeZone configuration parameter before being displayed to the client.” This can be set in postgresql.conf, using the SQL command SET TIME ZONE, or using a number of other options.
Use the AT TIME ZONE construct.
So, in regards to your original question: You don't have to do anything to your timestamptz for Postgres to store it in UTC. To display your attribute in UTC, you can either change the TimeZone configuration paramter, or use the construct below:
SELECT dt_with_timezone AT TIME ZONE 'UTC' FROM my_table

Understanding Time Zone in PostgreSQL >= 9.3

I am struggling in understanding how PostgreSQL is working with TIMESTAMP and TIME ZONE. I have a Debian Server where timezone is set to UTC running PostgreSQL 9.3 where cluster's postgresql.conf file is also set to UTC. My client is PgAdmin and my computer locale is also set to UTC. Therefore, all timezones are equals, there are no offsets and no DST in storage or display.
I have read many posts over the internet and I cannot understand the following output of my queries.
I assume as true the following affirmations:
Default data-type TIMESTAMP is WITHOUT TIME ZONE;
PostgreSQL stores all TIMESTAMP and TIMESTAMPTZ in UTC;
When using TIMESTAMP type, timezone conversion is not performed;
When using TIMESTAMPTZ type timezone conversion is performed;
POSIX and ISO standards define offset in a opposite fashion;
PostgreSQL display in ISO format but abbreviation are POSIX compliant.
First question: are all those statements right?
Then I checked it out, issuing:
SELECT '2000-01-01 00:00:00' AT TIME ZONE 'UTC-1';
-- Result: 2000-01-01 01:00:00
-- UTC --> LT
-- TIMESTAMP
SELECT '2000-01-01 00:00:00'::TIMESTAMP AT TIME ZONE 'UTC-1';
-- Result: 1999-12-31 23:00:00+00
-- LT --> UTC
-- TIMESTAMPTZ
SELECT '2000-01-01 00:00:00'::TIMESTAMPTZ AT TIME ZONE 'UTC-1';
-- Result: 2000-01-01 01:00:00
-- UTC --> LT
-- TIMESTAMP
This is very confusing to me because:
it looks like default data-type infered from string without cast is TIMESTAMPTZ instead of TIMESTAMP;
conversion is performed in different way when using different data-type;
when specifying data-type with casting ::operator it is the opposite type that is retured.
Second question: How must I interpret the result of those three queries?
Bonus question: I have read there that it is better to always store data with TIMESTAMPTZ. Is this a good practice?
Thank you further for your help!

Converting UTC time in local time in PostgreSQL 8.3

I run a Postgres 8.3 database where times seem to be stored in UTC without time zone.
I am trying to display in local time but not with '+01' suffix :
With select scheduled_start_ts I get :
2014-01-20 05:01:35.663
With select scheduled_start_ts at time zone 'MET' :
2014-01-20 05:01:35.663+01
I would like to get "2014-01-20 06:01:35.663" which is in local time.
The database I am using cannot be modified and I am not allowed to modify how data are stored.
If you want to format times, use the to_char function. See formatting functions in the docs.
regress=> SELECT to_char(
(TIMESTAMP '2014-01-20 05:01:35.663' AT TIME ZONE 'UTC')
AT TIME ZONE 'MET',
'YYYY-MM-DD HH:MI:SS'
);
to_char
---------------------
2014-01-20 06:01:35
(1 row)
The (TIMESTAMP 'xxx' AT TIME ZONE 'UTC') gets me a timestamptz with the correct time, by re-interpreting the TIMESTAMP as being in UTC. The second AT TIME STAMP instead converts the timestamptz into a timestamp in timezone MET. This then gets formatted.
Whatever the SQL standards committe were smoking when they designed this, I never, ever, ever want to be anywhere near it.

PostgreSQL - how to render date in different time zone?

My server is in Central Time. I would like to render timestamps using Eastern time.
For instance, I would like to render 2012-05-29 15:00:00 as 2012-05-29 16:00:00 EDT.
How can I achieve it?
to_char('2012-05-29 15:00:00'::timestamptz at time zone 'EST5EDT', 'YYYY-MM-DD HH24:MI:SS TZ') gives 2012-05-29 16:00:00 (no zone).
to_char('2012-05-29 15:00:00'::timestamp at time zone 'EST5EDT', 'YYYY-MM-DD HH24:MI:SS TZ') gives 2012-05-29 14:00:00 CDT (wrong).
This one works, but it's so ridiculously complicated there must be an easier way: replace(replace(to_char(('2012-05-29 15:00:00'::timestamptz at time zone 'EST5EDT')::timestamptz, 'YYYY-MM-DD HH24:MI:SS TZ'), 'CST', 'EST'), 'CDT', 'EDT')
The key is to switch the local timezone to the desired display timezone, for the duration of the transaction:
begin;
set local timezone to 'EST5EDT';
select to_char('2012-05-29 15:00:00'::timestamp at time zone 'CDT',
'YYYY-MM-DD HH24:MI:SS TZ');
end;
The result is:
2012-05-29 16:00:00 EDT
Note that with set [local] timezone it is required to use full time zone names instead of abbreviations (for instance, CST would not work). Look up in the pg_timezone_names view for valid choices.
To use that method in a context similar to a to_char() call, I believe this function does the job:
CREATE FUNCTION display_in_other_tz(
in_t timestamptz,
in_tzname text,
in_fmt text) RETURNS text
AS $$
DECLARE
v text;
save_tz text;
BEGIN
SHOW timezone into save_tz;
EXECUTE 'SET local timezone to ' || quote_literal(in_tzname);
SELECT to_char(in_t, in_fmt) INTO v;
EXECUTE 'SET local timezone to ' || quote_literal(save_tz);
RETURN v;
END;
$$ language plpgsql;
In fact, PG knows it all - to_char(x, 'TZ') differentiates CST from
CDT correctly, and at time zone EST5EDT respects DST as well.
When dealing with a timestamp Postgres knows:
The setting of the GUC timezone.
The data type.
The value, which is the same count of seconds since '1970-1-1 0:0 UTC' for timestamp and timestamptz. (Or, to be precise: UT1.)
Details about other time zones in your date/time configuration files
When interpreting input, Postgres uses information about the provided time zone.
When rendering a timestamp value, Postgres uses the current timezone setting, but time zone offset, abbreviation or name are only used to compute the correct value on input. They are not saved. It is impossible to extract that information later. More details in this related answer:
Your "correct" example is almost correct. TZ of to_char() returns 'CDT' for timestamps that fall in the daylight saving periods of Central Time and 'CST' else. Eastern Time (EST /EDT) switches daylight saving hours at the same local time - I quote Wikipedia:
The time is adjusted at 2:00 AM local time.
The two time zones are out of sync during two hours per year. Of course, this can never affect a timestamp at 15:00 or 16:00, only around 02:00.
A fully correct solution - much like what #Daniel already posted, slightly simplified:
BEGIN;
SET LOCAL timezone to 'EST5EDT';
SELECT to_char('2012-05-29 15:00 CST6CDT'::timestamptz
, 'YYYY-MM-DD HH24:MI:SS TZ')
RESET timezone; -- only if more commands follow in this transactions
END;
The effects of SET LOCAL last only till the end of the current transaction.
The manual about SET LOCAL.