Ако имате добра представа за всички възможни формати за дата, може да е по-лесно да използвате груба сила:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MON-YYYY', 'DD-MON-YY', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD'
, 'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'DD/MM/YY', 'MM/DD/YY');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
Имайте предвид, че съвременните версии на Oracle са доста прощаващи с преобразуване на дата. Тази функция обработва дати във формати, които не са в списъка, с някои интересни последици:
SQL> select clean_date('20160817') from dual;
CLEAN_DAT
---------
17-AUG-16
SQL> select clean_date('160817') from dual;
CLEAN_DAT
---------
16-AUG-17
SQL>
Което демонстрира границите на автоматизирано почистване на данни в лицето на слабите правила за целостта на данните. Заплатата на греха е покварени данни.
@AlexPoole повдига въпроса за използването на 'RR'
формат. Този елемент от маската за дата е въведен като Y2K kludge. Доста депресиращо е, че все още го обсъждаме почти две десетилетия в новото хилядолетие.
Както и да е, въпросът е следният. Ако изведем този низ '161225'
към дата кой век има? Е, 'yymmdd'
ще даде 2016-12-15
. Справедливо, но какво да кажем за '991225'
? Колко вероятно е датата, която наистина искаме, да е 2099-12-15
? Това е мястото, където 'RR'
форматът влиза в игра. По принцип той задава по подразбиране века:числата 00-49 по подразбиране на 20, 50-99 по подразбиране на 19. Този прозорец беше определен от проблема Y2K:през 2000 г. беше по-вероятно '98
отнася се до близкото минало, отколкото до близкото бъдеще и подобна логика се прилага към '02
. Оттук и половината от 1950 г. Обърнете внимание, че това е фиксирана точка не плъзгащ се прозорец. С напредването на 2000 г. тази опорна точка става по-малко полезна. Научете повече.
Както и да е, ключовият момент е, че 'RRRR' не играе добре с други формати за дата:to_date('501212', 'rrrrmmdd') hurls
ora-01843:не е валиден месец. So, use
'RR'and test for it before using
'YYYY''. Така че моята преработена функция (с известно подреждане) изглежда така:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MM-RR', 'MM-DD-RR', 'RR-MM-DD', 'RR-DD-MM'
, 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'YYYY-DD-MM');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
Ключовият момент остава:има ограничение за това колко умни можем да направим тази функция, когато става въпрос за тълкуване на дати, така че се уверете, че водите най-добре. Ако смятате, че повечето от низовете ви за дата отговарят на ден-месец-година, поставете това на първо място; пак ще получите някои грешни замятания, но по-малко, ако водите с година-месец-ден.