Se você tiver uma boa ideia de todos os formatos de data possíveis, pode ser mais fácil usar força 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;
/
Esteja ciente de que as versões modernas do Oracle são bastante tolerantes com a conversão de data. Esta função tratou datas em formatos que não estão na lista, com algumas consequências interessantes:
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>
O que demonstra os limites da limpeza automatizada de dados em face de regras frouxas de integridade de dados. O salário do pecado são dados corrompidos.
@AlexPoole levanta a questão de usar o
'RR'
formato. Este elemento da máscara de data foi introduzido como um kludge Y2K. É bastante deprimente que ainda estejamos discutindo isso quase duas décadas no novo Milênio. De qualquer forma, a questão é esta. Se convertermos esta string
'161225'
a uma data que século tem? Bem, 'yymmdd'
dará 2016-12-15
. Justo, mas e quanto a '991225'
? Qual é a probabilidade de que a data que realmente queremos seja 2099-12-15
? É aqui que o 'RR'
formato entra em jogo. Basicamente, o padrão do século:números 00-49 padrão para 20, 50-99 padrão para 19. Esta janela foi determinada pelo problema Y2K:em 2000 era mais provável que '98
referindo-se ao passado recente do que ao futuro próximo, e lógica semelhante aplicada a '02
. Daí o ponto médio de 1950. Observe que este é um ponto fixo não uma janela deslizante. À medida que nos afastamos do ano 2000, menos útil se torna o ponto de pivô. Descubra mais. De qualquer forma, o ponto chave é que 'RRRR' não funciona bem com outros formatos de data:
to_date('501212', 'rrrrmmdd') hurls
ora-01843:não é um mês válido. So, use
'RR'and test for it before using
'AAAA'. Então, minha função revisada (com algumas arrumações) se parece com isso: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;
/
O ponto-chave permanece:há um limite para o quão inteligente podemos tornar essa função quando se trata de interpretar datas, portanto, certifique-se de liderar com o melhor ajuste. Se você acha que a maioria de suas strings de data se encaixam em dia-mês-ano, coloque isso primeiro; você ainda terá alguns lançamentos errados, mas menos do que se você liderar com ano-mês-dia.