Efectuar operaciones aritméticas con fechas y horas

Actualización: noviembre 2007

Aunque tanto las estructuras DateTime como DateTimeOffset proporcionan miembros que efectúan operaciones aritméticas con sus valores, los resultados que arrojan estas operaciones son muy diferentes. En este tema se estudian esas diferencias, se relacionan con los grados de conocimiento de la zona horaria en los datos de fecha y hora, y se explica cómo realizar operaciones con conocimiento pleno de la zona horaria utilizando datos de fecha y hora.

Operaciones comparativas y aritméticas con valores de DateTime

A partir de la versión 2.0 de .NET Framework, los valores de DateTime poseen un grado limitado de conocimiento de la zona horaria. La propiedad DateTime.Kind permite asignar un valor de DateTimeKind a la fecha y la hora para indicar si representa la hora local, la hora universal coordinada (UTC) o la hora de una zona horaria no especificada. Sin embargo, esta información de zona horaria limitada se omite al efectuar comparaciones u operaciones aritméticas de fecha y hora con valores de DateTime. En el ejemplo siguiente, donde se compara la hora local actual con la hora UTC actual, se ilustra este punto.

Public Enum TimeComparison As Integer
   EarlierThan = -1
   TheSameAs = 0
   LaterThan = 1
End Enum

Module DateManipulation
   Public Sub Main()
      Dim localTime As Date = Date.Now
      Dim utcTime As Date = Date.UtcNow

      Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours", _
                        localTime.Kind.ToString(), _
                        utcTime.Kind.ToString(), _
                        (localTime - utcTime).Hours, _
                        (localTime - utcTime).Minutes)
      Console.WriteLine("The {0} time is {1} the {2} time.", _
                        localTime.Kind.ToString(), _ 
                        [Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)), _
                        utcTime.Kind.ToString())  
      ' If run in the U.S. Pacific Standard Time zone, the example displays 
      ' the following output to the console:
      '    Difference between Local and Utc time: -7:0 hours
      '    The Local time is EarlierThan the Utc time.                                                    
   End Sub
End Module
using System;

public enum TimeComparison
{
   EarlierThan = -1,
   TheSameAs = 0,
   LaterThan = 1
}

public class DateManipulation
{
   public static void Main()
   {
      DateTime localTime = DateTime.Now;
      DateTime utcTime = DateTime.UtcNow;

      Console.WriteLine("Difference between {0} and {1} time: {2}:{3} hours", 
                        localTime.Kind.ToString(), 
                        utcTime.Kind.ToString(), 
                        (localTime - utcTime).Hours, 
                        (localTime - utcTime).Minutes);
      Console.WriteLine("The {0} time is {1} the {2} time.", 
                        localTime.Kind.ToString(), 
                        Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)), 
                        utcTime.Kind.ToString());  
   }
}
// If run in the U.S. Pacific Standard Time zone, the example displays 
// the following output to the console:
//    Difference between Local and Utc time: -7:0 hours
//    The Local time is EarlierThan the Utc time.                                                    

El método CompareTo(DateTime) indica que la hora local es anterior a (o menor que) la hora UTC y la operación de resta indica que la diferencia entre la hora UTC y la hora local de un sistema situado en la zona horaria estándar del Pacífico (PST) de EE. UU. es de siete horas. Sin embargo, debido a que estos dos valores proporcionan representaciones diferentes de un mismo punto de tiempo, en este caso está claro que este intervalo de tiempo es totalmente atribuible a la diferencia entre la zona horaria local con respecto a la hora UTC.

De modo más general, la propiedad DateTime.Kind no afecta a los resultados devueltos por la comparación DateTime ni a los métodos aritméticos (tal como lo indica la comparación de dos puntos de tiempo idénticos), aunque puede afectar a la interpretación de esos resultados. Por ejemplo:

  • El resultado de cualquier operación aritmética efectuada con dos valores de fecha y hora cuyas propiedades DateTime.Kind sean iguales a Utc en ambos casos, refleja el intervalo de tiempo real entre los dos valores. De igual forma, la comparación de dos valores de fecha y hora de este tipo refleja con precisión la relación entre ambas horas.

  • El resultado de cualquier operación aritmética o comparativa realizada con dos valores de fecha y hora cuyas propiedades DateTime.Kind sean Local en ambos casos o con dos valores de fecha y hora que tengan valores diferentes de la propiedad DateTime.Kind refleja la diferencia de la hora que marca el reloj entre los dos valores.

  • Las operaciones aritméticas o comparativas con valores de fecha y hora locales no tienen en cuenta si un valor concreto es ambiguo o no válido, ni tampoco el efecto de ninguna regla de ajuste resultante de la transición de la zona horaria local al horario de verano o de invierno.

  • Cualquier operación que compara o calcula la diferencia entre la hora UTC y una hora local incluye en el resultado un intervalo de tiempo igual a la diferencia de la zona horaria local con respecto a la hora UTC.

  • Cualquier operación que compare o calcule la diferencia entre una hora no especificada y la hora UTC o la hora local refleja simplemente la hora del reloj. Las diferencias de zonas horarias no se tienen en cuenta y el resultado no refleja la aplicación de reglas de ajuste de la zona horaria.

  • Cualquier operación que compare o calcule la diferencia entre dos horas no especificadas puede incluir un intervalo desconocido que refleje las diferencias horarias en dos zonas horarias diferentes.

