jueves, 14 de julio de 2016

Manipulación de Fechas y Horas en C# - Parte 4/5: Fechas y Zonas Horarias

Índice

1. Introducción
2. Palabras Clave
3. DateTime y Zonas Horarias
4. DateTimeOffset y zonas Horarias
5. Clases TimeZone y TimeZoneInfo
5.1 Clase TimeZone
5.2 Clase TimeZoneInfo
6. Conclusiones
7. Literatura & Enlaces

1. Introducción

Este artículo ilustra al programador de C# acerca de los detalles que influencia el comportamiento de elementos de las estructuras DateTime y DateTimeOffset. De igual modo, se explora los tipos de datos TimeZone y TimeZoneInfo: estos tipos proveen información acerca de las conas horarias, compensaciones (offsets) y horario de verano.

2. Palabras Clave

  • Fecha
  • Hora
  • Horario de verano
  • Offset
  • Zona horaria

3. DateTime y Zonas Horarias

La estructura DateTime ("DateTime Structure", 2016) representa y manipula valores de fecha y hora a través de dos piezas básicas de información: 
  • Un valor numérico de 62 bits: Indica el número de tics a partir de la fecha 1/1/0001
  • Un valor de enumeración de 2 bits: Indica a través de la enumeración DateTimeKind ("DateTimeKind Enumeration", 2016) si un objeto DateTime representa una hora local, UTC (Coordinated Universal Time) o ninguna de los anteriores.
Ahora, se supone el caso en que el programador de C# compara dos instancias de la estructura DateTime con el operador de igualdad ==

DateTime dt1 = new DateTime(2012, 1, 1, 7, 0, 0, DateTimeKind.Local);
DateTime dt2 = new DateTime(2012, 1, 1, 7, 0, 0, DateTimeKind.Utc);
Console.WriteLine(dt1 == dt2);

El valor mostrado por la expresión Console.WriteLine(dt1 == dt2); es true. El operador de igualdad ignora el valor de DateTimeKind (Albahari, 2012) almacenado en la instancia.

Mientras que 

DateTime local = DateTime.Now;
DateTime utc = local.ToUniversalTime();
Console.WriteLine(local == utc);

muestra false. Esto es así porque el método de instancia ToUniversalTime() ("DateTime.ToUniversalTime Method ()", 2016) crea un nuevo objeto DateTime con valores de fecha y hora en UTC. El programador también debe tener en cuenta que un objeto DateTime es inmutable.

De manera análoga, esto también ocurre con el método ToLocalTime() ("DateTime.ToLocalTime Method()", 2016); este método convierte los valores de fecha y hora a local de acuerdo a la configuración regional del sistema (Albahari, 2012).

Si el programador desea ser explícito, la clase DateTime cuenta con el método static para especificar el tipo de fecha y hora, junto con la especificación del valor de la enumeración DateTimeKind; así: 

DateTime dt = new DateTime(2012, 11, 11); // Unspecified.
DateTime utc = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
Console.WriteLine("`dt` = {0}", dt);
Console.WriteLine("`utc` = {0}", utc);

El resultado de la ejecución es 

`dt` = 11/11/2012 12:00:00 AM
`utc` = 11/11/2012 12:00:00 AM

4. DateTimeOffset y Zonas Horarias

Una instancia de DateTimeOffset ("DateTimeOffset structure", 2016) representa internamente sus valores de la siguiente manera (Albahari, 2012)
  • Campo de tipo DateTime con valor de propiedad Kind en DateTimeKind.Utc.
  • Un campo como valor entero de 16 bits para representar el offset UTC en minutos.
Para operaciones de comparación sólo se usa el campo DateTime, mientras que el campo offset se usa principalmente para efectos de formato.

Por otro lado, los métodos de conversión ToLocalTime() ("DateTimeOffset.ToLocalTime Method", 2016) y ToUniversalTime() ("DateTimeOffset.ToUniversalTime Method", 2016) retornan una instancia DateTimeOffset con valores de fecha y hora iguales, pero con un valor offset con el tipo correspondiente -UTC o local-. Con un ejemplo de código fuente puede quedar este concepto aún más claro: 

DateTimeOffset local = DateTimeOffset.Now;
DateTimeOffset utc = local.ToUniversalTime();

Console.WriteLine(local.Offset);
Console.WriteLine(utc.Offset);

Los valores resultantes son 

-05:00:00
00:00:00

El valor offset corresponde con Bogotá D.C. (Colombia) respecto a UTC. Para este caso sólo ha cambiado el offset de la fecha y hora porque 

Console.WriteLine(local == utc);

retorna true; mientras que 

Console.WriteLine(local.EqualsExact(utc));

retorna false.

5. Clases TimeZone y TimeZoneInfo

Las clases TimeZone y TimeZoneInfo forman parte de .NET Framework, y proveen funcionalidad para obtener información acerca de zonas horarias, offsets de UTC, y horario de verano. En el caso de la segunda -TimeZoneInfo-, fue introducida en la versión 3.5 de .NET y cuenta con una funcionalidad más poderosa que TimeZone.

En las siguientes subsecciones se describe cada tipo, sin embargo vale generalizar que la diferencia más sobresaliente entre estas dos clases reside en que TimeZone sólo permite obtener información de la zona horaria local, mientras que TimeZoneInfo provee acceso a todas las zonas horarias del mundo.

5.1 Clase TimeZone

