How to convert Time string in UTC to CST in Oracle - oracle

I need to convert timestamp String in UTC TZ format to CST TZ format as shown in here "2019-01-02T11:53:59.269-05:00"
So basically i need the output of this query with SYSTIMESTAMP replaced with time String in UTC TZ Format.
select TO_CHAR(SYSTIMESTAMP ,'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM') CREATEDTIME from dual;
I tried lot of stuff but getting errors
select TO_CHAR( to_timestamp('2019-01-02 11:53:59.759', 'YYYY-MM-DD HH24:MI:SS.FF3TZH') ,'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM') CREATEDTIME from dual
Error
ORA-01821: date format not recognized
Could you please help me write the correct SQL query.

With a TSTZ, you can select at a different time-zone.
Below are some examples for daylight- and standard-time.
Standand-Time:
SELECT TO_CHAR(TO_TIMESTAMP_TZ('2019-01-02T16:53:59.269 UTC',
'YYYY-MM-DD"T"HH24:MI:SS.FF3 TZR') AT TIME ZONE 'AMERICA/CHICAGO',
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM')
AS CENTRAL_TIME
FROM DUAL;
Result:
CENTRAL_TIME
2019-01-02T10:53:59.269-06:00
1 row selected.
And a daylight-savings example:
SELECT TO_CHAR(TO_TIMESTAMP_TZ('2019-04-02T17:35:52.136 UTC',
'YYYY-MM-DD"T"HH24:MI:SS.FF3 TZR') AT TIME ZONE 'AMERICA/CHICAGO',
'YYYY-MM-DD"T"HH24:MI:SS.FF3TZH:TZM')
AS CENTRAL_TIME
FROM DUAL;
Result:
CENTRAL_TIME
2019-04-02T12:35:52.136-05:00
1 row selected.

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.

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 to_date with format doesn't show time

I have simple calculation, I subtract interval from date with time:
select TO_DATE('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS') - to_dsinterval('00 0:05:00') from dual;
It works fine, the result: 2016-12-05 22:59:59
but it doesn't work correctly with timezones, so the next approach solves the problem with timezone. I just wrap expression with to_date() one more time
select TO_DATE(
TO_DATE('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS') - to_dsinterval('00 0:05:00')) from dual;
but now it turns time to zeros. Result should be: 2016-12-05 22:59:59 but actual: 2016-12-05 00:00:00
If I add format to the outer to_date as this:
select to_date( TO_DATE('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS') - to_dsinterval('00 0:05:00'), 'YYYY-MM-DD HH24:MI:SS') from dual;
The result become very strange: 0005-12-16 00:00:00
What I'm doing wrong?
DATE data type does not support any time zone functions, you must use TIMESTAMP WITH TIME ZONE for that.
Your query
SELECT TO_DATE( TO_DATE('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS') - TO_DSINTERVAL('00 0:05:00'), 'YYYY-MM-DD HH24:MI:SS')
FROM dual;
does following:
Create a DATE '2016-12-05 23:04:59'
Subtract interval '00 0:05:00'
Cast to a VARCHAR2 (using NLS_DATE_FORMAT format)
Cast to a DATE using YYYY-MM-DD HH24:MI:SS format
In case your NLS_DATE_FORMAT would be equal to YYYY-MM-DD HH24:MI:SS this query returns correct output.
Use this one:
SELECT TO_TIMESTAMP('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS') - TO_DSINTERVAL('00 0:05:00')
FROM dual;
TO_DATE(... works as well. If you need time zone support you must do:
SELECT TO_TIMESTAMP_TZ('2016-12-05 23:04:59 Europe/Berlin', 'YYYY-MM-DD HH24:MI:SS TZR') - TO_DSINTERVAL('00 0:05:00')
FROM dual;
TO_DATE( char, fmt, nls ) takes VARCHAR2 arguments.
Performing TO_DATE('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS') - to_dsinterval('00 0:05:00') returns a DATE datatype which when you pass it to TO_DATE() oracle will cast it to a VARCHAR2 datatype so it matches the expected datatype of the argument (implicitly calling TO_CHAR( value, NLS_DATE_FORMAT ) to perform this cast) and then convert this back to a DATE datatype.
You just need to do:
SELECT TO_DATE('2016-12-05 23:04:59', 'YYYY-MM-DD HH24:MI:SS')
- to_dsinterval('00 0:05:00')
FROM DUAL;
If you want to handle time zones then use a TIMESTAMP AT TIME ZONE and just convert it to whatever timezone you want to store the date at:
SELECT TIMESTAMP '2016-12-05 23:04:59 Europe/Paris' AT TIME ZONE 'UTC'
FROM DUAL;
(Will create your timestamp in Paris' time zone and convert it to the correct time in the UTC time zone).

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.

Oracle Timestamp, Max and Minimal Values

I was searching, also in the Oracle Doc, for the following:
What is the range for Timestamp in Oracle?
I know for date it is -4712, Jan-01 to 9999 Dec-31, but what for Timestamp?
Anyone a clue or hint where I can search?
You can always just try it:
SQL> select to_timestamp( '9999-12-31 23:59:59', 'yyyy-mm-dd hh24:mi:ss' ) from dual;
TO_TIMESTAMP('9999-12-3123:59:59','YYYY-MM-DDHH24:MI:SS')
---------------------------------------------------------------------------
31-DEC-99 11.59.59.000000000 PM
and:
SQL> select to_timestamp( '9999-12-31 23:59:59', 'yyyy-mm-dd hh24:mi:ss' )+1 from dual;
select to_timestamp( '9999-12-31 23:59:59', 'yyyy-mm-dd hh24:mi:ss' )+1 from dual
*
ERROR at line 1:
ORA-01841: (full) year must be between -4713 and +9999, and not be 0
It would be surprising if the range for the DATE portion of a TIMESTAMP was smaller than the range for a DATE, so it should be:
-4712-01-01 00:00:00 to 9999-12-31 23:59:59.999999
That assumes no time zone; the UTC value is probably constrained to that range, but someone in an Eastern time zone might manage to see a data value on 1000-01-01 in their time zone.
It is hard to find definitive data off Oracle's site. The best I found in a casual survey was:
http://www.techonthenet.com/oracle/datatypes.php
There are probably others.
I found a quote which says:
TIMESTAMP Datatype
The TIMESTAMP datatype is an extension of the DATE datatype. It stores the year,
month, and day of the DATE datatype, plus hour, minute, and second values.

Resources