Dificilmente encontraremos uma aplicação corporativa que não lide com datas e horários. Para tal tarefa utilizamos um tipo específico: o DateTime.
O System.DateTime é um value type que representa os horários e datas entre 00:00:00 de 1 de Janeiro de 0001 AD e 11:59:59 de 31 de Janeiro de 9999 AD. Quando falamos, no .NET, em unidade representação de tempo, devemos ter em mente que tal medida é feita baseando-se em intervalos de décimos de microsegundos (1x10E-7 segundos ou, em termos frequência, 10MHz) - os chamados ticks. Temos, portanto, uma grande a precisão nas operações com tempo.
O primeiro ponto crítico a se discutir quando lidamos com datas é a questão das culturas, zonas (fusos) e economia de energia (Daylight Saving). De fato, deveria ser uma prática comum armazenarmos as datas das nossas aplicações corporativas globalizadas em Universal Time Coordinates (UTC) – este horário universal também é conhecido como Greenwich Mean Time (GMT).
A regra fundamental neste tipo de tarefa é que só existe relevância em um cálculo ou comparação de datas se as instâncias de DateTime forem criadas na mesma zona. Adicionamos a este zelo pelas zonas o fato de termos, também, os períodos de economia de energia, no Brasil conhecido como o horário de verão.
Com o Regional Options configurado para [portuguese (Brazil)] e a Time Zone para [(GMT – 03:00) Brasilia], ao executamos o seguinte método:
DateTime d = DateTime.Parse("3/25/2004");Obtemos uma FormatException dizendo que a string não foi reconhecida como um DateTime válido. Ocorre que, para lidarmos com datas em formatos diferentes da configuração regional local, devemos informar um objeto que implementa a interface IFormatProvider. Esta interface visa apresentar informações sobre a cultura, e uma das classes que a implementa é a CultureInfo. Fica assim:
System.Globalization.CultureInfo _ci = new System.Globalization.CultureInfo("en-US");
DateTime d = DateTime.Parse("3/25/2004", _ci);
MessageBox.Show(d.ToString());
No exemplo acima, apesar de estarmos interpretando uma data expressa no formato americano, a mesma é armazenada na instância usando-se o formato local, logo a caixa de mensagem abaixo é a obtida a partir da quarta linha do código citado.