La clase TimeZone ("TimeZone Class", 2016) representa una zona horaria (una zona horaria es una región geográfica en la cual el mismo estándar de tiempo es usado; por ejemplo, Bogotá, Lima, Quito, Rio Branco se hallan en la misma zona horaria ("Colombia Time Zone", 2016)).
Zona Horaria Bogotá
Figura 1. Zona Horaria Bogotá ("Colombia Time Zone", 2016).
Esta clase cuenta con la propiedad static CurrentTimeZone para obtener la zona horaria del sistema actual: 

TimeZone zonaHoraria = TimeZone.CurrentTimeZone;
Console.WriteLine(zonaHoraria.StandardName);
Console.WriteLine(zonaHoraria.DaylightName);

La salida de este código es: 

SA Pacific Standard Time
SA Pacific Daylight Time

Estas salidas corresponden con el nombre de presentación para esta zona horaria (Bogotá).

Otra funcionalidad interesante es la que facilitan los métodos IsDaylightSavingTime() y GetUtcOffsest()

TimeZone zonaHoraria = TimeZone.CurrentTimeZone;
DateTime dt = new DateTime(2008, 1, 1);
Console.WriteLine(zonaHoraria.IsDaylightSavingTime(dt));
Console.WriteLine(zonaHoraria.GetUtcOffset(dt));

Lo cual da como resultado: 

False
-05:00:00

5.2 Clase TimeZoneInfo

Mientras que la clase TimeZone sólo provee información acerca de la zona horaria local, TimeZoneInfo ("TimeZoneInfo Class", 2016) es capaz de representar cualquier zona horaria del planeta.

De acuerdo con MSDN, TimeZoneInfo ofrece un sinnúmero de mejoras respecto a la clase TimeZone, entre ellas: 
  • Representación de diferentes zonas horarias
  • Conversión entre zona horaria local y UTC
  • Conversión entre diferentes zonas horarias
Los miembros que pertenecen a esta clase permiten al programador de .NET realizar estos tipos de operaciones ("TimeZoneInfo Class", 2016)
  • Obtener la zona horaria del sistema operativo
  • Enumerar cada una de las zonas horarias configuradas en el sistema
  • Convertir entre las diferentes zonas horarias
  • Crear una nueva zona horaria no definida en el sistema operativo
  • Serializar una zona horaria para uso posterior
TimeZoneInfo zonaHoraria = TimeZoneInfo.Local;
Console.WriteLine(zonaHoraria.StandardName);

Estas líneas de código C# producen como resultado: 

SA Pacific Standard Time

TimeZoneInfo también cuenta con los métodos IsDaylightTimeSaving() y GetUtcOffset, aunque se diferencia en los parámetros que acepta: esta clase acepta ya sea un objeto DateTime o DateTimeOffset como argumento.

Continuando, el método static FindSystemTimeZoneById obtiene un objeto TimeZoneInfo con la información de la zona horaria especificada como clave: 

TimeZoneInfo zonaHorariaAustralia =
TimeZoneInfo.FindSystemTimeZoneById("W. Australia Standard Time");
Console.WriteLine(zonaHorariaAustralia.Id);
Console.WriteLine(zonaHorariaAustralia.DisplayName);
Console.WriteLine(zonaHorariaAustralia.BaseUtcOffset);
Console.WriteLine(zonaHorariaAustralia.SupportsDaylightSavingTime);

Una vez ejecutadas estas líneas de código se obtiene: 

W. Australia Standard Time
(UTC+08:00) Perth
08:00:00
True

Antes de cerrar esta subsección, vale mencionar otro de los métodos de bastante utilidad: TimeZoneInfo.GetSystemTimeZones() ("TimeZoneInfo.GetSystemTimeZones Method ()", 2016)

var zonasHorarias = from zonaHoraria in TimeZoneInfo.GetSystemTimeZones()
select (zonaHoraria);

zonasHorarias.Dump("Zonas Horarias");

Con esta sentencia LINQ (compuesta en LINQPad) se genera este resultado (versión reducida): 
Fechas y Zonas Horarias
Figura 2. Fechas y Zonas Horarias con TimeZoneInfo.GetSystemTimeZones().

7. Conclusiones

En este artículo C# el programador ha comprendido los conceptos fundamentales para el trabajo con fechas y zonas horarias. Este tipo de conocimiento es de enorme utilidad cuando se está diseñando una aplicación con ámbito internacional, por ejemplo.

El próximo artículo C# finaliza esta serie demostrando las capacidades en el manejo de horario de verano.

8. Literatura & Enlaces

Albahari, J., Albahari, B. (2012). C# 5.0 in a Nutshell. United States: O'Reilly Media.
DateTime Structure (System) (2016, junio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetime.aspx
DateTimeKind Enumeration (System) (2016, junio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/shx7s921.aspx?f=255&MSPPError=-2147217396
DateTime.ToUniversalTime Method (System) (2016, junio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetime.touniversaltime%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
DateTime.ToLocalTime Method (System) (2016, junio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
DateTimeOffset Structure (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetimeoffset(v=vs.110).aspx
DateTimeOffset.ToLocalTime Method (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetimeoffset.tolocaltime.aspx
DateTimeOffset.ToUniversalTime Method (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.datetimeoffset.touniversaltime.aspx
TimeZone Class (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.timezone%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
TimeZone.CurrentTimeZone Property (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.timezone.currenttimezone%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
TimeZoneInfo Class (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.timezoneinfo(v=vs.110).aspx
TimeZoneInfo.GetSystemTimeZones Method (System) (2016, julio 14). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.timezoneinfo.getsystemtimezones%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396


O

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.