Jeśli masz dobry pomysł na wszystkie możliwe formaty dat, może być łatwiej użyć brutalnej siły:
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;
/
Należy pamiętać, że współczesne wersje Oracle są dość wyrozumiałe w przypadku konwersji dat. Ta funkcja obsługiwała daty w formatach, których nie ma na liście, z kilkoma interesującymi konsekwencjami:
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>
Co pokazuje ograniczenia automatycznego czyszczenia danych w obliczu luźnych zasad integralności danych. Zapłatą za grzech są uszkodzone dane.
@AlexPoole podnosi kwestię używania 'RR'
format. Ten element maski daty został wprowadzony jako paczka Y2K. To raczej przygnębiające, że wciąż dyskutujemy o tym prawie dwie dekady nowego tysiąclecia.
W każdym razie problem polega na tym. Jeśli rzucimy ten ciąg '161225'
do tej pory jaki ma wiek? Cóż, 'yymmdd'
da 2016-12-15
. W porządku, ale co z '991225'
? Jakie jest prawdopodobieństwo, że data, której naprawdę chcemy, to 2099-12-15
? W tym miejscu 'RR'
w grę wchodzi format. Zasadniczo domyślnie ustawia wiek:liczby 00-49 domyślnie na 20, 50-99 domyślnie na 19. To okno zostało określone przez kwestię Y2K:w 2000 r. było bardziej prawdopodobne, że '98
odnosiło się do niedalekiej przeszłości niż niedalekiej przyszłości i podobna logika zastosowana do '02
. Stąd połowa roku 1950. Pamiętaj, że jest to punkt stały nie przesuwne okno. Im dalej od roku 2000, tym mniej użyteczny staje się ten punkt obrotu. Dowiedz się więcej.
W każdym razie kluczową kwestią jest to, że 'RRRR' nie działa dobrze z innymi formatami dat:to_date('501212', 'rrrrmmdd') hurls
ora-01843:niepoprawny miesiąc. So, use
'RR'and test for it before using
„RRRR”. Więc moja poprawiona funkcja (z pewnym porządkiem) wygląda tak:
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;
/
Kluczową kwestią pozostaje:istnieje granica tego, jak sprytnie możemy stworzyć tę funkcję, jeśli chodzi o interpretację dat, więc upewnij się, że prowadzisz z najlepszym dopasowaniem. Jeśli uważasz, że większość twoich ciągów dat pasuje do dnia-miesiąca-roku, umieść to jako pierwsze; nadal będziesz mieć złe rzuty, ale mniej, jeśli prowadzisz rok-miesiąc-dzień.