Convert multiple date varchar2 to date format in Oracle 11g - oracle

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).

Related

Convert Mon yyyy to yyyy-mm-dd format in oracle

I am sending date (August 2022) from java as a string to oracle plsql and i want to change the format from August 2022 to 01-08-22 in plsql. how can i do it ?
below is a part of code:
PROCEDURE CHECK_DUMMY
(
IN_SL_NO IN SIGN.SL_NO%TYPE,
IN_MONTH IN SIGN.MONTH%TYPE
)
AS
V_MON DATE := TO_DATE(IN_MONTH , 'DD-MM-YYYY');
You want to use the format model Month YYYY and specify the language you are using:
CREATE PROCEDURE CHECK_DUMMY
(
IN_SL_NO IN SIGN.SL_NO%TYPE,
IN_MONTH IN SIGN.MONTH%TYPE
)
AS
V_MON DATE := TO_DATE(IN_MONTH , 'Month YYYY', 'NLS_DATE_LANGUAGE=English');
BEGIN
-- Do Something
DBMS_OUTPUT.PUT_LINE( v_mon );
END;
/
Then:
BEGIN
CHECK_DUMMY(1, 'August 2022');
END;
/
Outputs:
2022-08-01 00:00:00
Note: In Oracle, a DATE is a binary data type comprising of 7 bytes that represent century, year-of-century, month, day, hours, minutes and seconds and always has those 7 components and is never stored with any particular format. If you want to output 01-08-22 for display purposes then use TO_CHAR(v_mon, 'DD-MM-YY') to convert the date to a formatted string.
fiddle

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

Date Conversion of one Timezone to different timezone

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

How to covert string to date in oracle

How can I convert the string "2014-03-14 15:15:35 PM EST to date format in Oracle by using 'to-date function. Would apprecaite help
The only way I can think of is to ignore "PM":
select to_timestamp_tz('2014-03-14 15:15:35 PM EST', 'YYYY-MM-DD HH24:MI:SS "PM" TZD')
from dual;
15:15:35 PM - the string contains 24-hour format, but at the same time it contains a "PM" indicator. Oracle cannot understand that. Is that 03:15:35 AM or 03:15:35 PM?

Resources