Daylight saving error - oracle

I would like to calculate time diff between to dates in a different time zone. I am converting this two times to Greenwich time. But when I am converting dates in Europe/Moscow timezone the offset is different although there is no daylight saving.
This is the case I checked:
SELECT TO_DATE('5/20/2018 10:05:00 PM','mm/dd/yyyy hh:mi:ss am'),
TO_DATE('5/20/2018 10:05:00 PM','mm/dd/yyyy hh:mi:ss am')
- extract(TIMEZONE_HOUR from from_tz (TO_TIMESTAMP('5/20/2018 10:05:00 PM','mm/dd/yyyy hh:mi:ss am') ,'GMT' ) at time zone 'Europe/Moscow')/24 deptime_GRINICH
from dual
UNION
SELECT TO_DATE('5/24/2018 11:35:00 PM','mm/dd/yyyy hh:mi:ss am'),
TO_DATE('5/24/2018 11:35:00 PM','mm/dd/yyyy hh:mi:ss am')
- extract(TIMEZONE_HOUR from from_tz (TO_TIMESTAMP('5/24/2018 11:35:00 PM','mm/dd/yyyy hh:mi:ss am') ,'GMT' ) at time zone 'Europe/Moscow')/24 deptime_GRINICH
from dual
And the results are:
Date date converting to Greenwich time zone
5/20/2018 10:05:00 PM 5/20/2018 6:05:00 PM --> hour difference 4 hours
5/24/2018 7:35:00 PM 5/24/2018 3:35:00 PM --> hour difference 4 hours
but 2018 Time Zones - Sochi is UTC + 3h

