Actuellement, nous avons un moyen standard de traiter les .NET DateTime
de manière consciente de TimeZone: chaque fois que nous en produisons un, DateTime
nous le faisons en UTC (par exemple en utilisant DateTime.UtcNow
), et chaque fois que nous en affichons un, nous convertissons à partir de l'UTC en heure locale de l'utilisateur. .
Cela fonctionne bien, mais j'ai lu DateTimeOffset
et comment il capture l'heure locale et UTC dans l'objet lui-même. La question est donc de savoir quels seraient les avantages de l'utilisation DateTimeOffset
par rapport à ce que nous avons déjà fait?
Réponses:
DateTimeOffset
est une représentation du temps instantané (également appelé temps absolu ). Par cela, je veux dire un moment dans le temps qui est universel pour tout le monde (sans compter les secondes intercalaires ou les effets relativistes de la dilatation du temps ). Une autre façon de représenter le temps instantané est avec unDateTime
où.Kind
estDateTimeKind.Utc
.Ceci est distinct du temps du calendrier (également connu sous le nom de temps civil ), qui est une position sur le calendrier de quelqu'un, et il existe de nombreux calendriers différents dans le monde entier. Nous appelons ces calendriers des fuseaux horaires . L'heure du calendrier est représentée par un
DateTime
où.Kind
estDateTimeKind.Unspecified
, ouDateTimeKind.Local
. Et.Local
n'a de sens que dans les scénarios où vous avez une compréhension implicite de l'emplacement de l'ordinateur qui utilise le résultat. (Par exemple, le poste de travail d'un utilisateur)Alors, pourquoi
DateTimeOffset
au lieu d'un UTCDateTime
? Tout est question de perspective. Prenons une analogie - nous prétendons être des photographes.Imaginez que vous vous tenez sur une chronologie de calendrier, pointant une caméra vers une personne sur la chronologie instantanée disposée devant vous. Vous alignez votre caméra selon les règles de votre fuseau horaire - qui changent périodiquement en raison de l'heure d'été ou en raison d'autres modifications de la définition légale de votre fuseau horaire. (Vous n'avez pas une main ferme, donc votre appareil photo est fragile.)
La personne debout sur la photo verrait l'angle sous lequel votre appareil photo est venu. Si d'autres prenaient des photos, elles pourraient prendre des angles différents. C'est ce que représente la
Offset
partie duDateTimeOffset
.Donc, si vous étiquetez votre appareil photo "heure de l'Est", parfois vous pointez à partir de -5, et parfois vous pointez à partir de -4. Il y a des caméras partout dans le monde, toutes étiquetées des choses différentes, et toutes pointant vers la même chronologie instantanée sous différents angles. Certains d'entre eux sont juste à côté (ou au-dessus) les uns des autres, il ne suffit donc pas de connaître le décalage pour déterminer le fuseau horaire auquel l'heure est liée.
Et qu'en est-il de l'UTC? Eh bien, c'est la seule caméra qui est garantie d'avoir une main ferme. C'est sur un trépied, fermement ancré dans le sol. Ça ne va nulle part. Nous appelons son angle de perspective le décalage nul.
Alors - que nous dit cette analogie? Il fournit des directives intuitives-
Si vous représentez le temps par rapport à un endroit en particulier, représentez-le en temps calendaire avec a
DateTime
. Assurez-vous simplement de ne jamais confondre un calendrier avec un autre.Unspecified
devrait être votre hypothèse.Local
est seulement utile venant deDateTime.Now
. Par exemple, je pourrais l'obtenirDateTime.Now
et l'enregistrer dans une base de données - mais quand je le récupère, je dois supposer que c'est le casUnspecified
. Je ne peux pas compter sur le fait que mon calendrier local est le même que celui à l'origine.Si vous devez toujours être certain du moment, assurez-vous de représenter le temps instantané. Utilisez
DateTimeOffset
pour l'appliquer, ou utilisez UTCDateTime
par convention.Si vous avez besoin de suivre un moment instantané, mais que vous voulez également savoir "À quelle heure l'utilisateur pensait-il qu'il était sur son calendrier local?" - alors vous devez utiliser un
DateTimeOffset
. C'est très important pour les systèmes de chronométrage, par exemple - à la fois pour des questions techniques et juridiques.Si vous devez modifier un enregistrement antérieur
DateTimeOffset
, vous n'avez pas suffisamment d'informations dans le décalage seul pour vous assurer que le nouveau décalage est toujours pertinent pour l'utilisateur. Vous devez également stocker un identifiant de fuseau horaire (pensez - j'ai besoin du nom de cette caméra pour pouvoir prendre une nouvelle photo même si la position a changé).Il convient également de noter que Noda Time a une représentation appelée
ZonedDateTime
à cet effet, tandis que la bibliothèque de classes de base .Net n'a rien de similaire. Vous devez stocker à la foisDateTimeOffset
uneTimeZoneInfo.Id
valeur et une valeur.Parfois, vous souhaiterez représenter une heure de calendrier qui est locale à "celui qui la regarde". Par exemple, lors de la définition de ce que signifie aujourd'hui . Aujourd'hui est toujours de minuit à minuit, mais ceux-ci représentent un nombre presque infini de plages qui se chevauchent sur la chronologie instantanée. (Dans la pratique, nous avons un nombre fini de fuseaux horaires, mais vous pouvez exprimer des décalages jusqu'à la graduation). Donc, dans ces situations, assurez-vous de comprendre comment limiter le "qui demande?" question vers le bas à un seul fuseau horaire, ou de les traduire en temps instantané, le cas échéant.
Voici quelques autres petits détails à
DateTimeOffset
ce sujet pour sauvegarder cette analogie, et quelques conseils pour la garder droite:Si vous comparez deux
DateTimeOffset
valeurs, elles sont d'abord normalisées à un décalage nul avant de les comparer. En d'autres termes,2012-01-01T00:00:00+00:00
et se2012-01-01T02:00:00+02:00
réfèrent au même moment instantané, et sont donc équivalents.Si vous effectuez un test unitaire et devez être certain du décalage, testez à la fois la
DateTimeOffset
valeur et la.Offset
propriété séparément.Il existe une conversion implicite unidirectionnelle intégrée au framework .Net qui vous permet de passer un
DateTime
dans n'importe quelDateTimeOffset
paramètre ou variable. Ce faisant, les.Kind
questions . Si vous passez un type UTC, il se poursuivra avec un décalage nul, mais si vous passez l'un.Local
ou l' autre.Unspecified
, il supposera être local . Le cadre dit essentiellement: "Eh bien, vous m'avez demandé de convertir le temps du calendrier en temps instantané, mais je n'ai aucune idée d'où cela vient, donc je vais simplement utiliser le calendrier local." C'est un énorme problème si vous chargez un fichier non spécifiéDateTime
sur un ordinateur avec un fuseau horaire différent. (À mon humble avis - cela devrait lever une exception - mais ce n'est pas le cas.)Prise sans vergogne:
Beaucoup de gens ont partagé avec moi qu'ils trouvent cette analogie extrêmement précieuse, donc je l'ai incluse dans mon cours Pluralsight, Fondamentaux de la date et de l'heure . Vous trouverez une procédure pas à pas de l'analogie de la caméra dans le deuxième module, "Context Matters", dans le clip intitulé "Calendar Time vs. Instantaneous Time".
la source
DateTimeOffset
en C #, vous devez le conserver sur unDATETIMEOFFSET
serveur SQL Server.DATETIME2
ou tout simplementDATETIME
(selon la plage requise) conviennent pour lesDateTime
valeurs normales . Oui - vous pouvez résoudre une heure locale à partir de n'importe quel appariement de fuseau horaire + dto ou utc. La différence est - voulez-vous toujours calculer les règles avec chaque résolution, ou voulez-vous les précalculer? Dans de nombreux cas (parfois pour des raisons juridiques), un DTO est un meilleur choix.DateTimeOffset.Now
sur le serveur, vous obtiendrez en effet l'offset du serveur. Le fait est que leDateTimeOffset
type peut conserver ce décalage. Vous pouvez tout aussi facilement le faire sur le client, l'envoyer au serveur, puis votre serveur connaîtra le décalage du client.De Microsoft:
source: "Choix entre DateTime, DateTimeOffset, TimeSpan et TimeZoneInfo" , MSDN
Nous utilisons
DateTimeOffset
presque tout car notre application traite des moments particuliers (par exemple, lorsqu'un enregistrement a été créé / mis à jour). En remarque, nous utilisons égalementDATETIMEOFFSET
dans SQL Server 2008.Je considère
DateTime
que cela est utile lorsque vous souhaitez traiter uniquement les dates, les heures uniquement ou les traiter dans un sens générique. Par exemple, si vous avez une alarme que vous voulez partir tous les jours à 7 heures, vous pouvez stocker que dans unDateTime
utilisant unDateTimeKind
deUnspecified
parce que vous voulez aller au large à 7h du matin quelle que soit l' heure d' été. Mais si vous souhaitez représenter l'historique des événements d'alarme, vous devez utiliserDateTimeOffset
.Soyez prudent lorsque vous utilisez un mélange de
DateTimeOffset
etDateTime
surtout lorsque vous attribuez et comparez les types. En outre, ne comparez que lesDateTime
instances identiquesDateTimeKind
car ellesDateTime
ignorent le décalage du fuseau horaire lors de la comparaison.la source
Kind
la même chose, la comparaison pourrait être erronée. Si les deux parties l'ont,DateTimeKind.Unspecified
vous ne savez pas vraiment qu'elles viennent du même fuseau horaire. Si les deux côtés le sontDateTimeKind.Local
, la plupart des comparaisons vont bien, mais vous pouvez toujours avoir des erreurs si un côté est ambigu dans le fuseau horaire local. Vraiment, seules lesDateTimeKind.Utc
comparaisons sont infaillibles, et oui,DateTimeOffset
c'est généralement préférable. (Acclamations!)DateTime est capable de stocker seulement deux heures distinctes, l'heure locale et UTC. La propriété Kind indique lequel.
DateTimeOffset développe cela en étant capable de stocker les heures locales de n'importe où dans le monde. Il stocke également le décalage entre cette heure locale et UTC. Notez que DateTime ne peut pas faire cela sauf si vous ajoutez un membre supplémentaire à votre classe pour stocker ce décalage UTC. Ou ne travaillez qu'avec UTC. Ce qui en soi est une belle idée d'ailleurs.
la source
Il y a quelques endroits où
DateTimeOffset
cela a du sens. La première est lorsque vous avez affaire à des événements récurrents et à l'heure d'été. Disons que je veux régler une alarme pour qu'elle se déclenche à 9 h tous les jours. Si j'utilise la règle "stocker en UTC, afficher comme heure locale", l'alarme se déclenchera à une heure différente lorsque l'heure d'été est en vigueur.Il y en a probablement d'autres, mais l'exemple ci-dessus est en fait celui que j'ai rencontré par le passé (c'était avant l'ajout de
DateTimeOffset
la BCL - ma solution à l'époque était de stocker explicitement l'heure dans le fuseau horaire local et d'enregistrer les informations de fuseau horaire à côté d'elle: essentiellement ce quiDateTimeOffset
fait en interne).la source
La distinction la plus importante est que DateTime ne stocke pas les informations de fuseau horaire, contrairement à DateTimeOffset.
Bien que DateTime fasse la distinction entre UTC et Local, aucun décalage de fuseau horaire explicite ne lui est associé. Si vous effectuez un type de sérialisation ou de conversion, le fuseau horaire du serveur sera utilisé. Même si vous créez manuellement une heure locale en ajoutant des minutes pour compenser une heure UTC, vous pouvez toujours obtenir un bit dans l'étape de sérialisation, car (en raison de l'absence de décalage explicite dans DateTime), il utilisera le décalage de fuseau horaire du serveur.
Par exemple, si vous sérialisez une valeur DateTime avec Kind = Local en utilisant Json.Net et un format de date ISO, vous obtiendrez une chaîne comme
2015-08-05T07:00:00-04
. Notez que la dernière partie (-04) n'a rien à voir avec votre DateTime ou tout décalage que vous avez utilisé pour le calculer ... c'est simplement le décalage de fuseau horaire du serveur.Pendant ce temps, DateTimeOffset inclut explicitement le décalage. Il peut ne pas inclure le nom du fuseau horaire, mais au moins il comprend l'offset, et si vous le sérialisez, vous obtiendrez l'offset explicitement inclus dans votre valeur au lieu de l'heure locale du serveur.
la source
The most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Ce morceau de code de Microsoft explique tout:
la source
La plupart des réponses sont bonnes, mais j'ai pensé à ajouter d'autres liens de MSDN pour plus d'informations
la source
Une différence majeure est qu'il
DateTimeOffset
peut être utilisé en conjonction avecTimeZoneInfo
pour convertir en heures locales dans des fuseaux horaires autres que l'actuel.Ceci est utile sur une application serveur (par exemple ASP.NET) accessible aux utilisateurs dans différents fuseaux horaires.
la source
Le seul côté négatif de DateTimeOffset que je vois est que Microsoft "a oublié" (par conception) de le prendre en charge dans leur classe XmlSerializer. Mais il a depuis été ajouté à la classe d'utilitaires XmlConvert.
XmlConvert.ToDateTimeOffset
XmlConvert.ToString
Je dis allez-y et utilisez DateTimeOffset et TimeZoneInfo en raison de tous les avantages, faites simplement attention lors de la création d'entités qui seront ou peuvent être sérialisées vers ou depuis XML (tous les objets métier alors).
la source