Compartir vía


Elija entre DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly y TimeZoneInfo

Las aplicaciones .NET pueden usar información de fecha y hora de varias maneras. Entre los usos más comunes de la información de fecha y hora se incluyen:

  • Para reflejar solo una fecha, por lo que la información de hora no es importante.
  • Para reflejar solo una hora, por lo que la información de fecha no es importante.
  • Para reflejar una fecha y hora abstractas que no están vinculadas a una hora y un lugar específicos (por ejemplo, la mayoría de las tiendas de una cadena internacional abren los días laborables a las 9:00 A.M.).
  • Para recuperar información de fecha y hora de orígenes fuera de .NET, normalmente donde la información de fecha y hora se almacena en un tipo de datos simple.
  • Para identificar de forma única e inequívoca un único punto en el tiempo. Algunas aplicaciones requieren que una fecha y hora sean inequívocas solo en el sistema host. Otras aplicaciones requieren que sea inequívoca en todos los sistemas (es decir, una fecha serializada en un sistema puede deserializarse y usarse significativamente en otro sistema en cualquier parte del mundo).
  • Para conservar múltiples momentos relacionados (como la hora local del usuario y el momento de recepción por parte del servidor para una solicitud web).
  • Para realizar la aritmética de fecha y hora, posiblemente con un resultado que identifica de forma única e inequívoca un único punto en el tiempo.

.NET incluye los tipos DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlyy TimeZoneInfo, todos los cuales se pueden usar para compilar aplicaciones que funcionan con fechas y horas.

Nota

En este artículo no se describe TimeZone porque su funcionalidad se incorpora casi completamente en la clase TimeZoneInfo. Siempre que sea posible, use la clase TimeZoneInfo en lugar de la clase TimeZone.

DateTimeOffset (estructura)

La estructura DateTimeOffset representa un valor de fecha y hora, junto con un desplazamiento que indica cuánto ese valor difiere de UTC. Por lo tanto, el valor siempre identifica de forma inequívoca un único punto en el tiempo.

El tipo DateTimeOffset incluye toda la funcionalidad del tipo DateTime junto con el reconocimiento de zona horaria. Esto hace que sea adecuado para las aplicaciones que:

  • Identificar de forma única e inequívoca un único punto en el tiempo. El tipo de DateTimeOffset se puede usar para definir de forma inequívoca el significado de "now", para registrar los tiempos de transacción, registrar las horas de eventos del sistema o de la aplicación, y para registrar los tiempos de creación y modificación de archivos.
  • Realice la aritmética de fecha y hora general.
  • Conservar varios tiempos relacionados, siempre que esos tiempos se almacenen como dos valores independientes o como dos miembros de una estructura.

Nota

Estos usos para los valores de DateTimeOffset son mucho más comunes que los valores de DateTime. Como resultado, considere DateTimeOffset como el tipo de fecha y hora predeterminados para el desarrollo de aplicaciones.

Un DateTimeOffset valor no está asociado a una zona horaria determinada, pero puede originarse en una variedad de zonas horarias. En el ejemplo siguiente se enumeran las zonas horarias a las que puede pertenecer un número de valores de DateTimeOffset (incluida una hora estándar del Pacífico local).

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
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

La salida muestra que cada valor de fecha y hora de este ejemplo puede pertenecer a al menos 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 con UTC no se corresponde necesariamente con la diferencia horaria con UTC base de la zona horaria de origen ni con la diferencia horaria con UTC que se encuentra en su nombre para mostrar. Dado que un solo valor de DateTimeOffset no está estrechamente unido a su zona horaria, no puede reflejar la transición de una zona horaria a y desde el horario de verano. Esto puede ser problemático cuando se usa la aritmética de fecha y hora para manipular un valor de DateTimeOffset. Para obtener una explicación sobre cómo realizar la aritmética de fecha y hora de una manera que tenga en cuenta las reglas de ajuste de una zona horaria, consulte Realización de operaciones aritméticas con fechas y horas.

Estructura DateTime

Un valor de DateTime define una fecha y hora determinada. 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 de DateTime representa la hora local (DateTimeKind.Local), hora universal coordinada (UTC) (DateTimeKind.Utc) o una hora no especificada (DateTimeKind.Unspecified).

La estructura DateTime es adecuada para aplicaciones con una o varias de las siguientes características:

  • Trabaje con fechas y horas abstractas.
  • Trabaje con fechas y horas para las que falta información de zona horaria.
  • Trabaje solo con fechas y horas UTC.
  • Realizar operaciones aritméticas de fecha y hora, pero con interés por los resultados generales. Por ejemplo, en una operación de adición que agrega seis meses a una fecha y hora determinada, a menudo no es importante si el resultado se ajusta para el horario de verano.

