Cómo: Utilizar zonas horarias en aritmética de fecha y hora
Actualización: noviembre 2007
Normalmente, cuando se realizan operaciones aritméticas de fecha y hora mediante valores DateTime o DateTimeOffset, el resultado no refleja las reglas de ajuste de la zona horaria. Esto es así incluso cuando la zona horaria del valor de fecha y hora es claramente identificable (por ejemplo, cuando la propiedad Kind está establecida en Local). En este tema se muestra cómo realizar operaciones aritméticas en valores de fecha y hora que pertenecen a una zona horaria determinada. Los resultados de las operaciones aritméticas reflejarán las reglas de ajuste de la zona horaria.
Para aplicar reglas de ajuste a operaciones aritméticas de fecha y hora
Implemente algún método para relacionar un valor de fecha y hora con la zona horaria a la que pertenece. Por ejemplo, declare una estructura que incluya los valores de fecha y hora y su zona horaria. En el ejemplo siguiente se utiliza este enfoque para vincular un valor de DateTime con su zona horaria.
' Define a structure for DateTime values for internal use only Friend Structure TimeWithTimeZone Dim TimeZone As TimeZoneInfo Dim Time As Date End Structure
// Define a structure for DateTime values for internal use only internal struct TimeWithTimeZone { TimeZoneInfo TimeZone; DateTime Time; }
Convierta una hora en hora UTC mediante una llamada a los métodos ConvertTimeToUtc o ConvertTime.
Realice la operación aritmética en la hora UTC.
Convierta la hora UTC a la zona horaria asociada a la hora original mediante una llamada al método TimeZoneInfo.ConvertTime(DateTime, TimeZoneInfo).
Ejemplo
En el ejemplo siguiente se agregan dos horas y treinta minutos al 9 de marzo de 2008, a la 1:30 a.m. horario central estándar. El cambio a horario de verano se produce treinta minutos después, a las 2:00 a.m. del 9 de marzo de 2008. Debido a que en el ejemplo se siguen los cuatro pasos indicados en la sección anterior, se notifica correctamente la hora resultante como 5:00 a.m. del 9 de marzo de 2008.
Public Structure TimeZoneTime
Public TimeZone As TimeZoneInfo
Public Time As Date
Public Sub New(tz As TimeZoneInfo, time As Date)
If tz Is Nothing Then _
Throw New ArgumentNullException("The time zone cannot be a null reference.")
Me.TimeZone = tz
Me.Time = time
End Sub
Public Function AddTime(interval As TimeSpan) As TimeZoneTime
' Convert time to UTC
Dim utcTime As DateTime = TimeZoneInfo.ConvertTimeToUtc(Me.Time, _
Me.TimeZone)
' Add time interval to time
utcTime = utcTime.Add(interval)
' Convert time back to time in time zone
Return New TimeZoneTime(Me.TimeZone, TimeZoneInfo.ConvertTime(utcTime, _
TimeZoneInfo.Utc, Me.TimeZone))
End Function
End Structure
Module TimeArithmetic
Public Const tzName As String = "Central Standard Time"
Public Sub Main()
Try
Dim cstTime1, cstTime2 As TimeZoneTime
Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tzName)
Dim time1 As Date = #03/09/2008 1:30AM#
Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)
cstTime1 = New TimeZoneTime(cst, time1)
cstTime2 = cstTime1.AddTime(twoAndAHalfHours)
Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time, _
twoAndAHalfHours.ToString(), _
cstTime2.Time)
Catch
Console.WriteLine("Unable to find {0}.", tzName)
End Try
End Sub
End Module
using System;
public struct TimeZoneTime
{
public TimeZoneInfo TimeZone;
public DateTime Time;
public TimeZoneTime(TimeZoneInfo tz, DateTime time)
{
if (tz == null)
throw new ArgumentNullException("The time zone cannot be a null reference.");
this.TimeZone = tz;
this.Time = time;
}
public TimeZoneTime AddTime(TimeSpan interval)
{
// Convert time to UTC
DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(this.Time, this.TimeZone);
// Add time interval to time
utcTime = utcTime.Add(interval);
// Convert time back to time in time zone
return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime,
TimeZoneInfo.Utc, this.TimeZone));
}
}
public class TimeArithmetic
{
public const string tzName = "Central Standard Time";
public static void Main()
{
try
{
TimeZoneTime cstTime1, cstTime2;
TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
DateTime time1 = new DateTime(2008, 3, 9, 1, 30, 0);
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);
cstTime1 = new TimeZoneTime(cst, time1);
cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time,
twoAndAHalfHours.ToString(),
cstTime2.Time);
}
catch
{
Console.WriteLine("Unable to find {0}.", tzName);
}
}
}
Los valores de DateTime y DateTimeOffset están disociados de cualquier zona horaria a la que pudieran pertenecer. Para realizar operaciones aritméticas de fecha y hora de manera que apliquen automáticamente reglas de ajuste de una zona horaria, la zona horaria a la que pertenece cualquier valor de fecha y hora debe ser identificable de forma inmediata. Esto significa que se debe relacionar estrechamente un valor de fecha y hora con su zona horaria asociada. Hay varias maneras de hacerlo, entre ellas:
Suponer que todas las horas utilizadas en una aplicación pertenecen a una zona horaria determinada. Aunque es adecuado en algunos casos, este enfoque es poco flexible y es posible que limite la portabilidad.
Definir un tipo que relacione estrechamente un valor de fecha y hora con su zona horaria asociada, incluyendo ambos como campos del tipo. Este es el enfoque utilizado en el código de ejemplo, que define una estructura para almacenar el valor de fecha y hora y la zona horaria en dos campos de miembro.
En el ejemplo se muestra cómo realizar operaciones aritméticas en valores de DateTime para aplicar las reglas de ajuste de la zona horaria al resultado. Sin embargo, los valores de DateTimeOffset se pueden usar fácilmente. En el ejemplo siguiente se muestra cómo el código del ejemplo original puede adaptarse para utilizar DateTimeOffset en lugar de valores de DateTime.
Public Structure TimeZoneTime
Public TimeZone As TimeZoneInfo
Public Time As DateTimeOffset
Public Sub New(tz As TimeZoneInfo, time As DateTimeOffset)
If tz Is Nothing Then _
Throw New ArgumentNullException("The time zone cannot be a null reference.")
Me.TimeZone = tz
Me.Time = time
End Sub
Public Function AddTime(interval As TimeSpan) As TimeZoneTime
' Convert time to UTC
Dim utcTime As DateTimeOffset = TimeZoneInfo.ConvertTime(Me.Time, TimeZoneInfo.Utc)
' Add time interval to time
utcTime = utcTime.Add(interval)
' Convert time back to time in time zone
Return New TimeZoneTime(Me.TimeZone, TimeZoneInfo.ConvertTime(utcTime, Me.TimeZone))
End Function
End Structure
Module TimeArithmetic
Public Const tzName As String = "Central Standard Time"
Public Sub Main()
Try
Dim cstTime1, cstTime2 As TimeZoneTime
Dim cst As TimeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(tzName)
Dim time1 As Date = #03/09/2008 1:30AM#
Dim twoAndAHalfHours As New TimeSpan(2, 30, 0)
cstTime1 = New TimeZoneTime(cst, _
New DateTimeOffset(time1, cst.GetUtcOffset(time1)))
cstTime2 = cstTime1.AddTime(twoAndAHalfHours)
Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time, _
twoAndAHalfHours.ToString(), _
cstTime2.Time)
Catch
Console.WriteLine("Unable to find {0}.", tzName)
End Try
End Sub
End Module
using System;
public struct TimeZoneTime
{
public TimeZoneInfo TimeZone;
public DateTimeOffset Time;
public TimeZoneTime(TimeZoneInfo tz, DateTimeOffset time)
{
if (tz == null)
throw new ArgumentNullException("The time zone cannot be a null reference.");
this.TimeZone = tz;
this.Time = time;
}
public TimeZoneTime AddTime(TimeSpan interval)
{
// Convert time to UTC
DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(this.Time, TimeZoneInfo.Utc);
// Add time interval to time
utcTime = utcTime.Add(interval);
// Convert time back to time in time zone
return new TimeZoneTime(this.TimeZone, TimeZoneInfo.ConvertTime(utcTime, this.TimeZone));
}
}
public class TimeArithmetic
{
public const string tzName = "Central Standard Time";
public static void Main()
{
try
{
TimeZoneTime cstTime1, cstTime2;
TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
DateTime time1 = new DateTime(2008, 3, 9, 1, 30, 0);
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);
cstTime1 = new TimeZoneTime(cst,
new DateTimeOffset(time1, cst.GetUtcOffset(time1)));
cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time,
twoAndAHalfHours.ToString(),
cstTime2.Time);
}
catch
{
Console.WriteLine("Unable to find {0}.", tzName);
}
}
}
Tenga en cuenta que si esta suma se realiza simplemente en el valor de DateTimeOffset sin convertirlo primero a hora UTC, el resultado reflejará el punto en el tiempo correcto pero su diferencia no reflejará la de la zona horaria designada para esa hora.
Compilar el código
Para este ejemplo se necesita:
Que se agregue al proyecto una referencia a System.Core.dll.
Que se importe el espacio de nombres System con la instrucción using (necesaria en código de C#).
Vea también
Conceptos
Efectuar operaciones aritméticas con fechas y horas