Exécution d’opérations arithmétiques avec des dates et heures

Même si les structures DateTime et DateTimeOffset fournissent des membres qui exécutent des opérations arithmétiques sur leurs valeurs, les résultats des opérations arithmétiques sont très différents. Cet article examine ces différences, les met en rapport avec les degrés de prise en charge des fuseaux horaires dans les données de date et d’heure, et explique comment exécuter des opérations prenant pleinement en charge les fuseaux horaires à l’aide de données de date et d’heure.

Comparaisons et opérations arithmétiques avec des valeurs DateTime

La propriété DateTime.Kind permet d’assigner une valeur DateTimeKind à la date et à l’heure pour indiquer si elle représente l’heure locale, le temps universel coordonné (UTC) ou l’heure dans un fuseau horaire non spécifié. Toutefois, ces informations limitées de fuseau horaire sont ignorées lors de la comparaison ou de l’exécution d’opérations arithmétiques avec des dates et des heures sur des valeurs DateTimeKind. L’exemple suivant, qui compare l’heure locale actuelle à l’heure UTC actuelle, illustre comment les informations de fuseau horaire sont ignorées.

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

La méthode CompareTo(DateTime) signale que l’heure locale est antérieure (ou inférieure) à l’heure UTC, et l’opération de soustraction indique que la différence entre l’heure UTC et l’heure locale pour un système dans le fuseau horaire américain de l’heure normale du Pacifique est de sept heures. Toutefois, comme ces deux valeurs donnent des représentations différentes d’un même point dans le temps, il apparaît clairement dans ce cas que cet intervalle de temps est totalement attribuable au décalage du fuseau horaire local par rapport à l’heure UTC.

En règle générale, la propriété DateTime.Kind n’affecte pas les résultats retournés par les méthodes de comparaison et d’arithmétique Kind (comme la comparaison de deux points identiques dans le temps l’indique), mais elle peut affecter l’interprétation de ces résultats. Par exemple :

  • Le résultat de toute opération arithmétique exécutée sur deux valeurs de date et d’heure dont les propriétés DateTime.Kind sont égales à DateTimeKind reflète l’intervalle de temps effectif entre ces deux valeurs. De même, la comparaison de deux valeurs de date et d’heure de ce type indique précisément la relation entre les heures.

  • Le résultat de toute opération arithmétique ou de comparaison exécutée sur deux valeurs de date et d’heure dont les propriétés DateTime.Kind sont égales à DateTimeKind ou sur deux valeurs de date et d’heure dont les propriétés DateTime.Kind ont des valeurs différentes reflète la différence de temps horloge entre ces deux valeurs.

  • Les opérations arithmétiques ou de comparaison sur des valeurs de date et d’heure locales ne prennent pas en compte le fait qu’une valeur particulière soit ambiguë ou non valide, ni l’incidence de règles d’ajustement quelconques résultant du passage du fuseau horaire local à l’heure d’été ou à l’heure d’hiver.

  • Toute opération qui compare ou calcule la différence entre l’heure UTC et une heure locale inclut dans le résultat un intervalle de temps égal au décalage du fuseau horaire local par rapport à l’heure UTC.

  • Toute opération qui compare ou calcule la différence entre une heure non spécifiée et l’heure UTC ou l’heure locale reflète le temps horloge simple. Les décalages entre les fuseaux horaires ne sont pas pris en compte et le résultat ne reflète pas l’application des règles d’ajustement des fuseaux horaires.

  • Toute opération qui compare ou calcule la différence entre deux heures non spécifiées peut inclure un intervalle inconnu qui reflète la différence d’heure entre deux fuseaux horaires différents.

Il existe de nombreux scénarios dans lesquels les différences entre les fuseaux horaires n’affectent pas les calculs des dates et des heures (vous pouvez consulter certains de ces scénarios dans Choisir entre DateTime, DateTimeOffset, TimeSpan et TimeZoneInfo) ou dans lesquels le contexte des données de date et d’heure définit la signification des opérations arithmétiques ou de comparaison.

