Partager via


Choisir entre DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly et TimeZoneInfo

Les applications .NET peuvent utiliser des informations de date et d’heure de plusieurs façons. Les utilisations les plus courantes des informations de date et d’heure sont les suivantes :

  • Pour refléter une date uniquement, afin que les informations d’heure ne soient pas importantes.
  • Pour refléter une heure uniquement, de sorte que les informations de date ne sont pas importantes.
  • Pour refléter une date et une heure abstraites qui ne sont pas liées à une heure et un emplacement spécifiques (par exemple, la plupart des magasins d’une chaîne internationale ouverte les jours de semaine à 9h00).
  • Pour récupérer des informations de date et d’heure à partir de sources en dehors de .NET, généralement où les informations de date et d’heure sont stockées dans un type de données simple.
  • Pour identifier de manière unique et sans ambiguïté un point unique dans le temps. Certaines applications nécessitent qu’une date et une heure soient non ambiguës uniquement sur le système hôte. D’autres applications exigent qu’elles soient non ambiguës entre les systèmes (autrement dit, une date sérialisée sur un système peut être désérialisée de manière significative et utilisée sur un autre système n’importe où dans le monde).
  • Pour préserver plusieurs horaires liés (par exemple, l'heure locale du demandeur et l'heure de réception par le serveur pour une requête web).
  • Pour effectuer une arithmétique de date et d’heure, peut-être avec un résultat qui identifie de manière unique et non ambiguë un point unique dans le temps.

.NET inclut les types DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlyet TimeZoneInfo, qui peuvent tous être utilisés pour générer des applications qui fonctionnent avec des dates et des heures.

Remarque

Cet article ne traite pas TimeZone, car ses fonctionnalités sont presque entièrement incorporées dans la classe TimeZoneInfo. Dans la mesure du possible, utilisez la classe TimeZoneInfo au lieu de la classe TimeZone.

La structure DateTimeOffset

La structure DateTimeOffset représente une valeur de date et d’heure, ainsi qu’un décalage indiquant de combien cette valeur diffère de l’heure UTC. Ainsi, la valeur identifie toujours sans ambiguïté un point unique dans le temps.

Le type DateTimeOffset inclut toutes les fonctionnalités du type DateTime ainsi que la reconnaissance du fuseau horaire. Cela le rend adapté aux applications qui :

  • Identifiez de manière unique et sans ambiguïté un point unique dans le temps. Le type DateTimeOffset peut être utilisé pour définir sans ambiguïté la signification de « maintenant », pour journaliser les temps de transaction, pour consigner les heures des événements système ou d’application, et pour enregistrer les heures de création et de modification des fichiers.
  • Effectuez des opérations arithmétiques de date et d’heure générales.
  • Conservez plusieurs temps liés, à condition que ces temps soient stockés sous forme de deux valeurs distinctes ou comme deux membres d'une structure.

Remarque

Ces utilisations pour les valeurs DateTimeOffset sont beaucoup plus courantes que celles des valeurs DateTime. Par conséquent, considérez DateTimeOffset comme type de date et d’heure par défaut pour le développement d’applications.

Une valeur DateTimeOffset n’est pas liée à un fuseau horaire particulier, mais peut provenir de divers fuseaux horaires. L’exemple suivant répertorie les fuseaux horaires auxquels un certain nombre de valeurs DateTimeOffset (y compris une heure standard du Pacifique locale) peuvent appartenir.

using System;
using System.Collections.ObjectModel;