A menos que un valor de DateTime determinado represente UTC, ese valor de fecha y hora suele ser ambiguo o limitado en su portabilidad. Por ejemplo, si un valor de DateTime representa la hora local, es portátil dentro de esa zona horaria local (es decir, si el valor se deserializa en otro sistema de la misma zona horaria, ese valor sigue identificando de forma inequívoca un único punto en el tiempo). Fuera de la zona horaria local, ese DateTime valor puede tener varias interpretaciones. Si el valor de la propiedad Kind es DateTimeKind.Unspecified, es aún menos portátil: ahora resulta ambiguo incluso dentro de la misma zona horaria y posiblemente también en el mismo sistema donde se serializó inicialmente. Solo si un valor de DateTime representa UTC hace que el valor identifique de forma inequívoca un único punto en el tiempo, independientemente del sistema o de la zona horaria en la que se use el valor.

Importante

Al guardar o compartir datos DateTime, use UTC y establezca la propiedad Kind del valor de DateTime en DateTimeKind.Utc.

Estructura DateOnly

La estructura DateOnly representa una fecha específica, sin hora. Puesto que no tiene ningún componente de hora, representa una fecha desde el inicio del día hasta el final del día. Esta estructura es ideal para almacenar fechas específicas, como una fecha de nacimiento, una fecha de aniversario, un día festivo o una fecha relacionada con la empresa.

Aunque puede usar DateTime al omitir el componente de hora, hay algunas ventajas para usar DateOnly sobre DateTime:

  • La estructura DateTime puede pasar al día anterior o al día siguiente si se desplaza por una zona horaria. DateOnly no se puede desplazar por una zona horaria y siempre representa la fecha establecida.
  • La serialización de una estructura DateTime incluye el componente de hora, que puede ocultar la intención de los datos. Además, DateOnly serializa menos datos.
  • Cuando el código interactúa con una base de datos, como SQL Server, las fechas completas se almacenan generalmente como el tipo de datos date, que no incluye una hora. DateOnly coincide mejor con el tipo de base de datos.

Para obtener más información sobre DateOnly, vea Cómo usar las estructuras DateOnly y TimeOnly.

Importante

DateOnly no está disponible para .NET Framework.

Estructura TimeSpan

La estructura TimeSpan representa un intervalo de tiempo. Sus dos usos típicos son:

  • Reflejar el intervalo de tiempo entre dos valores de fecha y hora. Por ejemplo, restar un valor DateTime de otro devuelve un valor TimeSpan.
  • Medir el tiempo transcurrido. Por ejemplo, la propiedad Stopwatch.Elapsed devuelve un valor TimeSpan que refleja el intervalo de tiempo transcurrido desde la llamada a uno de los métodos Stopwatch que comienzan a medir el tiempo transcurrido.

Un valor de TimeSpan también se puede usar como reemplazo de un valor de DateTime cuando ese valor refleja una hora sin referencia a un día determinado. Este uso es similar a las propiedades DateTime.TimeOfDay y DateTimeOffset.TimeOfDay, que devuelven un valor de TimeSpan que representa la hora sin referencia a una fecha. Por ejemplo, la estructura TimeSpan se puede usar para reflejar la hora diaria de apertura o cierre de un almacén, o bien se puede usar para representar la hora en la que se produce cualquier evento normal.

En el ejemplo siguiente se define una estructura StoreInfo que incluye TimeSpan objetos para las horas de apertura y cierre del almacén, así como un objeto TimeZoneInfo que representa la zona horaria del almacén. La estructura también incluye dos métodos, IsOpenNow y IsOpenAt, que indica si el almacén está abierto en un momento especificado por el usuario, que se supone que está en la zona horaria local.

using System;

public struct StoreInfo
{
   public String store;
   public TimeZoneInfo tz;
   public TimeSpan open;
   public TimeSpan close;

   public bool IsOpenNow()
   {
      return IsOpenAt(DateTime.Now.TimeOfDay);
   }