Hay muchos escenarios en los que las diferencias de zona horaria no afectan a los cálculos de fecha y hora (para obtener una explicación de algunos de ellos, consulte Elegir entre DateTime, DateTimeOffset y TimeZoneInfo) o en los que el contexto de los datos de fecha y hora define el significado de las operaciones comparativas o aritméticas.

Operaciones comparativas y aritméticas con valores de DateTimeOffset

Un valor de DateTimeOffset incluye no sólo una fecha y hora, sino también una diferencia que define sin ambigüedad esa fecha y hora con respecto a la hora UTC. Esto permite definir la igualdad de un modo diferente que para los valores de DateTime. Mientras que los valores de DateTime son iguales si tienen el mismo valor de fecha y hora, los valores de DateTimeOffset son iguales si ambos hacen referencia al mismo punto temporal. Esto hace que un valor de DateTimeOffset sea más preciso y su interpretación menos necesaria cuando se utiliza en comparaciones y en la mayoría de las operaciones aritméticas que determinan el intervalo entre dos fechas y horas. En el ejemplo siguiente, que es el equivalente de DateTimeOffset al ejemplo anterior en el que se comparaban los valores de DateTime local y UTC, se muestra esta diferencia de comportamiento.

Public Enum TimeComparison As Integer
   EarlierThan = -1
   TheSameAs = 0
   LaterThan = 1
End Enum

Module DateTimeOffsetManipulation
   Public Sub Main()
      Dim localTime As DateTimeOffset = DateTimeOffset.Now
      Dim utcTime As DateTimeOffset = DateTimeOffset.UtcNow

      Console.WriteLine("Difference between local time and UTC: {0}:{0:D2} hours.", _
                        (localTime - utcTime).Hours, _
                        (localTime - utcTime).Minutes)
      Console.WriteLine("The local time is {0} UTC.", _
                        [Enum].GetName(GetType(TimeComparison), localTime.CompareTo(utcTime)))  
   End Sub
End Module
' Regardless of the local time zone, the example displays 
' the following output to the console:
'    Difference between local time and UTC: 0:00 hours.
'    The local time is TheSameAs UTC.
'          Console.WriteLine(e.GetType().Name)
using System;

public enum TimeComparison
{
   EarlierThan = -1,
   TheSameAs = 0,
   LaterThan = 1
}

public class DateTimeOffsetManipulation
{
   public static void Main()
   {
      DateTimeOffset localTime = DateTimeOffset.Now;
      DateTimeOffset utcTime = DateTimeOffset.UtcNow;

      Console.WriteLine("Difference between local time and UTC: {0}:{1:D2} hours", 
                        (localTime - utcTime).Hours, 
                        (localTime - utcTime).Minutes);
      Console.WriteLine("The local time is {0} UTC.", 
                        Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)));  
   }
}
// Regardless of the local time zone, the example displays 
// the following output to the console:
//    Difference between local time and UTC: 0:00 hours.
//    The local time is TheSameAs UTC.

En este ejemplo, el método CompareTo indica que la hora local y la hora UTC actuales son iguales, y la resta de los valores de DateTimeOffset indica que la diferencia entre las dos horas es TimeSpan.Zero.

La limitación principal de utilizar valores de DateTimeOffset en las operaciones aritméticas con valores de fecha y hora reside en que, si bien los valores de DateTimeOffset tienen algún conocimiento de la zona horaria, no tienen conocimiento pleno de ella. Aunque la diferencia del valor de DateTimeOffset refleja la diferencia de una zona horaria con respecto a la hora UTC cuando se asigna un valor por primera vez a una variable DateTimeOffset, en lo sucesivo deja de estar asociada a la zona horaria. Al no seguir estando directamente asociada con una hora identificable, la adición y resta de intervalos de fecha y hora no tiene en cuenta las reglas de ajuste de la zona horaria.

Para ilustrarlo, la transición al horario de verano en la zona horaria estándar central (CST) de EE. UU. se produce a las 2:00 a.m. del 9 de marzo de 2008. En consecuencia, al sumar un intervalo de dos horas y media a la hora CST 1:30 a.m. del 9 de marzo de 2008, la fecha y hora resultantes deberían ser el 9 de marzo de 2008 a las 5:00 a.m. Sin embargo, como se muestra en ejemplo siguiente, el resultado de la suma es el 9 de marzo de 2008 a las 4:00 a.m. Tenga en cuenta que este último resultado de la operación sí representa el punto de tiempo correcto, aunque no sea la hora correcta en la zona horaria que nos interesa (es decir, no tiene la diferencia esperada de la zona horaria).

