Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

oracle - converte muitos formatos de data em uma única data formatada


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.