Date Conversion of one Timezone to different timezone - oracle

I have a date say 2011-09-26T21:00:00Z . I'm not sure what is T and Z in the date meant. This is EET time. I need to convert this to local time 'IST'.
Expected O/P : 2011-09-27T00:30:00Z ( since we have 3 hours 30 minutes difference b/w EET and IST. Even 2011-09-27 00:30:00 or 2011-09-27(at the least case) is ok for me.
I'm trying the below query But getting error as TimeZone region not found
select to_char((from_tz(to_timestamp(to_char('2011-09-26T21:00:00Z','YYYY-MM-DD HH24:MI:SS')
,'YYYY-MM-DD HH:MI:SS PM') ,'EET')
at time zone 'IST'),'YYYY-MM-DD HH:MI:SS PM TZD') as localtime
from dual;
Somebody pls suggest any other ways to convert.
UPDATE:
I have tried the below query. It is giving output. But that is not the desired format.
select TO_TIMESTAMP_TZ(TO_CHAR((to_timestamp('2011-09-26 21:00:00','yyyy/mm/dd hh24:mi:ss')),
'yyyy/mm/dd hh24:mi:ss')||' Europe/Berlin',
'yyyy/mm/dd hh24:mi:ss tzr') AT TIME ZONE 'Asia/Calcutta' ts_eet
from dual;
OUTPUT: 27-SEP-11 12.30.00.000000000 AM ASIA/CALCUTTA . Here i have one problem 1) I dont want SEP in month instead i need only 09 and the format should be same like 2011-09-27

