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. UneTimeSpan
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 structureTimeOnly
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 choisirDateTime.MinValue
(0001-01-01) comme date, toutefois, si des heures sont soustraites de la valeurDateTime
, une exceptionOutOfRange
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.