public class TimeOffsets
{
   public static void Main()
   {
      DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
      DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
      DateTimeOffset thisTime;

      thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));
      ShowPossibleTimeZones(thisTime);
   }

   private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)
   {
      TimeSpan offset = offsetTime.Offset;
      ReadOnlyCollection<TimeZoneInfo> timeZones;

      Console.WriteLine("{0} could belong to the following time zones:",
                        offsetTime.ToString());
      // Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones();
      // Iterate time zones
      foreach (TimeZoneInfo timeZone in timeZones)
      {
         // Compare offset with offset for that date in that time zone
         if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
            Console.WriteLine("   {0}", timeZone.DisplayName);
      }
      Console.WriteLine();
   }
}
// This example displays the following output to the console:
//       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
//          (GMT-07:00) Arizona
//          (GMT-08:00) Pacific Time (US & Canada)
//          (GMT-08:00) Tijuana, Baja California
//
//       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
//          (GMT-06:00) Central America
//          (GMT-06:00) Central Time (US & Canada)
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
//          (GMT-06:00) Saskatchewan
//
//       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
//          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
//          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
//          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
//          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
//          (GMT+01:00) West Central Africa
Imports System.Collections.ObjectModel

Module TimeOffsets
    Public Sub Main()
        Dim thisTime As DateTimeOffset

        thisTime = New DateTimeOffset(#06/10/2007#, New TimeSpan(-7, 0, 0))
        ShowPossibleTimeZones(thisTime)

        thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(-6, 0, 0))
        ShowPossibleTimeZones(thisTime)

        thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(+1, 0, 0))
        ShowPossibleTimeZones(thisTime)
    End Sub

    Private Sub ShowPossibleTimeZones(offsetTime As DateTimeOffset)
        Dim offset As TimeSpan = offsetTime.Offset
        Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo)

        Console.WriteLine("{0} could belong to the following time zones:", _
                          offsetTime.ToString())
        ' Get all time zones defined on local system
        timeZones = TimeZoneInfo.GetSystemTimeZones()
        ' Iterate time zones
        For Each timeZone As TimeZoneInfo In timeZones
            ' Compare offset with offset for that date in that time zone
            If timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset) Then
                Console.WriteLine("   {0}", timeZone.DisplayName)
            End If
        Next
        Console.WriteLine()
    End Sub
End Module
' This example displays the following output to the console:
'       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
'          (GMT-07:00) Arizona
'          (GMT-08:00) Pacific Time (US & Canada)
'          (GMT-08:00) Tijuana, Baja California
'       
'       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
'          (GMT-06:00) Central America
'          (GMT-06:00) Central Time (US & Canada)
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
'          (GMT-06:00) Saskatchewan
'       
'       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
'          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
'          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
'          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
'          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
'          (GMT+01:00) West Central Africa

La sortie indique que chaque valeur de date et d’heure de cet exemple peut appartenir à au moins trois fuseaux horaires différents. La valeur DateTimeOffset du 10/06/2007 indique que si une valeur de date et d’heure représente une heure d’été, son décalage par rapport à UTC ne correspond pas nécessairement au décalage UTC de base du fuseau horaire d’origine ou au décalage utc trouvé dans son nom d’affichage. Étant donné qu’une seule valeur de DateTimeOffset n’est pas étroitement couplée à son fuseau horaire, elle ne peut pas refléter la transition d’un fuseau horaire vers et depuis l’heure d’été. Cela peut être problématique lorsque l’arithmétique de date et d’heure est utilisée pour manipuler une valeur DateTimeOffset. Pour une discussion sur la façon d’effectuer des opérations arithmétiques de date et d’heure d’une manière qui tient compte des règles d’ajustement d’un fuseau horaire, consultez Effectuer des opérations arithmétiques avec des dates et des heures.

La structure DateTime

Une valeur DateTime définit une date et une heure particulières. Il inclut une propriété Kind qui fournit des informations limitées sur le fuseau horaire auquel appartient cette date et cette heure. La valeur DateTimeKind retournée par la propriété Kind indique si la valeur DateTime représente l’heure locale (DateTimeKind.Local), le temps universel coordonné (UTC) (DateTimeKind.Utc) ou une heure non spécifiée (DateTimeKind.Unspecified).

La structure DateTime convient aux applications présentant une ou plusieurs des caractéristiques suivantes :

  • Utiliser des dates et des heures abstraites.
  • Utilisez des dates et des heures pour lesquelles les informations de fuseau horaire sont manquantes.
  • Utilisez uniquement les dates et heures UTC.
  • Effectuent des calculs de dates et d'heures, mais sont surtout concernées par des résultats d'ordre général. Par exemple, dans une opération d’ajout qui ajoute six mois à une date et une heure particulières, il n’est souvent pas important de déterminer si le résultat est ajusté pour l’heure d’été.