Module IntervalArithmetic
   Public Sub Main()
      Dim generalTime As Date = #03/09/2008 1:30AM#
      Const tzName As String = "Central Standard Time"
      Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)

      ' Instantiate DateTimeOffset value to have correct CST offset
      Try
         Dim centralTime1 As New DateTimeOffset(generalTime, _
                    TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime))

         ' Add two and a half hours      
         Dim centralTime2 As DateTimeOffset = centralTime1.Add(twoAndAHalfHours)
         ' Display result
         Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
                                                    twoAndAHalfHours.ToString(), _
                                                    centralTime2)   
      Catch e As TimeZoneNotFoundException
         Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
      End Try
   End Sub
End Module
' The example displays the following output to the console:
'    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00
using System;

public class IntervalArithmetic
{
   public static void Main()
   {
      DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
      const string tzName = "Central Standard Time";
      TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

      // Instantiate DateTimeOffset value to have correct CST offset
      try
      {
         DateTimeOffset centralTime1 = new DateTimeOffset(generalTime, 
                    TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime));

         // Add two and a half hours      
         DateTimeOffset centralTime2 = centralTime1.Add(twoAndAHalfHours);
         // Display result
         Console.WriteLine("{0} + {1} hours = {2}", centralTime1, 
                                                    twoAndAHalfHours.ToString(), 
                                                    centralTime2);  
      }
      catch (TimeZoneNotFoundException)
      {
         Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
      }
   }
}
// The example displays the following output to the console:
//    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM -06:00

Operaciones aritméticas con horas en zonas horarias

La clase TimeZoneInfo incluye varios métodos de conversión que aplican ajustes automáticamente cuando convierten las horas de una zona horaria en otra. Se incluyen las siguientes:

Para obtener información detallada, consulte Convertir horas entre zonas horarias.

La clase TimeZoneInfo no proporciona ningún método que aplique automáticamente reglas de ajuste cuando se realizan operaciones aritméticas con valores de fecha y hora. Sin embargo, para hacerlo basta con convertir la hora de una zona horaria en la hora UTC, realizar la operación aritmética y, a continuación, volver a convertir la hora UTC en la hora correspondiente de la zona horaria. Para obtener información detallada, consulte Cómo: Utilizar zonas horarias en aritmética de fecha y hora.

Por ejemplo, el código siguiente es parecido al código anterior en el que se sumaban dos horas y media a las 2:00 a.m. del 9 de marzo de 2008. Sin embargo, como convierte la hora CST en hora UTC antes de realizar la operación aritmética y, a continuación, vuelve a convertir el resultado de la hora UTC en la hora CST, la hora resultante sí refleja la transición de la zona horaria CST al horario de verano.

Module TimeZoneAwareArithmetic
   Public Sub Main()
      Const tzName As String = "Central Standard Time"

      Dim generalTime As Date = #03/09/2008 1:30AM#
      Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tzName) 
      Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)

      ' Instantiate DateTimeOffset value to have correct CST offset
      Try
         Dim centralTime1 As New DateTimeOffset(generalTime, _
                    cst.GetUtcOffset(generalTime))

         ' Add two and a half hours 
         Dim utcTime As DateTimeOffset = centralTime1.ToUniversalTime()
         utcTime += twoAndAHalfHours

         Dim centralTime2 As DateTimeOffset = TimeZoneInfo.ConvertTime(utcTime, cst)
         ' Display result
         Console.WriteLine("{0} + {1} hours = {2}", centralTime1, _
                                                    twoAndAHalfHours.ToString(), _
                                                    centralTime2)   
      Catch e As TimeZoneNotFoundException
         Console.WriteLine("Unable to retrieve Central Standard Time zone information.")
      End Try
   End Sub
End Module
' The example displays the following output to the console:
'    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00
using System;

public class TimeZoneAwareArithmetic
{
   public static void Main()
   {
      const string tzName = "Central Standard Time";

      DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
      TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
      TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

      // Instantiate DateTimeOffset value to have correct CST offset
      try
      {
         DateTimeOffset centralTime1 = new DateTimeOffset(generalTime, 
                                       cst.GetUtcOffset(generalTime));

         // Add two and a half hours
         DateTimeOffset utcTime = centralTime1.ToUniversalTime();
         utcTime += twoAndAHalfHours;

         DateTimeOffset centralTime2 = TimeZoneInfo.ConvertTime(utcTime, cst);
         // Display result
         Console.WriteLine("{0} + {1} hours = {2}", centralTime1, 
                                                    twoAndAHalfHours.ToString(), 
                                                    centralTime2);  
      }
      catch (TimeZoneNotFoundException)
      {
         Console.WriteLine("Unable to retrieve Central Standard Time zone information.");
      }
   }
}
// The example displays the following output to the console:
//    3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM -05:00

Vea también

Tareas

Cómo: Utilizar zonas horarias en aritmética de fecha y hora

Otros recursos

Horas y zonas horarias