Elegir entre DateTime, DateTimeOffset y TimeZoneInfo

Actualización: noviembre 2007

Las aplicaciones .NET Framework que usan información de fecha y hora son muy diversas y utilizan esa información de varias maneras. Algunos de los usos más comunes de la información de fecha y hora son los siguientes:

  • Reflejar sólo una fecha, por lo que la información de hora no es importante.

  • Reflejar sólo una hora, por lo que la información de fecha no es importante.

  • Reflejar una fecha y hora abstractas que no están vinculadas con una hora o lugar concretos (por ejemplo, la mayoría de los almacenes de una cadena internacional abren los días laborables a las 9:00 a.m.).

  • Recuperar información de fecha y hora de orígenes situados fuera de .NET Framework en los que, normalmente, la información de fecha y hora se almacena con un tipo de datos simple.

  • Identificar de forma exclusiva e inequívoca un único momento dado. Algunas aplicaciones requieren que una fecha y hora sean inequívocas sólo en el sistema host; otras requieren que sean inequívocas en todos los sistemas (es decir, que una fecha serializada en un sistema se pueda deserializar y usar con sentido en otro sistema en cualquier parte del mundo).

  • Conservar varias horas relacionadas (como la hora local del solicitante y la hora del servidor donde se recibe una solicitud web).

  • Realizar operaciones aritméticas de fecha y hora, posiblemente con un resultado que identifique de forma exclusiva e inequívoca un único momento dado.

.NET Framework incluye los tipos DateTime, DateTimeOffset y TimeZoneInfo, todos los cuales se pueden usar para generar aplicaciones que trabajan con fechas y horas.

Nota:

En este tema no se describe un cuarto tipo, TimeZone, porque su funcionalidad está incorporada casi por completo en la clase TimeZoneInfo. Siempre que sea posible, los desarrolladores deben usar la clase TimeZoneInfo en lugar de la clase TimeZone.

Estructura DateTime

Un valor DateTime define una fecha y hora determinadas. A partir de la versión 2.0 de .NET Framework, se incluye una propiedad Kind que proporciona información limitada sobre la zona horaria a la que pertenece esa fecha y hora. El valor DateTimeKind devuelto por la propiedad Kind indica si el valor DateTime representa la hora local (DateTimeKind.Local), la hora universal coordinada (UTC) (DateTimeKind.Utc) o una hora no especificada (DateTimeKind.Unspecified).

La estructura DateTime resulta apropiada para las aplicaciones que hacen lo siguiente:

  • Trabajar sólo con fechas.

  • Trabajar sólo con horas.

  • Trabajar con fechas y horas abstractas.

  • Recuperar información de fecha y hora de orígenes situados fuera de .NET Framework, como bases de datos SQL. Normalmente, estos orígenes almacenan información de fecha y hora con un formato simple que es compatible con la estructura DateTime.

  • Realizar operaciones aritméticas de fecha y hora en las que interesan los resultados generales. Por ejemplo, en una operación que suma seis meses a una fecha y hora determinadas, no suele ser importante si el resultado se ajusta al horario de verano.

A menos que un valor DateTime determinado represente la hora UTC, ese valor de fecha y hora suele ser ambiguo o con una portabilidad limitada. Por ejemplo, si un valor DateTime representa la hora local, se puede mover dentro de esa zona horaria local (es decir, si el valor se deserializa en otro sistema de la misma zona horaria, ese valor identifica inequívocamente un único momento dado). Fuera de la zona horaria local, ese valor DateTime puede tener varias interpretaciones. Si la propiedad Kind del valor es DateTimeKind.Unspecified, es aún menos portátil: es ahora ambiguo dentro de la misma zona horaria y, posiblemente, en el mismo sistema en el que se serializó por primera vez. Sólo si un valor DateTime representa la hora UTC, ese valor identifica inequívocamente un único momento, independientemente del sistema o la zona horaria donde se use el valor.

Nota importante:

Cuando se guardan o se comparten datos DateTime, se debe usar la hora UTC y la propiedad Kind del valor DateTime debe estar establecida en DateTimeKindUTC().

Estructura DateTimeOffset

La estructura DateTimeOffset representa un valor de fecha y hora, junto con una diferencia horaria que indica cuánto difiere ese valor de la hora UTC. Así, el valor identifica siempre de forma inequívoca un único momento.

Aunque los tipos DateTimeOffset disponen de la mayoría de la funcionalidad del tipo DateTime, no se han diseñado para reemplazar el tipo DateTime en el desarrollo de aplicaciones. Por el contrario, es apropiado para las aplicaciones que hacen lo siguiente:

  • Identificar de forma exclusiva e inequívoca un único momento dado. El tipo DateTimeOffset se puede usar para definir inequívocamente el significado de "ahora", registrar la hora de las transacciones, registrar la hora de los eventos de sistema o de aplicación y registrar la hora de creación y modificación de los archivos.

  • Realizar operaciones aritméticas generales de fecha y hora.

  • Conservar varias horas relacionadas, siempre que esas horas se almacenen como dos valores independientes o como dos miembros de una estructura.

Nota:

Estos usos de los valores DateTimeOffset son mucho más comunes que los de los valores DateTime. Por consiguiente, DateTimeOffset debe considerarse el tipo de fecha y hora predeterminado para desarrollar aplicaciones.

Un valor DateTimeOffset no está vinculado con una zona horaria determinada, pero puede originarse en diferentes zonas horarias. Para ilustrar esto, en el ejemplo siguiente se muestran las zonas horarias a las que pueden pertenecer varios valores DateTimeOffset (incluida una zona horaria local estándar del Pacífico).

