Validação e conversão de datas
- Gerar link
- Outros aplicativos
Dentre os problemas mais recorrentes em sistemas podemos citar os relacionados à cultura: charset, datas e formatação de números.
Datas são especialmente problemáticas porque podem ser ambíguas dependendo da cultura, por exemplo 02/01/1990 pode ser dois de janeiro no Brasil ou primeiro de fevereiro nos EUA.
Ainda mais agravante é o fato de nem sempre podermos validar as datas nos imputs, pois as mesmas podem vir de arquivos de integração, outros sistemas, CSV, TXT, Excel ou webservices.
Pensando nisso eu fiz uma pequea demonstração do uso dos comandos cast e convert para converter varchar para datas e datas para varchar, e mostrando onde estão os erros mais comuns.
/******************************************************************************************************* ******************************** DEMONSTRAÇÃO DE CONVERSÃO DE DATAS ************************************ ********************************************************************************************************/ --imagine que tipo de coisa pode vir em um campo de "data" que não é datetime, ou um arquivo xml, txt, csv mal convertido --imagine também o problema de gravar a data em uma linguagem, formatar para exibição em outra, e os problemas que sempre acontecem ao se converter string de um campo de texto para datetime /*************************************** teste do convert *********************************/ --qual é a data zero select convert (datetime, 0, 103) --R: é a data 1900-01-01 00:00:00.000 --e se a data for null? select convert (datetime, null, 103) --R: null --E se a data for uma string vazia? select convert (datetime, '', 103) --R: é a mesma coisa que a data zero 1900-01-01 00:00:00.000 (aqui eu acho que deveria dar um erro) --mesma coisa só que usando o padrão americano select convert (datetime, 0, 101) --1900-01-01 00:00:00.000 select convert (datetime, null, 101) --null select convert (datetime, '', 101) --1900-01-01 00:00:00.000 /*************************************** teste do cast *********************************/ select cast (null as datetime) --como esperado, null select cast (0 as datetime) --como esperado, a data zero 1900-01-01 00:00:00.000 select cast ('' as datetime) --1900-01-01 00:00:00.000 --agora, se em um arquivo de texto ou xml você tem uma data 0 (zero) ou '' (string vazia), isso é essencialmente um erro. Não quer dizer que a data real seja em primeiro de janeiro de 1900, mas sim que o campo não foi preenchido, logo, certos literais deveriam ser convertidos para null select CAST(nullif(/*campo_que_pode_ser_string_vazia*/'','') as datetime) select CAST(nullif(/*campo_que_pode_ser_zero*/0,0) as datetime) --para todos os casos select CAST( coalesce( nullif(/*campo_que_pode_ser_string_vazia*/'',''), --se for null tenta o proximo ... nullif(/*campo_que_pode_ser_zero*/0,0), --- que se for null de novo vai null mesmo null) as datetime) /*********************************************************************************************************************** ******************************************** exemplos de problemas de conversão de datas ******************************* ***********************************************************************************************************************/ --fique atento quando as datas podem vir de outros bancos de dados, de sistemas que formatam de uma forma não canônica e quando você está usando o cast em vez do convert com formato específico --troque 2014 pelo ano corrente e não faça esse teste em 1º de janeiro --o que é maior, a data de hoje ou 2014-12-01 ? select ( cast ('2014-12-1' as datetime)), getdate(), case when cast ('2014-12-1' as datetime) > getdate() then 'Maior' else 'Menor' end --como estamos em março, primeiro de dezembro é maior --o que é maior, a data de hoje ou 2014.12.1 ? select (cast ('2014.12.1' as datetime)), getdate(), case when cast ('2014.12.1' as datetime) > getdate() then 'Maior' else 'Menor' end --padrão Brasileiro --o que é maior, a data de hoje ou 2014-12-01 ? select ( convert (datetime,'2014-12-1', 103)), getdate(), case when convert (datetime,'2014-12-1', 103) > getdate() then 'Maior' else 'Menor' end --como estamos em março, primeiro de dezembro é maior --o que é maior, a data de hoje ou 2014.12.1 ? select (convert (datetime,'2014.12.1', 103)), getdate(), case when convert (datetime,'2014.12.1', 103) > getdate() then 'Maior' else 'Menor' end --padrão americano --o que é maior, a data de hoje ou 2014-12-01 ? select ( convert (datetime,'2014-12-1', 101)), getdate(), case when convert (datetime,'2014-12-1', 101) > getdate() then 'Maior' else 'Menor' end --como estamos em março, primeiro de dezembro é maior --o que é maior, a data de hoje ou 2014.12.1 ? select (convert (datetime,'2014.12.1', 101)), getdate(), case when convert (datetime,'2014.12.1', 101) > getdate() then 'Maior' else 'Menor' end --isso mostra que o formato AAAA-MM-DD ou AAAA.MM.DD, nessa ordem, sempre é interpretado corretamente pelo SQL, independente da linguagem. Agora coisas estranhas podem ocorrer se você obtiver uma data da qual não sabe o formato e tentar converter para datetime forçando uma formatação brasileira ou americana --lembre-se também que o CAST depende da data do sistema. --Faça de tudo para, tanto para datas como para formatos numéricos, nunca depender das informações de localização e linguagem do servidor de SQL mas depender somente do servidor WEB e do .net framework, ou, na melhor das hipóteses, o que o usuário informar em suas preferências. --padrão Brasileiro select ( convert (datetime,'01/12/2014', 103)), getdate(), case when convert (datetime,'01/12/2014', 103) > getdate() then 'Maior' else 'Menor' end --beleza select (convert (datetime,'01.12.2014', 103)), getdate(), case when convert (datetime,'01.12.2014', 103) > getdate() then 'Maior' else 'Menor' end --beleza --padrão americano select ( convert (datetime,'01/12/2014', 101)), getdate(), case when convert (datetime,'01/12/2014', 101) > getdate() then 'Maior' else 'Menor' end --epa! select (convert (datetime,'01.12.2014', 101)), getdate(), case when convert (datetime,'01.12.2014', 101) > getdate() then 'Maior' else 'Menor' end --epa!
Porém não é apenas nas rotinas de banco de dados que devemos nos preocupar com as datas. Em sistemas data centric ou orientados a dados, ou com muitas stored procedures, as dicas acima são uma mão na roda, mas em sistemas orientados a objetos onde o problema das datas é resolvido no domínio da aplicação, via código, é bom conhecer as ferramentas disponibilizadas pelo seu ambiente.
No caso do C# temos Culture, e as ferramentas para identificar a culture corrente, sugerir uma padrão ou modificar a Culture.
Um outro problema relacionado são as datas no nosso domínio que podem ser nulas, mas que devemos em algum momento converter para string dependendo da aplicação para sua exibição.
Nesses casos provavelmente você tem no seu banco de dados um campo null, e na aplicação um DateTime? nulável.
Soma-se a isso o fato de a data mínima do banco de dados ser muito maior do que a data mínima na aplicação. Isso gera muito código repetitivo onde se verifica se uma data é nula, depois verifica-se se ela é a data mínima para só então fazer a conversão.
Isso pode ser feito via extension method de DateTime ou um método de uma classe estática, mais ou menos como abaixo, e poupar muito código repetitivo:
public static string DateToString(DateTime? data) { //se for nula retorna string vazia if (data == null) { return ""; } //não sei se é possível não ser nula e não ter um valor, acho que essas três linhas são inúteis, mas são uma alternativa às três linhas de cima if (!data.HasValue) { return ""; } //se a data for menor ou igual a data mínima do banco de dados é porque era para ser originalmente nula e foi erroneamente gravada ou exportada de forma incorreta (para um arquivo texto com a data mínima em vez de branco, por exemplo). if (data.Value <= new DateTime(1900, 1, 1)) { return ""; } //... //código para obter a culture corrente ou escolhida pelo usuário //... //devolvendo a data como string //em um formato padrão //return data.Value.ToString("dd/MM/yyyy"); //ou dependente da culture return data.Value.ToString(CultureEncontrada); }
Uma dica simples, em breve mais dicas sobre cultura, encoding e afins.
- Gerar link
- Outros aplicativos
Comentários
Postar um comentário