Partilhar via


Realização de operações aritméticas com datas e horas

Embora tanto as estruturas como as DateTimeDateTimeOffset estruturas forneçam membros que realizam operações aritméticas em seus valores, os resultados das operações aritméticas são muito diferentes. Este artigo examina essas diferenças, relaciona-as com graus de reconhecimento de fuso horário em dados de data e hora e discute como executar operações com reconhecimento de fuso horário usando dados de data e hora.

Comparações e operações aritméticas com valores DateTime

A DateTime.Kind propriedade permite que um DateTimeKind valor seja atribuído à data e hora para indicar se ele representa a hora local, o Tempo Universal Coordenado (UTC) ou a hora em um fuso horário não especificado. No entanto, essas informações limitadas de fuso horário são ignoradas ao comparar ou executar aritmética de data e hora em DateTimeKind valores. O exemplo a seguir, que compara a hora local atual com a hora UTC atual, ilustra como as informações de fuso horário são ignoradas.

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,
                        utcTime.Kind,
                        (localTime - utcTime).Hours,
                        (localTime - utcTime).Minutes);
      Console.WriteLine("The {0} time is {1} the {2} time.",
                        localTime.Kind,
                        Enum.GetName(typeof(TimeComparison), localTime.CompareTo(utcTime)),
                        utcTime.Kind);
   }
}
// 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.
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

O CompareTo(DateTime) método informa que a hora local é anterior (ou menor que) a hora UTC, e a operação de subtração indica que a diferença entre UTC e a hora local para um sistema no fuso horário padrão do Pacífico dos EUA é de sete horas. Mas como esses dois valores fornecem representações diferentes de um único ponto no tempo, é claro neste caso que o intervalo de tempo é completamente atribuível ao deslocamento do fuso horário local do UTC.

Mais geralmente, a DateTime.Kind propriedade não afeta os resultados retornados por Kind comparação e métodos aritméticos (como a comparação de dois pontos idênticos no tempo indica), embora possa afetar a interpretação desses resultados. Por exemplo:

  • O resultado de qualquer operação aritmética realizada em dois valores de data e hora cujas DateTime.Kind propriedades são iguais DateTimeKind reflete o intervalo de tempo real entre os dois valores. Da mesma forma, a comparação de dois desses valores de data e hora reflete com precisão a relação entre os tempos.

  • O resultado de qualquer operação aritmética ou de comparação realizada em dois valores de data e hora cujas DateTime.Kind propriedades são iguais DateTimeKind ou em dois valores de data e hora com valores de propriedade diferentes DateTime.Kind reflete a diferença no tempo de relógio entre os dois valores.

  • As operações aritméticas ou de comparação em valores locais de data e hora não consideram se um determinado valor é ambíguo ou inválido, nem têm em conta o efeito de quaisquer regras de ajustamento resultantes da transição do fuso horário local para ou da hora de verão.

  • Qualquer operação que compare ou calcule a diferença entre UTC e uma hora local inclui um intervalo de tempo igual ao deslocamento do fuso horário local do UTC no resultado.

  • Qualquer operação que compare ou calcule a diferença entre uma hora não especificada e UTC ou a hora local reflete a hora do relógio simples. As diferenças de fuso horário não são consideradas e o resultado não reflete a aplicação de regras de ajuste de fuso horário.

  • Qualquer operação que compare ou calcule a diferença entre duas horas não especificadas pode incluir um intervalo desconhecido que reflita a diferença entre a hora em dois fusos horários diferentes.

Há muitos cenários nos quais as diferenças de fuso horário não afetam os cálculos de data e hora (para uma discussão de alguns desses cenários, consulte Escolhendo entre DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo) ou nos quais o contexto dos dados de data e hora define o significado de comparação ou operações aritméticas.

Comparações e operações aritméticas com valores DateTimeOffset

Um DateTimeOffset valor inclui não apenas uma data e hora, mas também um deslocamento que define inequivocamente essa data e hora em relação ao UTC. Este deslocamento permite definir a igualdade de forma diferente do que para DateTime os valores. Enquanto DateTime os valores são iguais se tiverem o mesmo valor de data e hora, DateTimeOffset os valores são iguais se ambos se referirem ao mesmo ponto no tempo. Quando usado em comparações e na maioria das operações aritméticas que determinam o intervalo entre duas datas e horas, um DateTimeOffset valor é mais preciso e menos precisa de interpretação. O exemplo a seguir, que é o DateTimeOffset equivalente ao exemplo anterior que comparou valores locais e UTC DateTimeOffset , ilustra essa diferença de comportamento.

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.
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}:{1: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)

Neste exemplo, o CompareTo método indica que a hora local atual e a hora UTC atual são iguais, e a subtração de CompareTo(DateTimeOffset) valores indica que a diferença entre as duas vezes é TimeSpan.Zero.

A principal limitação do uso DateTimeOffset de valores na aritmética de data e hora é que, embora DateTimeOffset os valores tenham alguma consciência de fuso horário, eles não são totalmente conscientes do fuso horário. Embora o deslocamento do valor reflita o DateTimeOffset deslocamento de um fuso horário do UTC quando uma variável é atribuída pela primeira vez a um DateTimeOffset valor, ela se torna desassociada do fuso horário posteriormente. Como não está mais diretamente associado a uma hora identificável, a adição e subtração de intervalos de data e hora não considera as regras de ajuste de um fuso horário.

Para ilustrar, a transição para o horário de verão no fuso horário padrão central dos EUA ocorre às 2h00 do dia 9 de março de 2008. Com isso em mente, adicionar um intervalo de duas horas e meia a um horário padrão central de 1h30 de 9 de março de 2008 deve produzir uma data e hora de 5h00 de 9 de março de 2008. No entanto, como mostra o exemplo a seguir, o resultado da adição é 4:00 da manhã de 9 de março de 2008. O resultado desta operação representa o ponto no tempo correto, embora não seja a hora no fuso horário em que estamos interessados (ou seja, não tem o desvio de fuso horário esperado).

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

Operações aritméticas com horários em fusos horários

A TimeZoneInfo classe inclui métodos de conversão que aplicam automaticamente ajustes quando convertem horários de um fuso horário para outro. Estes métodos de conversão incluem:

Para obter detalhes, consulte Convertendo horários entre fusos horários.

A TimeZoneInfo classe não fornece nenhum método que aplique automaticamente regras de ajuste quando você executa aritmética de data e hora. No entanto, você pode aplicar regras de ajuste convertendo a hora em um fuso horário em UTC, executando a operação aritmética e, em seguida, convertendo de UTC de volta para a hora no fuso horário. Para obter detalhes, consulte Como usar fusos horários na aritmética de data e hora.

Por exemplo, o código a seguir é semelhante ao código anterior que adicionou duas horas e meia às 2h00 de 9 de março de 2008. No entanto, como ele converte uma hora padrão central em UTC antes de executar a aritmética de data e hora e, em seguida, converte o resultado do UTC de volta para a hora padrão central, a hora resultante reflete a transição do fuso horário padrão central para o horário de verão.

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

Consulte também