Imports System.Collections.ObjectModel

Module TimeOffsets
   Public Sub Main()
      Dim thisTime As DateTimeOffset 

      thisTime = New DateTimeOffset(#06/10/2007#, New TimeSpan(-7, 0, 0))
      ShowPossibleTimeZones(thisTime) 

      thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(-6, 0, 0))  
      ShowPossibleTimeZones(thisTime)

      thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(+1, 0, 0))
      ShowPossibleTimeZones(thisTime)
   End Sub

   Private Sub ShowPossibleTimeZones(offsetTime As DateTimeOffset)
      Dim offset As TimeSpan = offsetTime.Offset
      Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo)

      Console.WriteLine("{0} could belong to the following time zones:", _
                        offsetTime.ToString())
      ' Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones()     
      ' Iterate time zones
      For Each timeZone As TimeZoneInfo In timeZones
         ' Compare offset with offset for that date in that time zone
         If timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset) Then
            Console.WriteLine("   {0}", timeZone.DisplayName)
         End If   
      Next
      Console.WriteLine()
   End Sub
End Module
' This example displays the following output to the console:
'       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
'          (GMT-07:00) Arizona
'          (GMT-08:00) Pacific Time (US & Canada)
'          (GMT-08:00) Tijuana, Baja California
'       
'       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
'          (GMT-06:00) Central America
'          (GMT-06:00) Central Time (US & Canada)
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
'          (GMT-06:00) Saskatchewan
'       
'       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
'          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
'          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
'          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
'          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
'          (GMT+01:00) West Central Africa
using System;
using System.Collections.ObjectModel;

public class TimeOffsets
{
   public static void Main()
   {
      DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
      DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
      DateTimeOffset thisTime;

      thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));  
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));
      ShowPossibleTimeZones(thisTime);
   }

   private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)
   {
      TimeSpan offset = offsetTime.Offset;
      ReadOnlyCollection<TimeZoneInfo> timeZones;

      Console.WriteLine("{0} could belong to the following time zones:", 
                        offsetTime.ToString());
      // Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones();     
      // Iterate time zones 
      foreach (TimeZoneInfo timeZone in timeZones)
      {
         // Compare offset with offset for that date in that time zone
         if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
            Console.WriteLine("   {0}", timeZone.DisplayName);
      }
      Console.WriteLine();
   } 
}
// This example displays the following output to the console:
//       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
//          (GMT-07:00) Arizona
//          (GMT-08:00) Pacific Time (US & Canada)
//          (GMT-08:00) Tijuana, Baja California
//       
//       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
//          (GMT-06:00) Central America
//          (GMT-06:00) Central Time (US & Canada)
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
//          (GMT-06:00) Saskatchewan
//       
//       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
//          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
//          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
//          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
//          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
//          (GMT+01:00) West Central Africa

El resultado muestra que cada valor de fecha y hora de este ejemplo puede pertenecer al menos a tres zonas horarias diferentes. El valor DateTimeOffset de 6/10/2007 muestra que si un valor de fecha y hora representa un horario de verano, su diferencia horaria respecto a la hora UTC no tiene que corresponder necesariamente a la diferencia horaria respecto a UTC de la zona horaria de origen o a la diferencia horaria respecto a UTC de su nombre para mostrar. Esto significa que, debido a que un único valor DateTimeOffset no está vinculado con su zona horaria, no puede reflejar el cambio de una zona horaria al horario de verano. Esto puede resultar especialmente problemático cuando se usan operaciones aritméticas de fecha y hora para manipular un valor DateTimeOffset. (Para obtener una descripción de cómo realizar operaciones aritméticas de fecha y hora de manera que se tengan en cuenta las reglas de ajuste de una zona horaria, vea Efectuar operaciones aritméticas con fechas y horas.)

Clase TimeZoneInfo

La clase TimeZoneInfo representa cualquiera de las zonas horarias del mundo y permite la conversión de cualquier fecha y hora de una zona horaria a su equivalente en otra zona horaria. La clase TimeZoneInfo permite trabajar con fechas y horas de manera que cualquier valor de fecha y hora identifique inequívocamente un único momento dado. La clase TimeZoneInfo también es extensible. Aunque depende de la información de zona horaria proporcionada en los sistemas de Windows y definida en el Registro, admite la creación de zonas horarias personalizadas. También permite la serialización y deserialización de la información de zona horaria.

En algunos casos, aprovechar todas las ventajas de la clase TimeZoneInfo puede requerir un trabajo de desarrollo adicional. En primer lugar, los valores de fecha y hora no están vinculados con las zonas horarias a las que pertenecen. Como resultado, a menos que su aplicación proporcione algún mecanismo para vincular una fecha y hora con su zona horaria asociada, es fácil que un valor de fecha y hora determinado se desasocie de su zona horaria. (Un método para vincular esta información es definir una clase o estructura que contenga el valor de fecha y hora y su objeto de zona horaria asociado.) En segundo lugar, Windows XP y las versiones anteriores de Windows no tenían compatibilidad real para información histórica de la zona horaria y Windows Vista sólo tiene compatibilidad limitada. Las aplicaciones diseñadas para administrar fechas y horas históricas deben realizar un uso ampliado de las zonas horarias personalizadas.

Para aprovechar las ventajas de la compatibilidad de zonas horarias de .NET Framework, es necesario conocer la zona horaria a la que pertenece un valor de fecha y hora al crear instancias de ese objeto de fecha y hora. Éste no suele ser el caso, especialmente en aplicaciones web o de red.

Vea también

Otros recursos

Horas y zonas horarias