Comparaisons et opérations arithmétiques avec des valeurs DateTimeOffset

Une valeur DateTimeOffset inclut non seulement une date et une heure, mais également un décalage qui définit clairement cette date et cette heure par rapport à l’heure UTC. Ce décalage permet de définir une égalité d’une manière quelque peu différente de celle des valeurs DateTime. Les valeurs DateTime sont égales lorsqu’elles ont la même valeur de date et d’heure, alors que les valeurs DateTimeOffset sont égales lorsqu’elles font toutes les deux référence au même point dans le temps. Lorsqu’elle est utilisée dans des comparaisons ou dans la plupart des opérations arithmétiques qui déterminent l’intervalle entre deux dates et heures, une valeur DateTimeOffset est de ce fait plus précise et nécessite une interprétation moindre. Cette différence de comportement est illustrée dans l’exemple suivant, qui est l’équivalent DateTimeOffset de l’exemple précédent, qui comparait des valeurs DateTimeOffsetd’heure locale et d’heure UTC.

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)

Dans cet exemple, la méthode CompareTo indique que l’heure locale actuelle et l’heure UTC actuelle sont égales, et la soustraction des valeurs CompareTo(DateTimeOffset) indique que la différence entre les deux heures est TimeSpan.Zero.

L’utilisation de valeurs DateTimeOffset dans des opérations arithmétiques de date et d’heure présente une limitation majeure : les valeurs DateTimeOffset prennent partiellement en charge les fuseaux horaires, et non pas pleinement. Même si le décalage de la valeur DateTimeOffset reflète le décalage d’un fuseau horaire par rapport à l’heure UTC lorsqu’une valeur est assignée initialement à une variable DateTimeOffset, il se dissocie par la suite du fuseau horaire. Comme il n’est plus directement associé à une heure identifiable, l’addition et la soustraction d’intervalles de dates et d’heures ne prennent pas en compte les règles d’ajustement des fuseaux horaires.

À titre d’exemple, la transition vers l’heure d’été dans le fuseau horaire des États-Unis heure normale du Centre se produit à 2 h le 9 mars 2008. Dans cette optique, l’ajout d’un intervalle de deux heures et demi à 1 h 30 le 9 mars 2008 heure normale du Centre devrait avoir pour résultat une date et une heure de 5 h le 9 mars 2008. Toutefois, comme le montre l’exemple suivant, le résultat de l’addition est 4 h le 9 mars 2008. Le résultat de cette opération représente l’heure correcte, même s’il ne s’agit pas de l’heure dans le fuseau horaire qui nous intéresse (autrement dit, elle n’a pas le décalage horaire attendu).

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

Opérations arithmétiques avec des heures dans des fuseaux horaires

La classe TimeZoneInfo inclut des méthodes de conversion qui appliquent automatiquement des ajustements lorsqu’elles convertissent des heures d’un fuseau horaire à un autre. Ces méthodes de conversion sont les suivantes :

Pour en savoir plus, consultez Conversion d’heures entre fuseaux horaires.

La classe TimeZoneInfo ne fournit aucune méthode appliquant automatiquement des règles d’ajustement lorsque vous effectuez des opérations arithmétiques de date et d’heure. Toutefois, vous pouvez appliquer des règles d’ajustement en convertissant l’heure d’un fuseau horaire en heure UTC, effectuant l’opération arithmétique, puis reconvertissant l’heure UTC dans l’heure du fuseau horaire. Pour plus d’informations, consultez Guide pratique : utiliser des fuseaux horaires dans des opérations arithmétiques de date et d’heure.

Par exemple, le code suivant est semblable au code précédent qui ajoutait deux heures trente à 2 h le 9 mars 2008. Toutefois, comme il convertit l’heure du Centre des États-Unis en heure UTC avant d’effectuer les opérations arithmétiques de date et d’heure, puis reconvertit le résultat UTC en heure du Centre, l’heure résultante reflète le passage du fuseau horaire du Centre des États-Unis à l’heure d’été.

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

Voir aussi