Se hai una buona idea di tutti i possibili formati di data, potrebbe essere più facile usare la forza bruta:
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;
/
Tieni presente che le versioni moderne di Oracle sono abbastanza indulgenti con la conversione della data. Questa funzione ha gestito le date in formati non presenti nell'elenco, con alcune interessanti conseguenze:
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>
Il che dimostra i limiti della pulizia automatizzata dei dati di fronte a regole di integrità dei dati permissive. Il salario del peccato è dato corrotto.
@AlexPoole solleva la questione dell'utilizzo del 'RR'
formato. Questo elemento della maschera della data è stato introdotto come un kludge per l'anno 2000. È piuttosto deprimente che ne stiamo ancora discutendo a quasi due decenni dall'inizio del nuovo millennio.
Comunque il problema è questo. Se trasmettiamo questa stringa '161225'
ad una data che secolo ha? Bene, 'yymmdd'
darà 2016-12-15
. Abbastanza giusto, ma per quanto riguarda '991225'
? Quanto è probabile che la data che vogliamo davvero sia 2099-12-15
? Qui è dove si trova il 'RR'
entra in gioco il formato Fondamentalmente è predefinito il secolo:i numeri 00-49 predefinito a 20, 50-99 predefinito a 19. Questa finestra è stata determinata dal problema dell'anno 2000:nel 2000 era più probabile che '98
riferito al passato recente piuttosto che al futuro prossimo e una logica simile applicata a '02
. Da qui la metà del 1950. Nota che questo è un punto fermo non una finestra scorrevole. Man mano che ci spostiamo dall'anno 2000, il punto pivot diventa meno utile. Scopri di più.
Ad ogni modo, il punto chiave è che 'RRRR' non funziona bene con altri formati di data:to_date('501212', 'rrrrmmdd') hurls
ora-01843:mese non valido. So, use
'RR'and test for it before using
'AAAA'`. Quindi la mia funzione rivista (con un po' di riordino) si presenta così:
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;
/
Il punto chiave rimane:c'è un limite a quanto possiamo fare in modo intelligente questa funzione quando si tratta di interpretare le date, quindi assicurati di guidare con il miglior adattamento. Se ritieni che la maggior parte delle stringhe di data si adattino a giorno-mese-anno, mettila prima; otterrai comunque alcuni cast sbagliati, ma meno che se guidi con anno-mese-giorno.