In October 2014 Russia changed their rule for Daylight-Saving. Your Timezone file at Oracle Database could be an old one which does not cover the recent changes. Verify version with
SELECT * FROM V$TIMEZONE_FILE;
and consider an upgrade, see Upgrading the Time Zone File and Timestamp with Time Zone Data
However, it seems to be a bug in Oracle. Have a look at my query which is a bit more clear than your example:
SELECT *
FROM NLS_SESSION_PARAMETERS
WHERE parameter LIKE 'NLS_TIMESTAMP_TZ_FORMAT';
PARAMETER VALUE
-------------------------- --------------------------------------
NLS_TIMESTAMP_TZ_FORMAT YYYY-MM-DD HH24:MI:SSfmXFF3 fmTZH:TZM
1 row selected.
SELECT
EXTRACT(TIMEZONE_HOUR FROM TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow') AS TZ_HOUR,
TO_CHAR(TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM') AS ts2,
TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow' AS ts3
FROM dual;
TZ_HOUR TS2 TS3
---------- ------------------------------ ----------------------------------
4 2018-05-20 22:05:00 +04:00 20.05.2018 22:05:00.000000000 +03:00
1 row selected.
That's really strange, because TZH:TZM, resp. EXTRACT(TIMEZONE_HOUR FROM ...) returns different value than default NLS_TIMESTAMP_TZ_FORMAT = ... TZH:TZM. For other time zones (e.g. Europe/Zurich) I get always 02:00 - as expected.
You may open a ticket at Oracle support.
I have two databases, an old and a new one. The old one does not reflect recent changes in Russian Daylight-Saving times, the newer one does. However, the error as above appears on both:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD HH24:MI:SS TZH:TZM';
SELECT filename, VERSION,
TO_CHAR(TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow', 'TZH:TZM TZD') AS ts1,
TIMESTAMP '2018-05-20 22:05:00 Europe/Moscow' AS ts2,
TO_CHAR(TIMESTAMP '2018-01-20 22:05:00 Europe/Moscow', 'TZH:TZM TZD') AS ts3,
TIMESTAMP '2018-01-20 22:05:00 Europe/Moscow' AS ts4
FROM V$TIMEZONE_FILE;
Old (Daylight-Saving times still existing due to old timezlrg_14.dat file):
FILENAME VERSION TS1 TS2 TS3 TS4
---------------- -------- ----------- ------------------------------------ ----------- -------------------------------------
timezlrg_14.dat 14 +04:00 MSD 20.05.2018 22:05:00.000000000 +03:00 +03:00 MSK 20.01.2018 22:05:00.000000000 +03:00
New (no Daylight-Saving time changes):
FILENAME VERSION TS1 TS2 TS3 TS4
---------------- -------- ----------- ------------------------------------ ----------- -------------------------------------
timezlrg_18.dat 18 +04:00 MSK 20.05.2018 22:05:00.000000000 +03:00 +04:00 MSK 20.01.2018 22:05:00.000000000 +03:00

Related

Oracle convert from GMT to EST and EDT

I am using Oracle 19c.
I need to convert dates from GMT to EST and EDT.
I am using the following approach:
1. Get the destination time zone abbreviation for the p_date variable:
DEFINE p_date TO_DATE('03/11/2013 02:22:21', 'MM/DD/YYYY HH24:MI:SS');
SELECT TO_CHAR(FROM_TZ(CAST (&p_date AS TIMESTAMP), 'America/New_York'), 'TZD') INTO v_tzabbrev FROM DUAL;
Where:
p_date: is the date to be converted.
v_tzname: is the time zone name, such as America/New_York
v_tzabbrev: is the time zone abbreviation, such as 'EDT' or "EST" based on whether the date is during Daylight Saving Time or not
2. Convert the p_date using the time zone abbreviation obtained in #1
SELECT NEW_TIME(p_date, 'GMT', v_tzabbrev) INTO v_date FROM DUAL;
This seems to work. But, I believe the flaw is that it is using the GMT date to determine the destination time zone abbreviation, which is inaccurate.
For example, if p_date, in UTC, is '03/11/2013 02:22:21' and I need to convert it to 'America/New_York', Step #1 would return 'EDT', but this date in Eastern was actually "03/10/2013 21:22:21", which was before Daylight Saving started. So, it should actually be converted using "EST".
Daylight saving time in '2013 began at 2 a.m. on Sunday, March 10.
So, it seems that I need a way to take the GMT value and determine its new date in Eastern first, then apply additional logic based on whether that new date is EDT or EST.
Any assistance is appreciated.
You can define p_date directly as UTC time:
DEFINE p_date TO_TIMESTAMP_TZ('03/11/2013 02:22:21 UTC', 'MM/DD/YYYY HH24:MI:SS TZR');
SELECT TO_CHAR((&p_date AT TIME ZONE 'America/New_York'), 'TZD')
INTO v_tzabbrev
FROM DUAL;
Or in the statement:
DEFINE p_date TO_DATE('03/11/2013 02:22:21', 'MM/DD/YYYY HH24:MI:SS');
SELECT TO_CHAR((FROM_TZ(CAST(&p_date AS TIMESTAMP), 'UTC') AT TIME ZONE 'America/New_York'), 'TZD')
INTO v_tzabbrev
FROM DUAL;
Another possibility is to use SESSIONTIMEZONE implicitly, although I don't recommend this:
DEFINE p_date TO_DATE('03/11/2013 02:22:21', 'MM/DD/YYYY HH24:MI:SS');
ALTER SESSION SET TIME ZONE = 'UTC';
SELECT TO_CHAR((CAST(&p_date AS TIMESTAMP WITH TIME ZONE) AT TIME ZONE 'America/New_York'), 'TZD')
INTO v_tzabbrev
FROM DUAL;
"Daylight saving time in '2013 began at 2 a.m. on Sunday, March 10."
... which is correct, and you can see that happening with the UTC equivalent date/time as:
-- get New York DST start time as UTC
with cte (ts) as (
select timestamp '2013-03-10 01:59:59 America/New_York' from dual
union all
select timestamp '2013-03-10 03:00:00 America/New_York' from dual
)
select ts, to_char(ts, 'TZD') as tzd, ts at time zone 'UTC' as ts_utc
from cte
TS
TZD
TS_UTC
2013-03-10 01:59:59 AMERICA/NEW_YORK
EST
2013-03-10 06:59:59 UTC
2013-03-10 03:00:00 AMERICA/NEW_YORK
EDT
2013-03-10 07:00:00 UTC
but this date in Eastern was actually "03/10/2013 21:22:21", which was before Daylight Saving started.
No, it isn't, it's after DST started.
So, it should actually be converted using "EST".
No, it shouldn't. I'm afraid the premise of your question is wrong.
The conversion you are doing is getting the correct result:
-- get UTC timestamp as New York
with cte (ts) as (
select timestamp '2013-03-11 02:22:21 UTC' from dual
)
select ts as ts_utc, ts at time zone 'America/New_YORK' as ts, to_char(ts at time zone 'America/New_York', 'TZD') as tzd
from cte
TS_UTC
TS
TZD
2013-03-11 02:22:21 UTC
2013-03-10 22:22:21 AMERICA/NEW_YORK
EDT
fiddle
2013-03-11 02:22:21 UTC is after 2013-03-10 01:59:59 America/New_York, as it is the following day in UTC, and 19 hours after the New York DST switch occurred. In other words, 2013-03-11 02:22:21 UTC is 19 hours after 2012-03-10 07:00:00 UTC, which is the UTC equivalent of the EDT start-time from the first query above.
You seem to be confusing the date in your UTC value with the date that DST was applied in the USA that year.
Because the NEW_TIME() function is limited, I would prefer to use FROM_TZ and AT TIME ZONE, as Wernfried showed.

Java and Oracle timestamp with time zone

I'm trying to save two dates in Oracle with hibernate. both dates have the same timestamps in the moscow time zone: 2005-10-30T02:00+03:00[Europe/Moscow] and 2005-10-30T02:00+04:00[Europe/Moscow] ("Sun Oct 30 02:00:00 MSK 2005" and "Sun Oct 30 02:00:00 MSD 2005"). the dates are separated in time by one hour and are associated with the transition to winter/summer time.
I created the table in Oracle:
create table TMP
(
ID LONG,
TS TIMESTAMP,
TSLTZ TIMESTAMP WITH LOCAL TIME ZONE,
TSTZ TIMESTAMP WITH TIME ZONE
);
and entity in my module:
#Entity
#Table(name = "tmp")
public class DateTimeOracle {
private Long id;
private ZonedDateTime ts;
private ZonedDateTime tsltz;
private ZonedDateTime tstz;
#Id
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public ZonedDateTime getTs() {
return ts;
}
public ZonedDateTime setTs(ZonedDateTime ts) {
this.ts = ts;
}
public ZonedDateTime getTsltz() {
return tsltz;
}
public ZonedDateTime setTsltz(ZonedDateTime tsltz) {
this.tsltz = tsltz;
}
public ZonedDateTime getTstz() {
return tstz;
}
public ZonedDateTime setTstz1(ZonedDateTime tstz) {
this.tstz = tstz;
}
}
In entity, all fields are initialized by a single date.
After saving, both dates in Oracle have the same values and look like:
ts = 2005-10-30 02:00:00.000000
TSLTZ = 2005-10-29 23:00:00.000000
TSTZ = 2005-10-30 02:00:00.000000 +04:00
Why does oracle keep the same value for different dates (including offset +04:00)? Is there any way to fix this?
P.S. Postgres stores the date correctly. One with an offset +03:00, the other with an offset +04:00 (2005-10-29 23:00:00.000000 and 2005-10-29 22:00:00.000000, respectively).
Update
This is how i create the dates:
Date dt2 = new Date(1130623200000L); //2005-10-29 23:00:00 +04:00
Date dt3 = new Date(1130626800000L); //2005-10-29 23:00:00 +03:00
ZonedDateTime zdt2 = ZonedDateTime.ofInstant(dt2.toInstant(), ZoneId.systemDefault()); // My zone is MSK
ZonedDateTime zdt3 = ZonedDateTime.ofInstant(dt3.toInstant(), ZoneId.systemDefault()); // My zone is MSK
OffsetDateTime odt2 = zdt2.toOffsetDateTime();
OffsetDateTime odt3 = zdt3.toOffsetDateTime();
If I not use Hibernate and use jdbc directly, the situation doesn't change.
Connection conn = DriverManager.getConnection("<oracle_url>",
"<username>", "<password>");
PreparedStatement pstmt = conn.prepareStatement("insert into tmp (id, TSTZ1, TSTZ2) values (200, ?, ?)", Statement.RETURN_GENERATED_KEYS);
pstmt.setDate(1, new java.sql.Date(dt2.getTime()));
pstmt.setDate(2, new java.sql.Date(dt3.getTime()));
int z1 = pstmt.executeUpdate();
pstmt.close();
conn.close();
Update2
If I save OffsetDateTime via the jdbc driver in timestamp with local timezone or in timestamp with time zone, then everything is fine.
PreparedStatement pstmt = conn.prepareStatement("insert into tmp (TSLTZ1, TSLTZ2, TSTZ1, TSTZ2) values (?, ?, ?, ?)");
pstmt.setObject(1, odt2);
pstmt.setObject(2, odt3);
pstmt.setObject(3, odt2);
pstmt.setObject(4, odt3);
I see in DB:
2005-10-29 22:00:00.000000 2005-10-29 23:00:00.000000 2005-10-30 02:00:00.000000 +04:00 2005-10-30 02:00:00.000000 +03:00
But if I save ZonedDateTime, then the values in timestamp with local timezone are correct, but in timestamp with time zone are incorrect.
PreparedStatement pstmt = conn.prepareStatement("insert into tmp (TSLTZ1, TSLTZ2, TSTZ1, TSTZ2) values (?, ?, ?, ?)");
pstmt.setObject(1, zdt2);
pstmt.setObject(2, zdt3);
pstmt.setObject(3, zdt2);
pstmt.setObject(4, zdt3);
In DB I see:
2005-10-29 22:00:00.000000 2005-10-29 23:00:00.000000 2005-10-30 02:00:00.000000 +04:00 2005-10-30 02:00:00.000000 +04:00
The last two values are incorrect.
Some explanations about the Oracle TIMESTAMP data types:
TIMESTAMP: Does not store any timezone information. If you enter a timestamp with time zone then the time zone information is simply truncated and lost.
TIMESTAMP WITH TIME ZONE: Stores the timestamp with time zone information (i.e. either as named region or as UTC-Offset) as you insert the timestamp into database.
TIMESTAMP WITH LOCAL TIME ZONE: Timestamp is stored as DBTIMEZONE (recommended and usually UTC). The timestamp is always and only displayed in the current user session SESSIONTIMEZONE. Thus it does not display any time zone information, because by definition this is always your local time zone.
Which one should I use?
It depends on your requirements - of course.
With TIMESTAMP WITH LOCAL TIME ZONE you don't have to care about any settings of your client, the time is always shown as local time. The time is stored in DBTIMEZONE, thus you lose the original inserted time zone.
Be aware, when you create an index on TIMESTAMP WITH TIME ZONE. It is not possible to create an index directly on such column. Instead Oracle creates a virtual column for SYS_EXTRACT_UTC(TSTZ) and creates the index on this virtual column. You should pay attention to this when you develop your queries.
Update
You have quite a special situation. When you insert TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow' then this time is ambiguous, it could mean 2005-10-30 02:00:00+03:00 or 2005-10-30 02:00:00+04:00
Take this example:
SELECT TO_CHAR(TIMESTAMP '2005-10-30 00:00:00 Europe/Moscow' + LEVEL * INTERVAL '1' HOUR,
'YYYY-MM-DD hh24:mi:ss TZH:TZM TZD tzr') AS ts
FROM dual
CONNECT BY LEVEL <= 4;
+--------------------------------------------+
|TS |
+--------------------------------------------+
|2005-10-30 01:00:00 +04:00 MSD Europe/Moscow|
|2005-10-30 02:00:00 +04:00 MSD Europe/Moscow|
|2005-10-30 02:00:00 +03:00 MSK Europe/Moscow|
|2005-10-30 03:00:00 +03:00 MSK Europe/Moscow|
+--------------------------------------------+
Have a look at TIMESTAMP WITH TIME ZONE Data Type
To eliminate the ambiguity of boundary cases when the time switches from Standard Time to Daylight Saving Time, use both the TZR format element and the corresponding TZD format element. The TZD format element is an abbreviation of the time zone region with Daylight Saving Time information included. Examples are PST for U. S. Pacific Standard Time and PDT for U. S. Pacific Daylight Time. The following specification ensures that a Daylight Saving Time value is returned:
TIMESTAMP '1999-10-29 01:30:00 America/Los_Angeles PDT'
If you do not add the TZD format element, and the datetime value is ambiguous, then Oracle Database returns an error if you have the ERROR_ON_OVERLAP_TIME session parameter set to TRUE. If ERROR_ON_OVERLAP_TIME is set to FALSE (the default value), then Oracle Database interprets the ambiguous datetime as Standard Time.
Note, time zone +04:00 or +03:00 is not equal to Europe/Moscow. Timezone Europe/Moscow considers daylight-saving-times (when it was still used in Russia about 10 years ago) but +04:00/+03:00 does not.
Sorry, I never used hibernate, so I don't know how this framework handles such data. I am not familiar with Java either. Maybe the Daylight Saving Time information is not supported.
I can just guess, class java.sql.Date and method setDate refers to DATE data type in Oracle. As already stated, better use java.sql.Timestampand setTimestamp.
The DATE data type does not support any time zone information. If you try to insert a DATE value into a TIMESTAMP WITH [LOCAL] TIME ZONE column then Oracle actually does
FROM_TZ(CAST(<your DATE value> AS TIMESTAMP), SESSIONTIMEZONE)
You conditions are very special, let's try to lighten it a bit.
Unix time 1130623200 is 2005-10-29 22:00:00 UTC
In Moscow time this is 2005-10-30 02:00:00 Europe/Moscow, however this is ambiguous.
It could be
either 2005-10-30 02:00:00 +04:00 Europe/Moscow MSD
or 2005-10-30 02:00:00 +03:00 Europe/Moscow MSK
Unix time 1130626800 is 2005-10-29 23:00:00 UTC
In Moscow time this is 2005-10-30 02:00:00 Europe/Moscow, however this is ambiguous.
It could be
either 2005-10-30 02:00:00 +04:00 Europe/Moscow MSD
or 2005-10-30 02:00:00 +03:00 Europe/Moscow MSK
According to Oracle Documentation the ambiguous TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow' let to standard time, i.e. 2005-10-30 02:00:00 Europe/Moscow MSK +03:00 (as it was in 2005!)
Be aware, in 2005 Moscow standard time was MSK => +03:00. In 2011, the Russian government proclaimed that daylight saving time would in future be observed all year round, thus effectively displacing standard time. I.e. today Moscow standard time is MSK => +04:00 which was called MSD before 2011.
Verify with
SELECT
TO_CHAR(TIMESTAMP '2005-10-29 22:00:00 UTC' AT TIME ZONE 'Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_1_UTC,
TO_CHAR(TIMESTAMP '2005-10-29 23:00:00 UTC' AT TIME ZONE 'Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_2_UTC,
TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS,
TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSK', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_MSK,
TO_CHAR(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSD', 'YYYY-MM-DD HH24:MI:SS TZH:TZM tzr TZD') AS TS_MSD
FROM dual
TS_1_UTC
TS_2_UTC
TS
TS_MSK
TS_MSD
2005-10-30 02:00:00 +04:00 Europe/Moscow MSD
2005-10-30 02:00:00 +03:00 Europe/Moscow MSK
2005-10-30 02:00:00 +03:00 Europe/Moscow MSK
2005-10-30 02:00:00 +03:00 Europe/Moscow MSK
2005-10-30 02:00:00 +04:00 Europe/Moscow MSD
ALTER SESSION SET TIME_ZONE = 'Europe/Moscow';
SELECT
TO_CHAR(CAST(TIMESTAMP '2005-10-29 22:00:00 UTC' AT TIME ZONE 'Europe/Moscow' AS TIMESTAMP WITH LOCAL TIME ZONE), 'YYYY-MM-DD HH24:MI:SS TZD') AS TS_1_UTC,
TO_CHAR(CAST(TIMESTAMP '2005-10-29 23:00:00 UTC' AT TIME ZONE 'Europe/Moscow' AS TIMESTAMP WITH LOCAL TIME ZONE), 'YYYY-MM-DD HH24:MI:SS TZD') AS TS_2_UTC,
TO_CHAR(CAST(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow' AS TIMESTAMP WITH LOCAL TIME ZONE), 'YYYY-MM-DD HH24:MI:SS TZD') AS TS,
TO_CHAR(CAST(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSK' AS TIMESTAMP WITH LOCAL TIME ZONE), 'YYYY-MM-DD HH24:MI:SS TZD') AS TS_MSK,
TO_CHAR(CAST(TIMESTAMP '2005-10-30 02:00:00 Europe/Moscow MSD' AS TIMESTAMP WITH LOCAL TIME ZONE), 'YYYY-MM-DD HH24:MI:SS TZD') AS TS_MSD
FROM dual
TS_1_UTC
TS_2_UTC
TS
TS_MSK
TS_MSD
2005-10-30 02:00:00 MSD
2005-10-30 02:00:00 MSK
2005-10-30 02:00:00 MSK
2005-10-30 02:00:00 MSK
2005-10-30 02:00:00 MSD
I think Oracle does it absolutely correct, not matter if you have TIMESTAMP WITH TIME ZONE or TIMESTAMP WITH LOCAL TIME ZONE. But you have to check carefully what is actually inserted into table and how you display it.

Time difference with daylight savings oracle

We are trying to perform arithmetic on a date. We are trying to add 1 day (24 hours) Here's a basic example...
select to_date('2018-10-06 22:00:00') + 1 from dual;
The result we get back from this is as follows...
2018-10-07 22:00:00
That seems good on paper, however we are in the Sydney timezone and during that interim period daylight savings occurs so the time should like something more like this...
2018-10-07 23:00:00
So I suppose my question is, when doing arithmetic in oracle is it possible to take into consideration daylight savings time. One idea was to convert the initial date to UTC and convert back to SYDNEY time, but it seems like a complicated solution that might already be handled by oracle functionality somewhere.
Cheers
You can't really do it directly with a date as those are not timezone aware.
You can start with a timestamp with time zone, e.g. as a timestamp literal:
select timestamp '2018-10-06 22:00:00 Australia/Sydney' + interval '1' day
from dual;
TIMESTAMP'2018-10-0622:00:00AUSTRALIA/SYDNEY'+
----------------------------------------------
2018-10-07 23:00:00.000000000 AUSTRALIA/SYDNEY
which you can cast back to a date if you want to discard that information:
select cast(timestamp '2018-10-06 22:00:00 Australia/Sydney' + interval '1' day as date)
from dual;
CAST(TIMESTAMP'2018
-------------------
2018-10-07 23:00:00
If you're starting from a date, e.g. in a table column, you can cast that to a timestamp and specify the time zone:
with your_table (your_date) as (
select to_date('2018-10-06 22:00:00') from dual
)
select from_tz(cast(your_date as timestamp), 'Australia/Sydney') + interval '1' day
from your_table;
FROM_TZ(CAST(YOUR_DATEASTIMESTAMP),'AUSTRALIA/
----------------------------------------------
2018-10-07 23:00:00.000000000 AUSTRALIA/SYDNEY
and optionally cast back to date again to discard the fractional seconds and time zone information.
If your session time zone is Sydney then you could let that be set implicitly by casting to timestamp with time zone instead:
with your_table (your_date) as (
select to_date('2018-10-06 22:00:00') from dual
)
select cast(your_date as timestamp with time zone) + interval '1' day
from your_table;
but it's generally better not to assume anything about the session environment that will run your code.
When the clocks go back at the end of DST you can specify which of the two possible values of times between 02:00 and 03:00 you mean by including a DST indicator:
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS TZH:TZM';
select timestamp '2018-04-01 01:00:00 Australia/Sydney AEDT' from dual;
TIMESTAMP'2018-04-0101:00:
--------------------------
2018-04-01 01:00:00 +11:00
select timestamp '2018-04-01 02:00:00 Australia/Sydney AEDT' from dual;
TIMESTAMP'2018-04-0102:00:
--------------------------
2018-04-01 02:00:00 +11:00
select timestamp '2018-04-02 02:00:00 Australia/Sydney AEST' from dual;
TIMESTAMP'2018-04-0202:00:
--------------------------
2018-04-02 02:00:00 +10:00
select timestamp '2018-04-01 03:00:00 Australia/Sydney AEST' from dual;
TIMESTAMP'2018-04-0103:00:
--------------------------
2018-04-01 03:00:00 +10:00
Although for me this only seems to work in 12.2.0.1; in earlier versions AEST/AEDT aren't recognised (though EST is). I'm not sure if that's just down the time zone file version - my 12.2 instance have version 26.
Going the other way if you have times that appear to be in the hour that doesn't exist when the clocks go forward at the start of DST, there isn't much you can do about it as they are invalid. If you populate fields using sysdate and the server is set to Sydney, or current_date and the session is, then that shouldn't happen. Dates created with to_date() could be in that window, but then you probably need to figure out why and stop that happening.

Convert UTC time to specific time zone in oracle

I have to convert UTC time to some specific time zone, I have converted the sysdate to UTC,now I want to convert it to time zone specific, This is how I am converting sysdate to UTC
select cast(sys_extract_utc(systimestamp) as DATE) from dual;
I tried writing +5 before from keyword,but its returning the date after 5 days.
select cast(sys_extract_utc(systimestamp) as DATE)+5 from dual;
I have get the timezone offset with below query, but I have clue to use this in above query
SELECT TZ_OFFSET('US/Eastern') FROM DUAL;
How can I convert the UTC time to some timezone offset,something like below
select cast(sys_extract_utc(systimestamp) as DATE) from dual where TZ_OFFSET=+5;
You can get the current UTC time using:
SELECT CAST( SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE )
FROM DUAL;
You can reverse the process to change a UTC DATE to a TIMESTAMP WITH TIME ZONE at +04:00 using:
SELECT FROM_TZ( CAST( your_date AS TIMESTAMP ), 'UTC' ) AT TIME ZONE '+04:00'
FROM your_table
Trying to add on the time zone offset would give you problems with daylight savings adjustments.
You can use the at time zone syntax to specify a conversion, but you don't need to convert to UTC first. If you do you might not get what you expect, without taking an extra step:
select systimestamp as sys_ts,
sys_extract_utc(systimestamp) as utc_ts,
sys_extract_utc(systimestamp) at time zone 'US/Eastern' as edt_ts
from dual;
SYS_TS UTC_TS EDT_TS
------------------------------ ----------------------- ----------------------------------
2017-10-05 11:30:41.023 +01:00 2017-10-05 10:30:41.023 2017-10-05 05:30:41.023 US/EASTERN
That says the time in New York is 05:30, when it's actually 06:30 at time of writing.
The sys_extract_utc function gives you the UTC equivalent of your system time, but with on embedded time zone info - it's a plain timestamp, not a timestamp with time zone. So when you adjust it, it's implicitly converted to the system time zone with no adjustment, leaving you with the wrong actual time.
You can specify that the extracted value is UTC using the from_tz() function:
select systimestamp as sys_ts,
sys_extract_utc(systimestamp) as utc_ts,
from_tz(sys_extract_utc(systimestamp), 'UTC') at time zone 'US/Eastern' as edt_ts
from dual;
SYS_TS UTC_TS EDT_TS
------------------------------ ----------------------- ----------------------------------
2017-10-05 11:30:41.144 +01:00 2017-10-05 10:30:41.144 2017-10-05 06:30:41.144 US/EASTERN
But you don't need to do that much work, you can just take the original time zone-aware systimestamp value and apply at time zone directly to that:
select systimestamp as sys_ts,
systimestamp at time zone 'US/Eastern' as edt_ts
from dual;
SYS_TS EDT_TS
------------------------------ ----------------------------------
2017-10-05 11:30:41.271 +01:00 2017-10-05 06:30:41.271 US/EASTERN
Which you can then cast to a date data type if that is a requirement:
cast(from_tz(sys_extract_utc(systimestamp), 'UTC') at time zone 'US/Eastern' as date)
or more simply:
cast(systimestamp at time zone 'US/Eastern' as date)

Oracle equivalent of teradata timezones

I am trying to convert the following Teradata SQL to Oracle. and the problem is that I am not able to find the equivalent time zone value in Oracle. In the below example, 'Europe Central' is not recognized in Oracle.
select hi.create_date
, to_char( hi.create_date,'YYYY-MM-DD HH24:MI:SS')
, ((CAST(to_char( hi.create_date,'YYYY-MM-DD HH24:MI:SS')||'+00:00' AS TIMESTAMP(0))) at time zone 'Europe Central')
from historical_info hi
This below code in Oracle throws an error:
SELECT create_date,
CAST( create_date
AS TIMESTAMP WITH TIME ZONE
) AT TIME ZONE 'Europe Central'
TZ_LOSANG
FROM historical_info
ORA-01878: specified field not found in datetime or interval.
01878. 00000 - "specified field not found in datetime or interval"
*Cause: The specified field was not found in the datetime or interval.
*Action: Make sure that the specified field is in the datetime or interval.
Can you please help me convert the Teradata time zones to Oracle recognized time zones.
You can get a list of supported time zones, as shown in the documentaion:
You can obtain a list of time zone names and time zone abbreviations from the time zone file that is installed with your database by entering the following statement:
SELECT TZNAME, TZABBREV
FROM V$TIMEZONE_NAMES
ORDER BY TZNAME, TZABBREV;
There isn't a simple built-in way to convert the Teradata name to an Oracle name, so you'll need to pick a suitable equivalent for each zone/region you have to deal with.
Oracle doesn't have a 'Europe/Central' time zone name, but does recognise the CET abbreviation, or any of the names which map to that abbreviation:
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS TZR';
with historical_info(create_date) as (select sysdate from dual)
SELECT create_date,
CAST(create_date AS TIMESTAMP) AT TIME ZONE 'CET' TZ_AT_CET
FROM historical_info;
CREATE_DATE TZ_AT_CET
------------------- -----------------------
2015-08-17 11:59:29 2015-08-17 12:59:29 CET
But that is adjusting the time from my session time zone to CET, which may not be what you want. If you are saying that the stored time represents CET then you want the from_tz function:
SELECT create_date,
CAST(create_date AS TIMESTAMP) AT TIME ZONE 'CET' TZ_AT_CET,
FROM_TZ(CAST(create_date AS TIMESTAMP), 'CET') TZ_FROM_CET
FROM historical_info;
CREATE_DATE TZ_AT_CET TZ_FROM_CET
------------------- ----------------------- -----------------------
2015-08-17 12:01:40 2015-08-17 13:01:40 CET 2015-08-17 12:01:40 CET
From your column alias you may be trying to show that CET time in a different zone, which needs both steps:
SELECT create_date,
FROM_TZ(CAST(create_date AS TIMESTAMP), 'CET') TZ_FROM_CET,
FROM_TZ(CAST(create_date AS TIMESTAMP), 'CET')
AT TIME ZONE 'America/Los_Angeles' TZ_LOSANG
FROM historical_info
CREATE_DATE TZ_FROM_CET TZ_LOSANG
------------------- ----------------------- ---------------------------------------
2015-08-17 12:02:55 2015-08-17 12:02:55 CET 2015-08-17 03:02:55 AMERICA/LOS_ANGELES
If you have rows with different zones and those are stored as a separate column at the moment, you can use a case expression (or decode) to specify the Oracle equivalent for each one; but you'll still have to do this mapping yourself. You could put the translation into a look-up table if this isn't a one-off task.
with historical_info(create_date, orig_zone) as (
select sysdate, 'Europe Central' from dual
union all select sysdate, 'Europe Western' from dual
union all select sysdate, 'America Central' from dual
)
SELECT create_date,
FROM_TZ(CAST(create_date AS TIMESTAMP),
case orig_zone
when 'Europe Central' then 'CET'
when 'Europe Western' then 'WET'
when 'America Central' then 'US/Central'
-- when x then y for all other values you need
end) TZ_ADJUSTED
FROM historical_info
CREATE_DATE TZ_ADJUSTED
------------------- --------------------------------------
2015-08-17 12:16:13 2015-08-17 12:16:13 CET
2015-08-17 12:16:13 2015-08-17 12:16:13 WET
2015-08-17 12:16:13 2015-08-17 12:16:13 US/CENTRAL
You need to be careful to use time zones (or abbreviations) that adjust for daylight savings appropriately.

Resources