Sauf si une valeur DateTime particulière représente UTC, cette valeur de date et d’heure est souvent ambiguë ou limitée dans sa portabilité. Par exemple, si une valeur DateTime représente l’heure locale, elle est portable dans ce fuseau horaire local (autrement dit, si la valeur est désérialisée sur un autre système dans le même fuseau horaire, cette valeur identifie toujours un point unique dans le temps). En dehors du fuseau horaire local, cette valeur DateTime peut avoir plusieurs interprétations. Si la propriété Kind de la valeur est DateTimeKind.Unspecified, elle est encore moins portable : elle est désormais ambiguë dans le même fuseau horaire et peut-être même sur le même système où elle a été sérialisée pour la première fois. Seulement si une valeur DateTime représente UTC, cette valeur identifie sans ambiguïté un point unique dans le temps, quel que soit le système ou le fuseau horaire dans lequel la valeur est utilisée.

Important

Lors de l’enregistrement ou du partage de données DateTime, utilisez UTC et définissez la propriété Kind de la valeur DateTime sur DateTimeKind.Utc.

La structure DateOnly

La structure DateOnly représente une date spécifique, sans heure. Étant donné qu’il n’a pas de composant d’heure, il représente une date du début du jour à la fin du jour. Cette structure est idéale pour stocker des dates spécifiques, telles qu’une date de naissance, une date anniversaire, un jour férié ou une date liée à l’entreprise.

Bien que vous puissiez utiliser DateTime tout en ignorant le composant de temps, il existe quelques avantages à utiliser DateOnly sur DateTime:

  • La structure DateTime peut passer au jour précédent ou suivant si elle est décalée par un fuseau horaire. DateOnly ne peut pas être décalé par un fuseau horaire et représente toujours la date définie.
  • La sérialisation d’une structure DateTime inclut le composant de temps, qui peut masquer l’intention des données. En outre, DateOnly sérialise moins de données.
  • Lorsque le code interagit avec une base de données, telle que SQL Server, les dates entières sont généralement stockées en tant que type de données date, ce qui n’inclut pas une heure. DateOnly correspond mieux au type de base de données.

Pour plus d’informations sur DateOnly, consultez Comment utiliser les structures DateOnly et TimeOnly.

Important

DateOnly n’est pas disponible pour .NET Framework.

La structure TimeSpan

La structure TimeSpan représente un intervalle de temps. Ses deux utilisations classiques sont les suivantes :

  • Reflétant l’intervalle de temps entre deux valeurs de date et d’heure. Par exemple, soustraire une valeur DateTime d’une autre retourne une valeur TimeSpan.
  • Mesure du temps écoulé. Par exemple, la propriété Stopwatch.Elapsed retourne une valeur TimeSpan qui reflète l’intervalle de temps écoulé depuis l’appel à l’une des méthodes Stopwatch qui commence à mesurer le temps écoulé.

Une valeur TimeSpan peut également être utilisée comme remplacement d’une valeur DateTime lorsque cette valeur reflète une heure sans référence à un jour donné. Cette utilisation est similaire aux propriétés DateTime.TimeOfDay et DateTimeOffset.TimeOfDay, qui retournent une valeur TimeSpan qui représente l’heure sans référence à une date. Par exemple, la structure de TimeSpan peut être utilisée pour refléter l’heure d’ouverture ou de fermeture quotidienne d’un magasin, ou elle peut être utilisée pour représenter l’heure à laquelle tout événement régulier se produit.

L’exemple suivant définit une structure StoreInfo qui inclut des objets TimeSpan pour les heures d’ouverture et de fermeture du magasin, ainsi qu’un objet TimeZoneInfo qui représente le fuseau horaire du magasin. La structure inclut également deux méthodes, IsOpenNow et IsOpenAt, qui indique si le magasin est ouvert à une heure spécifiée par l’utilisateur, qui est supposé être dans le fuseau horaire local.