É simples sobrescrever as configurações locais e armazenar as datas em uma cultura diferente da local usando uma sobrecarga do construtor da CultureInfo. A linha ficaria assim (repare no booleano instruindo a sobrescrita):
System.Globalization.CultureInfo _ci = new System.Globalization.CultureInfo("en-US", true);Podemos exibir a data no formato americano ou em qualquer outro, independentemente da cultura armazenada na instância. Para tal usamos o seguinte fragmento.
MessageBox.Show(d.ToString(_ci),"System.DateTime");
Um erro muito comum ao se lidar com a estrutura DateTime se deve ao fato de que os métodos membros da mesma retornam novas instâncias, não modificando, portanto, o valor original. No exemplo a seguir o identificador d permanece inalterado, apesar das operações efetivadas.
DateTime d = DateTime.Parse("3/25/2004", _ci);
MessageBox.Show(d.ToUniversalTime().ToString());
// Exibe 25/03/2004 03:00:00
MessageBox.Show(d.ToUniversalTime().AddHours(5).ToString());
// Exibe 25/03/2004 08:00:00
MessageBox.Show(d.ToString());
// Exibe 25/03/2004 00:00:00
Aproveitando a oportunidade, vejam que invoquei acima o método ToUniversalTime(). Este método facilita o nosso trabalho fazendo o translado do nosso horário local para o universal (UTC/GMT). Este é, como já comentado, o formato recomendado para armazenamento de dados pelas aplicações corporativas globalizadas.
- OK Miguel, é interessante este ponto de vista e, de fato, resolvemos adotar a política de armazenamento do horário universal em detrimento do local pois isto, entre outros benefícios, nos poupará a necessidade de armazenamento das informações do fuso.
Ótimo! Apenas não se esqueça que temos zonas que implementam suporte ao daylight savings time (horário de economia de energia, dia mais longo, horário de verão, etc…). Falarei sobre este assunto em outra oportunidade pois nesta ocasião quero me concentrar na questão mais crítica envolvendo aplicações globalizadas: serialização.
Vamos supor que eu tenha uma classe que, entre outras tarefas, precisa lidar com datas. Em benefício da simplicidade tomemos o seguinte exemplo:
using System;
using System.Xml.Serialization;
namespace MSDN{
[XmlType(TypeName = "timeTestDef", Namespace = "http://tempuri.org/Timetester.xsd"),
XmlRoot(), Serializable()]
public class ClasseSerializavel{
private DateTime _DateTime;
[XmlIgnore()]
private bool _TempoEspecificado = false;
[XmlElement(ElementName = "Tempo", IsNullable = false,
Form = System.Xml.Schema.XmlSchemaForm.Qualified,
DataType = "dateTime", Namespace ="http://dotnetraptors.com.br/Timetester.xsd")]
public DateTime UmaDataQualquer{
get{return _DateTime;}
set{_DateTime = value; _TempoEspecificado = true;}
}
}
}
Vejamos um fragmento de código simulando a utilização da classe acima por uma aplicação cliente que pretende serializar o objeto em questão.
MSDN.ClasseSerializavel _o = new MSDN.ClasseSerializavel();
// vamos trabalhar com horário universal
_o.UmaDataQualquer = DateTime.Parse("7/9/2004 08:30:00");
_o.UmaDataQualquer = _o.UmaDataQualquer.ToUniversalTime();
// Processo de serialização
System.Xml.XmlTextWriter _xmlw = new System.Xml.XmlTextWriter
(@"C:\ClasseSerializavel.xml", System.Text.Encoding.UTF8);
System.Xml.Serialization.XmlSerializer _xmlser =
new System.Xml.Serialization.XmlSerializer(_o.GetType());
_xmlser.Serialize(_xmlw, _o);
_xmlw.Close();
Apenas lendo o código, percebemos que a data está sendo atribuída à propriedade UmaDataQualquer uma data universal (UTC). Pretendemos, inclusive, trabalhar com as datas em UTC para padronizar o armazenamento das nossas aplicações. No que se refere à serialização, é aí que teremos problemas se não testarmos/atentarmos para o que foi serializado. Vejamos o XML obtido:
<?xml version="1.0" encoding="utf-8" ?> <MSDNBrasil xmlns:xsd=http://www.w3.org/2001/XMLSchema xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"?> <UmaDataQualquer xmlns="http://microsoft.com/brasil/msdn"?> 2004-09-07T11:30:00.0000000-03:00 </UmaDataQualquer?> </MSDNBrasil?>
A inconsistência é facilmente identificável uma vez que uma data UTC (GMT) foi atribuída à propriedade UmaDataQualquer da classe mas, após a serialização, percebemos que nosso fuso local (offset [– 03:00]) também foi registrado no XML. Este não deveria ser o caso uma vez que estamos passando datas UTC entre as camadas. Percebemos, assim, que o serializador entende as datas como LOCAL TIME. Isto fatalmente nos trará problemas na deserialização se estivermos passando datas UTC ao serializador.
Este é o tipo de inconsitência é dificilmente detectado uma vez que se testarmos em máquinas no mesmo fuso, nenhum problema será detectado. Mas se deserializarmos o xml em outro fuso, nos surpreenderemos com este inconveniente. Vejamos o código de deserialização:
// Processo de DeSerialização MSDN.ClasseSerializavel _o2 = new MSDN.ClasseSerializavel(); System.Xml.XmlTextReader _xmlr = new System.Xml.XmlTextReader(@"C:\ClasseSerializavel.xml"); System.Xml.Serialization.XmlSerializer _xmlser = new System.Xml.Serialization.XmlSerializer(_o2 .GetType()); _o2 = (MSDN.ClasseSerializavel)_xmlser.Deserialize(_xmlr); _xmlr.Close(); MessageBox.Show(_o2.UmaDataQualquer.ToLocalTime().ToString());
O usuário do código acima está em PST (GMT – 8:00), logo, ao deserializar nossa data (UTC: 7 de Setembro de 2004 – 11:30:00 / LOCAL: 7 de Setembro de 2004 – 08:30:00), deveria obter a data LOCAL: 7 de Setembro de 2004 – 03:30:00, mas obtém LOCAL: 7 de Setembro de 2004 – 00:30:00.
É desnecessário frisar a importância da questão acima na passagem de datas entre webservices e aplicações que usam serialização e deserialização em XML.