Partilhar via


Como: Usar fusos horários na aritmética de data e hora

Normalmente, quando você executa aritmética de data e hora usando DateTime valores ou DateTimeOffset , o resultado não reflete nenhuma regra de ajuste de fuso horário. Isso é verdadeiro mesmo quando o fuso horário do valor de data e hora é claramente identificável (por exemplo, quando a Kind propriedade é definida como Local). Este tópico mostra como executar operações aritméticas em valores de data e hora que pertencem a um fuso horário específico. Os resultados das operações aritméticas refletirão as regras de ajuste do fuso horário.

Para aplicar regras de ajuste à aritmética de datas e tempos

  1. Implemente algum método para uma ligação próxima de um valor de data e hora com o fuso horário ao qual pertence. Por exemplo, declare uma estrutura que inclua o valor de data e hora e seu fuso horário. O exemplo a seguir usa essa abordagem para vincular um DateTime valor com seu fuso horário.

    // Define a structure for DateTime values for internal use only
    internal struct TimeWithTimeZone
    {
        TimeZoneInfo TimeZone;
        DateTime Time;
    }
    
    ' Define a structure for DateTime values for internal use only
    Friend Structure TimeWithTimeZone
        Dim TimeZone As TimeZoneInfo
        Dim Time As Date
    End Structure
    
  2. Converta uma hora em Tempo Universal Coordenado (UTC) chamando o ConvertTimeToUtc método ou o ConvertTime método.

  3. Execute a operação aritmética na hora UTC.

  4. Converta a hora de UTC para o fuso horário associado à hora original, chamando o método TimeZoneInfo.ConvertTime(DateTime, TimeZoneInfo).

Exemplo

O exemplo a seguir adiciona duas horas e trinta minutos a 9 de março de 2008, às 1h30 (horário padrão central). A transição do fuso horário para o horário de verão ocorre trinta minutos depois, às 2h00 do dia 9 de março de 2008. Como o exemplo segue as quatro etapas listadas na seção anterior, ele informa corretamente o horário resultante como 5:00 da manhã de 9 de março de 2008.

using System;

public struct TimeZoneTime
{
    public TimeZoneInfo TimeZone;
    public DateTime Time;

    public TimeZoneTime(TimeZoneInfo tz, DateTime time)
    {
        ArgumentNullException.ThrowIfNull(tz);

        TimeZone = tz;
        Time = time;
    }

    public TimeZoneTime AddTime(TimeSpan interval)
    {
        // Convert time to UTC
        DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(Time, TimeZone);
        // Add time interval to time
        utcTime = utcTime.Add(interval);
        // Convert time back to time in time zone
        return new TimeZoneTime(TimeZone, TimeZoneInfo.ConvertTime(utcTime,
                                TimeZoneInfo.Utc, 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(2008, 3, 9, 1, 30, 0);
            TimeSpan twoAndAHalfHours = new(2, 30, 0);

            cstTime1 = new TimeZoneTime(cst, time1);
            cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
            Console.WriteLine($"{cstTime1.Time} + {twoAndAHalfHours.ToString()} hours = {cstTime2.Time}");
        }
        catch
        {
            Console.WriteLine($"Unable to find {TzName}.");
        }
    }
}
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

DateTime Ambos e DateTimeOffset valores são dissociados de qualquer fuso horário ao qual possam pertencer. Para executar a aritmética de data e hora de uma forma que aplique automaticamente as regras de ajuste de um fuso horário, o fuso horário ao qual qualquer valor de data e hora pertence deve ser imediatamente identificável. Isso significa que uma data e hora e seu fuso horário associado devem estar firmemente acoplados. Há várias maneiras de fazer isso, que incluem o seguinte:

  • Suponha que todas as horas usadas em um aplicativo pertencem a um fuso horário específico. Embora adequada em alguns casos, esta abordagem oferece flexibilidade limitada e, possivelmente, portabilidade limitada.

  • Defina um tipo que combine firmemente uma data e hora com seu fuso horário associado, incluindo ambos como campos do tipo. Essa abordagem é usada no exemplo de código, que define uma estrutura para armazenar a data, a hora e o fuso horário em dois campos de membro.

O exemplo ilustra como executar operações aritméticas em DateTime valores para que as regras de ajuste de fuso horário sejam aplicadas ao resultado. No entanto, DateTimeOffset os valores podem ser usados com a mesma facilidade. O exemplo a seguir ilustra como o código no exemplo original pode ser adaptado para uso DateTimeOffset em vez de DateTime valores.

using System;

public struct TimeZoneTime2
{
    public TimeZoneInfo TimeZone;
    public DateTimeOffset Time;

    public TimeZoneTime2(TimeZoneInfo tz, DateTimeOffset time)
    {
        ArgumentNullException.ThrowIfNull(tz);

        TimeZone = tz;
        Time = time;
    }

    public TimeZoneTime2 AddTime(TimeSpan interval)
    {
        // Convert time to UTC
        DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(Time, TimeZoneInfo.Utc);
        // Add time interval to time
        utcTime = utcTime.Add(interval);
        // Convert time back to time in time zone
        return new TimeZoneTime2(TimeZone, TimeZoneInfo.ConvertTime(utcTime, TimeZone));
    }
}

public class TimeArithmetic2
{
    public const string tzName = "Central Standard Time";

    public static void Main()
    {
        try
        {
            TimeZoneTime2 cstTime1, cstTime2;

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

            cstTime1 = new TimeZoneTime2(cst,
                           new DateTimeOffset(time1, cst.GetUtcOffset(time1)));
            cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
            Console.WriteLine($"{cstTime1.Time} + {twoAndAHalfHours.ToString()} hours = {cstTime2.Time}");
        }
        catch
        {
            Console.WriteLine($"Unable to find {tzName}.");
        }
    }
}
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

Observe que, se essa adição for simplesmente aplicada ao valor DateTimeOffset sem primeiro convertê-lo para UTC, o resultado refletirá o ponto de tempo correto, mas o seu deslocamento não refletirá o do fuso horário designado para esse momento.

Consulte também