Question DateTime vs DateTimeOffset


Actuellement, nous avons un moyen standard de traiter .net DateTimes d'une manière consciente TimeZone: Chaque fois que nous produisons un DateTime nous le faisons en UTC (par exemple en utilisant DateTime.UtcNow), et chaque fois que nous en affichons un, nous revenons de l'UTC à l'heure locale de l'utilisateur.

Cela fonctionne bien, mais j'ai lu à propos de DateTimeOffset et comment il capture l'heure locale et l'heure UTC dans l'objet lui-même. Donc, la question est, quels seraient les avantages de l'utilisation DateTimeOffset vs ce que nous avons déjà fait?


531
2017-12-02 02:39


origine


Réponses:


DateTimeOffset est une représentation de temps instantané (aussi connu sous le nom temps absolu). Par là, je veux dire un moment universel pour tout le monde (sans compter secondes intercalairesou les effets relativistes de dilatation du temps). Une autre façon de représenter le temps instantané est avec un DateTime où .Kind est DateTimeKind.Utc.

Ceci est distinct de heure du calendrier (aussi connu sous le nom heure civile), 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 fuseaux horaires. L'heure du calendrier est représentée par un DateTime où .Kind est DateTimeKind.Unspecified, ou DateTimeKind.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 UTC DateTime? Tout est une question de perspective.  Utilisons une analogie - nous prétendons être des photographes.

Imaginez que vous vous tenez sur une ligne du temps du calendrier, en pointant une caméra sur une personne sur la ligne de temps instantanée disposée devant vous. Vous alignez votre caméra en fonction des 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 stable, donc votre appareil photo est fragile.)

La personne qui se trouve sur la photo verra l'angle d'où provient votre appareil photo. Si d'autres prenaient des photos, ils pourraient avoir des angles différents. C'est ce que Offset partie de la DateTimeOffset représente.

Donc, si vous étiquetez votre caméra "Eastern Time", vous pointez parfois de -5, et parfois vous pointez de -4. Il y a des caméras partout dans le monde, toutes étiquetées différemment, et toutes pointant sur la même ligne de temps instantanée sous des angles différents. Certains d'entre eux se trouvent juste à côté (ou au-dessus) l'un de l'autre, donc le simple fait de connaître le décalage n'est pas suffisant 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 soit sûre d'avoir une main sûre. 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 zéro.

Instantaneous Time vs Calendar Time Visualization