Oracle treats EET as a time zone region name (SELECT tzname, tzabbrev FROM V$TIMEZONE_NAMES WHERE tzname = 'EET', or see the docs) so it handles daylight saving/summer time (EEST/EEDT). On 2011-09-26 the EET region was actually EEST so it was three hours ahead of UTC, not two; and as IST is always +05:30 the offset was actually +02:30. So your result is 'correct'.
Rather than bounce back and forth to a string, you can use the from_tz function to change a plain timestamp into a timestamp with time zone; and then at time zone to get that in a different zone:
select to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') orig_ts,
from_tz(to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'EET') orig_tsz,
sys_extract_utc(from_tz(to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'EET')) orig_utc,
from_tz(to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'EET') at time zone 'Asia/Calcutta' new_tsz
from dual;
ORIG_TS ORIG_TSZ ORIG_UTC NEW_TSZ
------------------- -------------------------- ------------------- ---------------------------------
2011-09-26 21:00:00 2011-09-26 21:00:00 EET 2011-09-26 18:00:00 2011-09-26 23:30:00 ASIA/CALCUTTA
The 'T' in your original string denotes the time, and the 'Z' denotes (or is supposed to!) that this represents a date/time in UTC; this is the ISO 8601 standard notation. Oracle's conversion functions allow you to embed character literals in the format model to ignore those. Since it has a 'Z' you should be treating it as UTC not EET, so you might want to go back to whoever you're getting these values from.
If you really do expect all the values to be from the EET timezone with no summer time offset then you could use a fixed TZH:TZM rather than a region name:
select to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') orig_ts,
from_tz(to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'+02:00') orig_tsz,
sys_extract_utc(from_tz(to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'+02:00')) orig_utc,
from_tz(to_timestamp('2011-09-26T21:00:00Z', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'),
'+02:00') at time zone 'Asia/Calcutta' new_tsz
from dual;
ORIG_TS ORIG_TSZ ORIG_UTC NEW_TSZ
------------------- -------------------------- ------------------- ---------------------------------
2011-09-26 21:00:00 2011-09-26 21:00:00 +02:00 2011-09-26 19:00:00 2011-09-27 00:30:00 ASIA/CALCUTTA
I doubt that is actually what you want but you'll need to clarify your requirements.
Finally to get the converted value back into the original format you need to use to_char:
select to_char(from_tz(to_timestamp('2011-09-26T21:00:00Z',
'YYYY-MM-DD"T"HH24:MI:SS"Z"'), 'EET') at time zone 'Asia/Calcutta',
'YYYY-MM-DD HH24:MI:SS') new_string
from dual;
NEW_STRING
-------------------
2011-09-26 23:30:00
You can embed the 'T' back in with 'YYYY-MM-DD"T"HH24:MI:SS'; adding the 'Z' back onto the end seems wrong though.
If your source time is from Berlin as your question edit suggests (which is CET/CEST, not EET/EEST; gaining you that missing hour) just change the from_tz region:
select to_char(from_tz(to_timestamp('2011-09-26T21:00:00Z',
'YYYY-MM-DD"T"HH24:MI:SS"Z"'), 'Europe/Berlin') at time zone 'Asia/Calcutta',
'YYYY-MM-DD"T"HH24:MI:SS') new_string
from dual;
NEW_STRING
-------------------
2011-09-27T00:30:00

Related

Converting TIMESTAMP to DATE with respect to timezone

I was inspecting how are DATEs stored in database. Consider the following statement:
SELECT
CAST (to_timestamp_tz('2018-12-05T10:00:00+01:00', 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE) AS PRAGUE_TIME,
CAST (to_timestamp_tz('2018-12-05T10:00:00+00:00', 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE) AS GMT_TIME
FROM DUAL
Results in:
PRAGUE_TIME GMT_TIME
2018-12-05 10:00:00 2018-12-05 10:00:00
The times are the same, despite one being created from a +1 timezone offset. Just to be sure, I added further conversion to string:
SELECT
TO_CHAR(CAST (to_timestamp_tz('2018-12-05T10:00:00+01:00', 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE),'YYYY-MM-DD HH24:MI:SS') AS PRAGUE_TIME,
TO_CHAR(CAST (to_timestamp_tz('2018-12-05T10:00:00+00:00', 'YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE),'YYYY-MM-DD HH24:MI:SS') AS GMT_TIME
FROM DUAL
PRAGUE_TIME GMT_TIME
2018-12-05 10:00:00 2018-12-05 10:00:00
Same results. So how can I convert TIMESTAMP to DATE without loosing the timezone information?
how can I convert TIMESTAMP to DATE without loosing the timezone information?
You cannot.
A DATE only has year, month, day, hour, minute and second components.
A TIMESTAMP has all those components plus nanoseconds and optionally time zone or time zone offset components.
If you convert from a TIMESTAMP to a DATE then you will lose the information that the DATE cannot store.
What you can do is convert all the TIMESTAMPs to the same time zone using, for example, AT TIME ZONE 'UTC':
SELECT CAST( TIMESTAMP '2018-12-05 10:00:00+01:00' AT TIME ZONE 'UTC' AS DATE )
AS PRAGUE_TIME_AS_UTC,
CAST( TIMESTAMP '2018-12-05 10:00:00+00:00' AT TIME ZONE 'UTC' AS DATE )
AS GMT_TIME_AS_UTC
FROM DUAL
Results:
| PRAGUE_TIME_AS_UTC | GMT_TIME_AS_UTC |
|----------------------|----------------------|
| 2018-12-05T09:00:00Z | 2018-12-05T10:00:00Z |

Convert epoch to date in Oracle

Was wondering if anyone could help with precision time conversion.
Sample: 1501646399999 which is GMT: Wednesday, August 2, 2017 3:59:59.999 AM
I used the below query, but it always rounds off to 02-AUG-17 04:00:00. Can anyone please guide me
select TO_TIMESTAMP('1970-01-01 00:00:00.000', 'YYYY-MM-DD hh24:mi:SS.FF3') + ((1/86400000) * 1501646399999)
from dual;
The problem is that you're adding a number to your fixed timestamp, which is causing that timestamp to be implicitly converted to a date - which doesn't have sub-second precision.
If you add an interval instead then it stays as a timestamp:
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF3';
select TO_TIMESTAMP('1970-01-01 00:00:00.000', 'YYYY-MM-DD hh24:mi:SS.FF3')
+ numtodsinterval(1501646399999/1000, 'SECOND')
from dual;
TO_TIMESTAMP('1970-01-0
-----------------------
2017-08-02 03:59:59.999
Incidentally, you could slightly simplify your query with a timestamp literal:
select TIMESTAMP '1970-01-01 00:00:00' + numtodsinterval(...)
You may also want to check if you should be declaring that timestamp as being UTC, and converting back to local time zone after adding the epoch value; or leaving it explicitly as UTC but as a timestamp with time zone value. It depends exactly what that number is supposed to represent. (You said it's GMT/UTC, but still...)

Oracle giving ORA-01821 on a format that appears to be valid

Calling
SELECT TO_DATE('Mon Sep 22 18:02:41 CDT 2014', 'DY MON DD HH24:MI:SS TZD YYYY') FROM Dual;
I get
ORA-01821: date format not recognized
01821. 00000 - "date format not recognized"
I put together the format string from these Oracle instructions. I tried both "TZR" and "TZD", neither works.
DY Abbreviated name of day.
MON Abbreviated name of month.
DD Day of month (1-31).
HH24 Hour of day (0-23).
MI Minute (0-59).
SS Second (0-59).
TZD Daylight savings information. For example, 'PST'
TZR Time zone region.
YYYY 4-digit year
Why is this not working?
TO_DATE doesn't support timezone.
I think you need to use to_timestamp_TZ() to do what you're after...
SELECT To_TimeStamp_TZ('Mon Sep 22 18:02:41 2014 CDT', 'DY MON DD HH24:MI:SS YYYY TZD')
FROM Dual;
Gives you something like (with my NLS paramaters)
22-SEP-14 06.02.41.000000000 PM AMERICA/CHICAGO
Also note the data type must be TIMESTAMP WITH TIME ZONE Data Type; or oracle just drops the timezone information w/o error.
https://docs.oracle.com/cd/E11882_01/server.112/e10729/ch4datetime.htm#NLSPG238

I have a varchar value 1st March 2017 that i need as a date 01-MAR-2017 in pl/sql

SELECT TO_DATE('1st March 2017','DD MON YYYY') from dual
It's ok with SELECT TO_DATE(01 March 2017','DD MON YYYY') from dual , doesn't like the 'st'
I think you need something like regex:
SELECT TO_DATE(
regexp_replace('1st March 2017','^(\d+)\w+','\1')
,'DD MON YYYY') from dual
Alas, you can't do it directly: https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#BABGDDFB
Notes on date format element suffixes:
When you add one of these suffixes to a datetime format element, the return value is always in English.
Datetime suffixes are valid only to format output. You cannot use them to insert a date into the database.
Which actually means they work only with to_char() to transform a date into a string; they don't work with to_date() to convert a string to a date.
So you will have to play dirty tricks - perhaps regexp_replace to get rid of st. Like Michael has shown already.
Hope here is your answer
SELECT to_date('1st march 2017','dd"st" month yyyy') "Date" FROM dual;
Date
01-MAR-17
SELECT TO_CHAR(to_date('1st march 2017','dd"st" month yyyy'),'dd-mon-yyyy') "Date"
FROM dual;
Date
01-mar-2017

Convert multiple date varchar2 to date format in Oracle 11g

I am having an issue trying to convert multiple dates to one defined format. We are receiving the multiple dates from another DB source so I do not have control of the formatting until it reaches ours.
Here are all the formats:
YYYYMMDD
YYYY-MM-DD HH:MM:SS
MM/DD/YYYY
MM-DD-YYYY
Abrieviated Day Month DD HH:MM:SS TimeZone YYYY ('Thu Feb 02 20:49:59 MSK 2012')
Fully written Day, Month DD, YYYY HH:MM:SS AM/PM
My requirement is to set them all to the standard MM/DD/YYYY format or null. Any ideas?
Thank you.
You may define a transformation function, basically processing sequentially each format:
create or replace function translate_date(i_date_string VARCHAR2) return date as
begin
-- you may optimize to not to go in all blocks based on the string format
-- order the blocks on the expected frequency
begin
return to_date(i_date_string,'yyyymmdd');
EXCEPTION
WHEN OTHERS THEN NULL;
end;
begin
return to_date(i_date_string,'yyyy/mm/dd');
EXCEPTION
WHEN OTHERS THEN NULL;
end;
begin
return to_date(i_date_string,'yyyy-mm-dd');
EXCEPTION
WHEN OTHERS THEN NULL;
end;
begin
return to_date(i_date_string,'yyyy-mm-dd hh24:mi:ss');
EXCEPTION
WHEN OTHERS THEN NULL;
end;
begin
-- transform to local timestamp and than to date
return cast(cast(to_timestamp_tz(i_date_string,'dy month dd hh24:mi:ss tzr yyyy') as TIMESTAMP WITH LOCAL TIME ZONE) as date);
EXCEPTION
WHEN OTHERS THEN NULL;
end;
begin
return to_date(i_date_string,'dy, month dd, yyyy hh:mi:ss am');
EXCEPTION
WHEN OTHERS THEN NULL;
end;
return NULL;
end;
/
for example for sample data
TSTMP
------------------------
20150101
2015-01-01 23:59:59
2015/01/01
2015-01-01
Thu Feb 02 20:49:59 Europe/Moscow 2012
Thu, Feb 02, 2012 10:49:59 AM
Thu, Feb 02, 2012 10:49:59 PM
you get
TSTMP RESULT_DATE
------------------------------------------ -------------------
20150101 01.01.2015 00:00:00
2015-01-01 23:59:59 01.01.2015 23:59:59
2015/01/01 01.01.2015 00:00:00
2015-01-01 01.01.2015 00:00:00
Thu Feb 02 20:49:59 Europe/Moscow 2012 02.02.2012 17:49:59
Thu, Feb 02, 2012 10:49:59 AM 02.02.2012 10:49:59
Thu, Feb 02, 2012 10:49:59 PM 02.02.2012 22:49:59
Note that I skipped the case with time zone abbraviation (MSK), see possible solution in the answer from #Sentinel, but check Conversion of String with Abbreviated Timezone to Timestamp that this may be ambiguous.
I'd suggest using a case statement with regexp_like conditions to detect likely formats and return dates using the appropriate date mask in the then clauses e.g.:
with tz as (
SELECT distinct tzabbrev
, first_value(min(tzname)) over (partition by tzabbrev order by count(*) desc) tzname
FROM v$timezone_names
group by tzabbrev
, TZ_OFFSET(tzname)
), dta as (
select yt.install_date
, regexp_replace(yt.install_date,tzabbrev,tzname,1,1,'i') install_date2
from your_table yt
left join tz
on regexp_like(install_date, tz.TZABBREV,'i')
)
select install_date, install_date2
, to_timestamp_tz( install_date2
, case
when regexp_like(install_date2,'^[A-Z]{3,} [A-Z]{3,} [0-9]{1,2} [0-9]{1,2}(:[0-9]{2}){1,2} [[:print:]]{5,} [0-9]{2,4}','i') then 'DY MON DD HH24:MI:SS TZR YYYY'
when regexp_like(install_date2,'^[A-Z]{4,},? [A-Z]{3,},? [0-9]{1,2},? [0-9]{2,4}','i') then 'DAY MONTH DD YYYY'
when regexp_like(install_date2,'^[A-Z]{3},? [A-Z]{3,},? [0-9]{1,2},? [0-9]{2,4}','i') then 'DY MONTH DD YYYY'
when regexp_like(install_date2,'^[0-9]{1,2}[-/][0-9]{1,2}[-/]([0-9]{2}){1,2}') then 'MM-DD-RRRR'
when regexp_like(install_date2,'^[0-9]{1,2}[-/ ][A-Z]{3,}[-/ ]([0-9]{2}){1,2}','i') then 'DD-MON-RRRR'
when regexp_like(install_date2,'^[A-Z]{3,}[-/ ][0-9]{1,2},?[-/ ]([0-9]{2}){1,2}','i') then 'MON-DD-RRRR'
when regexp_like(install_date2,'^(19|20)[0-9]{6}') then 'RRRRMMDD'
when regexp_like(install_date2,'^[23][0-9]{5}') then 'DDMMRR'
when regexp_like(install_date2,'^[0-9]{6}') then 'MMDDRR'
when regexp_like(install_date2,'^[01][0-9]{7}') then 'MMDDRRRR'
when regexp_like(install_date2,'^[23][0-9]{7}') then 'DDMMRRRR'
ELSE NULL
end
||case
when regexp_like(install_date2, '[0-9]{1,2}(:[0-9]{2}){1,2}$') then ' HH24:MI:SS'
when regexp_like(install_date2, '[0-9]{1,2}(:[0-9]{2}){1,2} ?(am|pm)$','i') then ' HH:MI:SS AM'
else null
end
)
Install_Time_Stamp
from dta;
I had issues with the time zone abbreviations so I added a step to replace them with time zone regions first.
The function can be simplified a little bit as to_date tolerates minor deviations (different separators, missing separators, missing time components, 2/4-digit year). For instance to_date(:str,'rrrr-mm-dd hh24:mi:ss') will cover
2020/09/18 01.02
2020.09.18 01
20200918010203
2020-0901
202009-01
20/09/18
To_timestamp_tz will also tolerate missing milliseconds and time zone (including missing TZ elements TZM and TZD).
So we basically need to take care of major variations only like hh24/hh, mm/mon, elements order, "T" separator in ISO 8601 (2020-01-01T01:02:03), TZ designators (UTC offset TZH:TZM / region name TZR), and day of week (DY).

Resources