using System;

public struct StoreInfo
{
   public String store;
   public TimeZoneInfo tz;
   public TimeSpan open;
   public TimeSpan close;

   public bool IsOpenNow()
   {
      return IsOpenAt(DateTime.Now.TimeOfDay);
   }

   public bool IsOpenAt(TimeSpan time)
   {
      TimeZoneInfo local = TimeZoneInfo.Local;
      TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;

      // Is the store in the same time zone?
      if (tz.Equals(local)) {
         return time >= open & time <= close;
      }
      else {
         TimeSpan delta = TimeSpan.Zero;
         TimeSpan storeDelta = TimeSpan.Zero;

         // Is it daylight saving time in either time zone?
         if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
            delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;

         if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
            storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;

         TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
         return comparisonTime >= open && comparisonTime <= close;
      }
   }
}
Public Structure StoreInfo
    Dim store As String
    Dim tz As TimeZoneInfo
    Dim open As TimeSpan
    Dim close As TimeSpan

    Public Function IsOpenNow() As Boolean
        Return IsOpenAt(Date.Now.TimeOfDay)
    End Function

    Public Function IsOpenAt(time As TimeSpan) As Boolean
        Dim local As TimeZoneInfo = TimeZoneInfo.Local
        Dim offset As TimeSpan = TimeZoneInfo.Local.BaseUtcOffset

        ' Is the store in the same time zone?
        If tz.Equals(local) Then
            Return time >= open AndAlso time <= close
        Else
            Dim delta As TimeSpan = TimeSpan.Zero
            Dim storeDelta As TimeSpan = TimeSpan.Zero

            ' Is it daylight saving time in either time zone?
            If local.IsDaylightSavingTime(Date.Now.Date + time) Then
                delta = local.GetAdjustmentRules(local.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            If tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(Date.Now.Date + time, local, tz))
                storeDelta = tz.GetAdjustmentRules(tz.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            Dim comparisonTime As TimeSpan = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate
            Return (comparisonTime >= open AndAlso comparisonTime <= close)
        End If
    End Function
End Structure

La structure StoreInfo peut ensuite être utilisée par le code client comme suit.

public class Example
{
   public static void Main()
   {
      // Instantiate a StoreInfo object.
      var store103 = new StoreInfo();
      store103.store = "Store #103";
      store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
      // Store opens at 8:00.
      store103.open = new TimeSpan(8, 0, 0);
      // Store closes at 9:30.
      store103.close = new TimeSpan(21, 30, 0);

      Console.WriteLine("Store is open now at {0}: {1}",
                        DateTime.Now.TimeOfDay, store103.IsOpenNow());
      TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
                           new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0) };
      foreach (var time in times)
         Console.WriteLine("Store is open at {0}: {1}",
                           time, store103.IsOpenAt(time));
   }
}
// The example displays the following output:
//       Store is open now at 15:29:01.6129911: True
//       Store is open at 08:00:00: True
//       Store is open at 21:00:00: True
//       Store is open at 04:59:00: False
//       Store is open at 18:31:00: True
Module Example
    Public Sub Main()
        ' Instantiate a StoreInfo object.
        Dim store103 As New StoreInfo()
        store103.store = "Store #103"
        store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
        ' Store opens at 8:00.
        store103.open = new TimeSpan(8, 0, 0)
        ' Store closes at 9:30.
        store103.close = new TimeSpan(21, 30, 0)

        Console.WriteLine("Store is open now at {0}: {1}",
                          Date.Now.TimeOfDay, store103.IsOpenNow())
        Dim times() As TimeSpan = {New TimeSpan(8, 0, 0),
                                    New TimeSpan(21, 0, 0),
                                    New TimeSpan(4, 59, 0),
                                    New TimeSpan(18, 31, 0)}
        For Each time In times
            Console.WriteLine("Store is open at {0}: {1}",
                              time, store103.IsOpenAt(time))
        Next
    End Sub