Alors, que nous dit cette analogie? Il fournit des directives intuitives.

  • Si vous représentez le temps par rapport à un lieu en particulier, représentez-le en temps calendaire avec un DateTime. Assurez-vous de ne jamais confondre un calendrier avec un autre. Unspecified devrait être votre hypothèse. Local est seulement utile venant de DateTime.Now. Par exemple, je pourrais obtenir DateTime.Now et l'enregistrer dans une base de données - mais quand je le récupère, je dois supposer qu'il est Unspecified. Je ne peux pas compter que mon calendrier local est le même calendrier qu'il a été pris à l'origine.

  • Si vous devez toujours être certain du moment, assurez-vous que vous représentez l'heure instantanée. Utilisation DateTimeOffset pour l'appliquer ou utiliser UTC DateTime par convention.

  • Si vous avez besoin de suivre un moment instantané, mais que vous voulez également savoir «À quelle heure l'utilisateur a-t-il pensé que c'était sur son calendrier local? - alors vous doit utiliser un DateTimeOffset. Ceci est très important pour les systèmes de chronométrage, par exemple - à la fois pour des raisons techniques et juridiques.

  • Si jamais vous avez besoin de modifier un enregistrement précédemment enregistré 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 aussi 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 souligner que Noda Time a une représentation appelée ZonedDateTime pour cela, alors que la bibliothèque de classes de base .Net n'a rien de similaire. Vous devez stocker à la fois un DateTimeOffset et un TimeZoneInfo.Id valeur.

  • De temps en temps, vous voudrez représenter une heure de calendrier qui est locale à «qui la regarde». Par exemple, lors de la définition de aujourd'hui veux dire. Aujourd'hui est toujours minuit à minuit, mais ceux-ci représentent un nombre quasi-infini de plages de chevauchement sur la chronologie instantanée. (Dans la pratique, nous avons un nombre limité de fuseaux horaires, mais vous pouvez exprimer les décalages jusqu'à la coche) Donc, dans ces situations, assurez-vous de comprendre comment limiter le "qui demande?" interrogez un seul fuseau horaire ou faites-les traduire en temps instantané, selon le cas.

Voici quelques autres petites choses à propos de DateTimeOffset que sauvegarder cette analogie, et quelques conseils pour le garder droit:

  • Si vous comparez deux DateTimeOffset valeurs, elles sont d'abord normalisées à zéro avant la comparaison. En d'autres termes, 2012-01-01T00:00:00+00:00 et 2012-01-01T02:00:00+02:00 se référer au même moment instantané, et sont donc équivalents.

  • Si vous faites des tests unitaires et que vous avez besoin d'être certain de l'offset, testez tous les deux 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 tout DateTimeOffset paramètre ou variable. Ce faisant, la .Kind importe. Si vous passez un type UTC, il va porter avec un décalage de zéro, mais si vous passez soit .Local ou .Unspecified, il supposera être local. Le cadre dit essentiellement: «Eh bien, vous m'avez demandé de convertir l'heure du calendrier en temps instantané, mais je n'ai aucune idée d'où cela vient, donc je vais juste utiliser le calendrier local." Ceci est un énorme gotcha si vous chargez un non spécifié DateTimesur un ordinateur avec un fuseau horaire différent. (À mon humble avis - cela devrait jeter une exception - mais ce n'est pas le cas.)

Plug sans scrupule:

Beaucoup de gens ont partagé avec moi qu'ils trouvent cette analogie extrêmement précieuse, alors je l'ai inclus dans mon cours Pluralsight, Principes de base de la date et de l'heure. Vous trouverez une procédure étape par étape de l'analogie de l'appareil photo dans le deuxième module, «Context Matters», dans le clip intitulé «Calendrier vs temps instantané».


856
2018-01-10 22:09



De Microsoft:

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

la source: "Choix entre DateTime, DateTimeOffset, TimeSpan et TimeZoneInfo", MSDN

Nous utilisons DateTimeOffset pour presque tout, car notre application traite des points particuliers dans le temps (par exemple, lorsqu'un enregistrement a été créé / mis à jour). En remarque, nous utilisons DATETIMEOFFSET dans SQL Server 2008 également.

je vois DateTime comme étant utile lorsque vous voulez traiter des dates seulement, des temps seulement, ou traiter avec l'un ou l'autre dans un sens générique. Par exemple, si vous avez une alarme que vous voulez éteindre tous les jours à 7 heures du matin, vous pouvez stocker cela dans un DateTime en utilisant un DateTimeKind de Unspecified parce que vous voulez qu'il se déclenche à 7 heures du matin, peu importe l'heure d'été. Mais si vous voulez représenter l'historique des occurrences d'alarme, vous utiliserez DateTimeOffset.

Soyez prudent lorsque vous utilisez un mélange de DateTimeOffset et DateTime en particulier lors de l'attribution et la comparaison entre les types. Aussi, comparez seulement DateTime les instances qui sont les mêmes DateTimeKind car DateTime ignore le décalage de fuseau horaire lors de la comparaison.


201
2018-01-09 19:42



DateTime est capable de stocker uniquement deux heures distinctes, l'heure locale et UTC. le Gentil la propriété indique lequel.

DateTimeOffset développe cela en étant capable de stocker des heures locales de n'importe où dans le monde. Il stocke également les décalage entre cette heure locale et UTC. Notez que DateTime ne peut pas le faire, sauf si vous ajoutez un membre supplémentaire à votre classe pour stocker ce décalage UTC. Ou seulement travailler avec UTC. Ce qui en soi est une bonne idée.


54
2017-12-02 17:47



Il y a quelques endroits où DateTimeOffset logique. L'un est lorsque vous avez affaire à des événements récurrents et l'heure d'été. Disons que je veux mettre une alarme pour partir à 9h tous les jours. Si j'utilise la règle «stocker en UTC, affichage en heure locale», l'alarme se déclenchera à différent le moment où l'heure d'été est en vigueur.

Il y en a probablement d'autres, mais l'exemple ci-dessus en est un que j'ai déjà rencontré dans 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 enregistrer les informations de fuseau horaire à côté de lui: DateTimeOffset fait intérieurement).


31
2017-12-02 02:59



La distinction la plus importante est que DateTime ne stocke pas d'informations sur les fuseaux horaires, contrairement à DateTimeOffset.

Bien que DateTime fasse la distinction entre UTC et Local, il n'y a absolument aucun décalage de fuseau horaire explicite. Si vous effectuez une sérialisation ou une conversion, le fuseau horaire du serveur va être 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 juste le décalage du 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 inclut le décalage, et si vous le sérialisez, vous obtiendrez le décalage explicitement inclus dans votre valeur au lieu de l'heure locale du serveur.


16
2017-08-05 23:00



La plupart des réponses sont bonnes, mais j'ai pensé à ajouter quelques liens de MSDN pour plus d'informations


9
2018-04-29 00:41



Une différence majeure est que DateTimeOffset peut être utilisé en conjonction avec TimeZoneInfo convertir aux heures locales en fuseaux horaires autres que celui en cours.

Ceci est utile sur une application serveur (par exemple ASP.NET) à laquelle les utilisateurs accèdent dans différents fuseaux horaires.


7
2017-12-02 13:03