   public bool IsOpenAt(TimeSpan time)
   {
      TimeZoneInfo local = TimeZoneInfo.Local;
      TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;

      // Is the store in the same time zone?
      if (tz.Equals(local)) {
         return time >= open & time <= close;
      }
      else {
         TimeSpan delta = TimeSpan.Zero;
         TimeSpan storeDelta = TimeSpan.Zero;

         // Is it daylight saving time in either time zone?
         if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
            delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;

         if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
            storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;

         TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
         return comparisonTime >= open && comparisonTime <= close;
      }
   }
}
Public Structure StoreInfo
    Dim store As String
    Dim tz As TimeZoneInfo
    Dim open As TimeSpan
    Dim close As TimeSpan

    Public Function IsOpenNow() As Boolean
        Return IsOpenAt(Date.Now.TimeOfDay)
    End Function

    Public Function IsOpenAt(time As TimeSpan) As Boolean
        Dim local As TimeZoneInfo = TimeZoneInfo.Local
        Dim offset As TimeSpan = TimeZoneInfo.Local.BaseUtcOffset

        ' Is the store in the same time zone?
        If tz.Equals(local) Then
            Return time >= open AndAlso time <= close
        Else
            Dim delta As TimeSpan = TimeSpan.Zero
            Dim storeDelta As TimeSpan = TimeSpan.Zero

            ' Is it daylight saving time in either time zone?
            If local.IsDaylightSavingTime(Date.Now.Date + time) Then
                delta = local.GetAdjustmentRules(local.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            If tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(Date.Now.Date + time, local, tz))
                storeDelta = tz.GetAdjustmentRules(tz.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            Dim comparisonTime As TimeSpan = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate
            Return (comparisonTime >= open AndAlso comparisonTime <= close)
        End If
    End Function
End Structure

Después, el código de cliente puede usar la estructura StoreInfo como la siguiente.

public class Example
{
   public static void Main()
   {
      // Instantiate a StoreInfo object.
      var store103 = new StoreInfo();
      store103.store = "Store #103";
      store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
      // Store opens at 8:00.
      store103.open = new TimeSpan(8, 0, 0);
      // Store closes at 9:30.
      store103.close = new TimeSpan(21, 30, 0);

      Console.WriteLine("Store is open now at {0}: {1}",
                        DateTime.Now.TimeOfDay, store103.IsOpenNow());
      TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
                           new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0) };
      foreach (var time in times)
         Console.WriteLine("Store is open at {0}: {1}",
                           time, store103.IsOpenAt(time));
   }
}
// The example displays the following output:
//       Store is open now at 15:29:01.6129911: True
//       Store is open at 08:00:00: True
//       Store is open at 21:00:00: True
//       Store is open at 04:59:00: False
//       Store is open at 18:31:00: True
Module Example
    Public Sub Main()
        ' Instantiate a StoreInfo object.
        Dim store103 As New StoreInfo()
        store103.store = "Store #103"
        store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
        ' Store opens at 8:00.
        store103.open = new TimeSpan(8, 0, 0)
        ' Store closes at 9:30.
        store103.close = new TimeSpan(21, 30, 0)

        Console.WriteLine("Store is open now at {0}: {1}",
                          Date.Now.TimeOfDay, store103.IsOpenNow())
        Dim times() As TimeSpan = {New TimeSpan(8, 0, 0),
                                    New TimeSpan(21, 0, 0),
                                    New TimeSpan(4, 59, 0),
                                    New TimeSpan(18, 31, 0)}
        For Each time In times
            Console.WriteLine("Store is open at {0}: {1}",
                              time, store103.IsOpenAt(time))
        Next
    End Sub
End Module
' The example displays the following output:
'       Store is open now at 15:29:01.6129911: True
'       Store is open at 08:00:00: True
'       Store is open at 21:00:00: False
'       Store is open at 04:59:00: False
'       Store is open at 18:31:00: False

Estructura TimeOnly

La estructura TimeOnly representa un valor de marca horaria del día, como un reloj despertador diario o la hora a la que almuerzas cada día. TimeOnly está limitado al intervalo de 00:00:00.0000000 - 23:59:59.9999999, un tiempo específico del día.

Antes de introducir el tipo de TimeOnly, los programadores suelen usar el tipo de DateTime o el tipo de TimeSpan para representar una hora específica. Sin embargo, el uso de estas estructuras para simular una hora sin una fecha puede presentar algunos problemas, que TimeOnly resuelve:

  • TimeSpan representa el tiempo transcurrido, como el tiempo medido con un cronómetro. El rango superior es de más de 29 000 años, y su valor puede ser negativo para indicar que se mueve hacia atrás en el tiempo. Un TimeSpan negativo no indica una hora específica del día.
  • Si TimeSpan se usa como hora del día, existe el riesgo de que se pueda manipular en un valor fuera del día de 24 horas. TimeOnly no tiene este riesgo. Por ejemplo, si el turno de trabajo de un empleado comienza a las 18:00 y dura 8 horas, la adición de 8 horas a la estructura TimeOnly pasa a las 2:00
  • El uso de DateTime para indicar una hora del día requiere asociar una fecha arbitraria a la hora, la cual luego se omita. Es habitual elegir DateTime.MinValue (0001-01-01-01) como fecha, sin embargo, si las horas se restan del valor de DateTime, podría producirse una excepción de OutOfRange. TimeOnly no tiene este problema, ya que el tiempo se avanza y retrocede alrededor del marco de tiempo de 24 horas.
  • La serialización de una estructura DateTime incluye el componente de fecha, que puede ocultar la intención de los datos. Además, TimeOnly serializa menos datos.

Para obtener más información sobre TimeOnly, vea Cómo usar las estructuras DateOnly y TimeOnly.

Importante

TimeOnly no está disponible para .NET Framework.

La clase TimeZoneInfo

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

En algunos casos, aprovechar al máximo la clase TimeZoneInfo puede requerir un mayor trabajo de desarrollo. Si los valores de fecha y hora no están estrechamente acoplados con las zonas horarias a las que pertenecen, se requiere más trabajo. A menos que la 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 de vinculación de esta información es definir una clase o estructura que contenga el valor de fecha y hora y su objeto de zona horaria asociado.

Para aprovechar la compatibilidad con la zona horaria en .NET, debe conocer la zona horaria a la que pertenece un valor de fecha y hora cuando se crea una instancia de ese objeto de fecha y hora. La zona horaria a menudo no se conoce, especialmente en aplicaciones web o de red.

Consulte también