End Module
' The example displays the following output:
'       Store is open now at 15:29:01.6129911: True
'       Store is open at 08:00:00: True
'       Store is open at 21:00:00: False
'       Store is open at 04:59:00: False
'       Store is open at 18:31:00: False

La structure TimeOnly

La structure TimeOnly représente une valeur de temps de jour, telle qu’une horloge d’alarme quotidienne ou l’heure à laquelle vous mangez le déjeuner chaque jour. TimeOnly est limité à la plage de 00:00:00.0000000 - 23:59:59.99999999, une heure spécifique du jour.

Avant l’introduction du type TimeOnly, les programmeurs utilisaient généralement le type DateTime ou le type TimeSpan pour représenter une heure spécifique. Toutefois, l’utilisation de ces structures pour simuler une heure sans date peut entraîner des problèmes, ce qui TimeOnly résout :

  • TimeSpan représente le temps écoulé, tel que le temps mesuré avec un chronomètre. La plage supérieure est supérieure à 29 000 ans, et sa valeur peut être négative pour indiquer le recul dans le temps. Une TimeSpan négative n’indique pas une heure spécifique de la journée.
  • Si TimeSpan est utilisé comme heure de la journée, il existe un risque qu’il puisse être manipulé sur une valeur en dehors de la journée de 24 heures. TimeOnly n’a pas ce risque. Par exemple, si l'équipe de travail d'un employé commence à 18:00 et dure 8 heures, l'ajout de 8 heures à la structure TimeOnly aboutira sur 2:00.
  • L’utilisation de DateTime pour une heure de la journée nécessite qu’une date arbitraire soit associée à l’heure, puis ignorée ultérieurement. Il est courant de choisir DateTime.MinValue (0001-01-01) comme date, toutefois, si des heures sont soustraites de la valeur DateTime, une exception OutOfRange peut se produire. TimeOnly n’a pas ce problème car le temps se déplace vers l’avant et l’arrière autour de la période de 24 heures.
  • La sérialisation d’une structure DateTime inclut le composant de date, qui peut masquer l’intention des données. En outre, TimeOnly sérialise moins de données.

Pour plus d’informations sur TimeOnly, consultez Comment utiliser les structures DateOnly et TimeOnly.

Important

TimeOnly n’est pas disponible pour .NET Framework.

La classe TimeZoneInfo

La classe TimeZoneInfo représente l’un des fuseaux horaires de la Terre et active la conversion de n’importe quelle date et heure dans un fuseau horaire en son équivalent dans un autre fuseau horaire. La classe TimeZoneInfo permet d’utiliser des dates et des heures afin que toute valeur de date et d’heure identifie sans ambiguïté un point unique dans le temps. La classe TimeZoneInfo est également extensible. Bien qu’il dépend des informations de fuseau horaire fournies pour les systèmes Windows et définis dans le Registre, il prend en charge la création de fuseaux horaires personnalisés. Il prend également en charge la sérialisation et la désérialisation des informations de fuseau horaire.

Dans certains cas, tirer pleinement parti de la classe TimeZoneInfo peut nécessiter d’autres travaux de développement. Si les valeurs de date et d’heure ne sont pas étroitement couplées aux fuseaux horaires auxquels elles appartiennent, un travail supplémentaire est nécessaire. Sauf si votre application fournit un mécanisme permettant de lier une date et une heure à son fuseau horaire associé, il est facile pour une valeur de date et d’heure particulière de se dissocier de son fuseau horaire. Une méthode de liaison de ces informations consiste à définir une classe ou une structure qui contient à la fois la valeur de date et d’heure et son objet de fuseau horaire associé.

Pour tirer parti de la prise en charge du fuseau horaire dans .NET, vous devez connaître le fuseau horaire auquel appartient une valeur de date et d’heure lorsque cet objet date et heure est instancié. Le fuseau horaire n’est souvent pas connu, en particulier dans les applications web ou réseau.

Voir aussi