DateTime vs DateTimeOffset

742

Derzeit haben wir eine Standardmethode für den DateTimezeitzonenbewussten Umgang mit .NETs: Immer wenn wir eine DateTimein UTC erstellen (z. B. mit DateTime.UtcNow) und wenn wir eine anzeigen, konvertieren wir von UTC zurück in die Ortszeit des Benutzers .

Das funktioniert gut, aber ich habe darüber gelesen DateTimeOffsetund wie es die lokale und UTC-Zeit im Objekt selbst erfasst. Die Frage ist also, was wären die Vorteile der Verwendung im DateTimeOffsetVergleich zu dem, was wir bereits getan haben?

David Reis
quelle
3
Es gibt einige gute Antworten unten. Aber ich frage mich immer noch, was Sie, wenn überhaupt, davon überzeugt haben könnte, DateTimeOffset zu verwenden.
HappyNomad
1
Könnte
nawfal
Auch bei der Speicherung ist stackoverflow.com/questions/4715620/… interessant.
Dejan

Antworten:

1169

DateTimeOffsetist eine Darstellung der momentanen Zeit (auch als absolute Zeit bekannt ). Damit meine ich einen Moment in der Zeit, der für alle universell ist (ohne Berücksichtigung von Schaltsekunden oder den relativistischen Effekten der Zeitdilatation ). Eine andere Möglichkeit, die momentane Zeit darzustellen, ist das DateTimeWo .Kindist DateTimeKind.Utc.

Dies unterscheidet sich von der Kalenderzeit (auch als Zivilzeit bezeichnet ), die eine Position im Kalender einer Person darstellt, und es gibt weltweit viele verschiedene Kalender. Wir nennen diese Kalender Zeitzonen . Die Kalenderzeit wird durch ein DateTimeWo .Kindist DateTimeKind.Unspecifiedoder dargestellt DateTimeKind.Local. Und .Localist nur sinnvoll in Situationen , in denen Sie ein implizites Verständnis davon, wo der Computer, der das Ergebnis verwendet wird , positioniert. (Zum Beispiel die Workstation eines Benutzers)

Warum also DateTimeOffsetstatt einer UTC DateTime? Es geht nur um Perspektive. Verwenden wir eine Analogie - wir geben vor, Fotografen zu sein.

Stellen Sie sich vor, Sie stehen auf einer Kalender-Zeitleiste und richten eine Kamera auf eine Person auf der vor Ihnen liegenden augenblicklichen Zeitleiste. Sie richten Ihre Kamera gemäß den Regeln Ihrer Zeitzone aus - diese ändern sich regelmäßig aufgrund der Sommerzeit oder aufgrund anderer Änderungen an der gesetzlichen Definition Ihrer Zeitzone. (Sie haben keine ruhige Hand, daher ist Ihre Kamera wackelig.)

Die Person, die auf dem Foto steht, sieht den Winkel, aus dem Ihre Kamera stammt. Wenn andere Bilder machen, können sie aus verschiedenen Blickwinkeln sein. Dies ist, was der OffsetTeil der DateTimeOffsetdarstellt.

Wenn Sie Ihre Kamera also als "Eastern Time" bezeichnen, zeigen Sie manchmal von -5 und manchmal von -4. Es gibt Kameras auf der ganzen Welt, die alle mit unterschiedlichen Dingen beschriftet sind und aus verschiedenen Winkeln auf dieselbe augenblickliche Zeitachse zeigen. Einige von ihnen befinden sich direkt nebeneinander (oder übereinander). Es reicht also nicht aus, nur den Versatz zu kennen, um festzustellen, auf welche Zeitzone sich die Zeit bezieht.

Und was ist mit UTC? Nun, es ist die einzige Kamera da draußen, die garantiert eine ruhige Hand hat. Es steht auf einem Stativ und ist fest im Boden verankert. Es geht nirgendwo hin. Wir nennen seinen Perspektivwinkel den Nullpunktversatz.

Visualisierung von Momentanzeit und Kalenderzeit

Also - was sagt uns diese Analogie? Es bietet einige intuitive Richtlinien-

  • Wenn Sie die Zeit relativ zu einem bestimmten Ort darstellen, stellen Sie sie in Kalenderzeit mit a dar DateTime. Stellen Sie nur sicher, dass Sie niemals einen Kalender mit einem anderen verwechseln. Unspecifiedsollte deine Annahme sein. Localist nur nützlich, wenn man von kommt DateTime.Now. Zum Beispiel könnte ich es bekommen DateTime.Nowund in einer Datenbank speichern - aber wenn ich es abrufe, muss ich davon ausgehen, dass es ist Unspecified. Ich kann mich nicht darauf verlassen, dass mein lokaler Kalender derselbe ist, aus dem er ursprünglich stammt.

  • Wenn Sie immer sicher sein müssen, stellen Sie sicher, dass Sie die momentane Zeit darstellen. Verwenden DateTimeOffsetSie diese Option , um sie durchzusetzen, oder verwenden Sie UTC DateTimegemäß Konvention.

  • Wenn Sie einen Moment der augenblicklichen Zeit verfolgen müssen, aber auch wissen möchten: "Wann hat der Benutzer gedacht, dass er in seinem lokalen Kalender steht?" - dann müssen Sie a verwenden DateTimeOffset. Dies ist beispielsweise für Zeitnehmungssysteme sehr wichtig - sowohl aus technischen als auch aus rechtlichen Gründen.

  • Wenn Sie jemals eine zuvor aufgezeichnete Änderung ändern DateTimeOffsetmüssen, verfügen Sie nicht über genügend Informationen allein im Offset, um sicherzustellen, dass der neue Offset für den Benutzer weiterhin relevant ist. Sie müssen auch eine Zeitzonen-ID speichern (denken Sie - ich benötige den Namen dieser Kamera, damit ich ein neues Bild aufnehmen kann, auch wenn sich die Position geändert hat).

    Es sollte auch darauf hingewiesen werden, dass Noda Time eine dafür vorgesehene Darstellung ZonedDateTimehat, während die .Net-Basisklassenbibliothek nichts Ähnliches hat. Sie müssten sowohl einen DateTimeOffsetals auch einen TimeZoneInfo.IdWert speichern .

  • Gelegentlich möchten Sie eine Kalenderzeit darstellen, die lokal für "denjenigen ist, der sie betrachtet" ist. Zum Beispiel bei der Definition dessen, was heute bedeutet. Heute ist immer Mitternacht bis Mitternacht, aber diese repräsentieren eine nahezu unendliche Anzahl überlappender Bereiche auf der augenblicklichen Zeitachse. (In der Praxis haben wir eine endliche Anzahl von Zeitzonen, aber Sie können Offsets bis zum Häkchen ausdrücken.) Stellen Sie in diesen Situationen sicher, dass Sie verstehen, wie Sie entweder das "Wer fragt?" Begrenzen können. Fragen Sie bis zu einer einzelnen Zeitzone oder übersetzen Sie sie gegebenenfalls zurück in die momentane Zeit.

Hier sind ein paar andere Kleinigkeiten DateTimeOffset, die diese Analogie stützen, und einige Tipps, um sie klar zu halten:

  • Wenn Sie zwei DateTimeOffsetWerte vergleichen , werden diese vor dem Vergleich zunächst auf Nullversatz normiert. Mit anderen Worten, 2012-01-01T00:00:00+00:00und 2012-01-01T02:00:00+02:00beziehen sich auf den gleichen augenblicklichen Moment und sind daher äquivalent.

  • Wenn Sie einen Komponententest durchführen und den Versatz sicher sein müssen, testen Sie sowohl den DateTimeOffsetWert als auch die .OffsetEigenschaft separat.

  • In das .NET-Framework ist eine implizite Einwegkonvertierung integriert, mit der Sie a an einen DateTimebeliebigen DateTimeOffsetParameter oder eine beliebige Variable übergeben können. Dabei die es .Kindankommt . Wenn Sie eine UTC-Art übergeben, wird diese mit einem Nullpunktversatz übertragen. Wenn Sie jedoch eine .Localoder übergeben .Unspecified, wird davon ausgegangen, dass sie lokal ist . Das Framework sagt im Grunde: "Nun, Sie haben mich gebeten, die Kalenderzeit in die Momentanzeit umzuwandeln, aber ich habe keine Ahnung, woher diese stammt, also werde ich nur den lokalen Kalender verwenden." Dies ist ein großes Problem, wenn Sie ein nicht angegebenes Gerät DateTimeauf einen Computer mit einer anderen Zeitzone laden . (IMHO - das sollte eine Ausnahme auslösen - tut es aber nicht.)

Schamloser Stecker:

Viele Leute haben mir mitgeteilt, dass sie diese Analogie äußerst wertvoll finden, deshalb habe ich sie in meinen Pluralsight-Kurs, Datums- und Zeitgrundlagen, aufgenommen . Eine schrittweise Anleitung zur Kameraanalogie finden Sie im zweiten Modul "Context Matters" im Clip "Calendar Time vs. Instantaneous Time".

Matt Johnson-Pint
quelle
4
@ZackJannsen Wenn Sie ein DateTimeOffsetin C # haben, sollten Sie dies auf einem DATETIMEOFFSETin SQL Server beibehalten . DATETIME2oder nur DATETIME(abhängig vom erforderlichen Bereich) sind für reguläre DateTimeWerte in Ordnung . Ja - Sie können eine Ortszeit aus einer beliebigen Paarung von Zeitzone + dto oder utc auflösen. Der Unterschied besteht darin, dass Sie die Regeln immer mit jeder Auflösung berechnen möchten oder sie vorberechnen möchten. In vielen Fällen (manchmal aus rechtlichen Gründen) ist ein DTO die bessere Wahl.
Matt Johnson-Pint
3
@ZackJannsen Für den zweiten Teil Ihrer Frage würde ich empfehlen, so viel wie möglich serverseitig zu tun. Javascript eignet sich nicht so gut für die Zeitzonenberechnung. Wenn Sie dies tun müssen, verwenden Sie eine dieser Bibliotheken . Aber die Serverseite ist am besten. Wenn Sie weitere detailliertere Fragen haben, starten Sie bitte eine neue SO-Frage für diese und ich werde antworten, wenn ich kann. Vielen Dank.
Matt Johnson-Pint
4
@ JoaoLeme - Das hängt davon ab, woher Sie es haben. Sie haben Recht, wenn Sie DateTimeOffset.Nowauf dem Server sagen , erhalten Sie tatsächlich den Offset des Servers. Der Punkt ist, dass der DateTimeOffsetTyp diesen Versatz beibehalten kann. Sie können dies genauso einfach auf dem Client tun, an den Server senden, und dann würde Ihr Server den Offset des Clients kennen.
Matt Johnson-Pint
8
Ich liebe die Kamera-Analogie wirklich.
Sachin Kainth
2
Ja das ist richtig. Mit der Ausnahme, dass DTO als Paar (Ortszeit, Versatz) und nicht als Paar (UTC-Zeit, Versatz) gespeichert wird. Mit anderen Worten, der Versatz von UTC spiegelt sich bereits in der Ortszeit wider. Um wieder in utc zu konvertieren, invertieren Sie das Vorzeichen des Offsets und wenden Sie es auf die Ortszeit an.
Matt Johnson-Pint
328

Von Microsoft:

Diese Verwendungen für DateTimeOffset-Werte sind weitaus häufiger als für DateTime-Werte. Daher sollte DateTimeOffset als Standarddatum und -zeit für die Anwendungsentwicklung betrachtet werden.

Quelle: "Auswahl zwischen DateTime, DateTimeOffset, TimeSpan und TimeZoneInfo" , MSDN

Wir verwenden DateTimeOffsetfür fast alles, da unsere Anwendung bestimmte Zeitpunkte behandelt (z. B. als ein Datensatz erstellt / aktualisiert wurde). Als Randnotiz verwenden wir auch DATETIMEOFFSETin SQL Server 2008.

Ich sehe es DateTimeals nützlich an, wenn Sie sich nur mit Datumsangaben, nur mit Uhrzeiten oder generisch mit beiden befassen möchten. Wenn Sie beispielsweise einen Alarm haben, den Sie jeden Tag um 7 Uhr morgens auslösen möchten, können Sie diesen in einer DateTimeVerwendung DateTimeKindvon speichern, Unspecifiedda er unabhängig von der Sommerzeit um 7 Uhr morgens ausgelöst werden soll. Wenn Sie jedoch den Verlauf von Alarmereignissen darstellen möchten, würden Sie verwenden DateTimeOffset.

Seien Sie vorsichtig, wenn Sie eine Mischung aus DateTimeOffsetund DateTimeinsbesondere beim Zuweisen und Vergleichen zwischen den Typen verwenden. Vergleichen Sie außerdem nur DateTimeInstanzen, die identisch sind, DateTimeKindda DateTimebeim Vergleich der Zeitzonenversatz ignoriert wird.

Lehm
quelle
146
Die akzeptierte Antwort ist zu lang und die Analogie ist angespannt. Dies ist eine viel bessere und präzisere Antwort, IMO.
Nexus sagt
10
Ich sage nur, dass mir diese Antwort auch gefällt und ich stimme zu. Obwohl im letzten Teil - auch wenn sichergestellt Kindwird, dass sie gleich sind -, könnte der Vergleich fehlerhaft sein. Wenn beide Seiten haben DateTimeKind.Unspecified, wissen Sie nicht wirklich, dass sie aus derselben Zeitzone stammen. Wenn beide Seiten sind DateTimeKind.Local, die meisten Vergleiche fein sein werden, aber man konnte immer noch Fehler haben ist die eine Seite in der lokalen Zeitzone nicht eindeutig ist. Wirklich nur DateTimeKind.UtcVergleiche sind kinderleicht und ja, DateTimeOffsetwird normalerweise bevorzugt. (Prost!)
Matt Johnson-Pint
1
+1 Ich möchte noch hinzufügen: Der von Ihnen ausgewählte Datentyp sollte Ihre Absicht widerspiegeln. Verwenden Sie DateTimeOffset nicht überall, nur weil. Wenn der Versatz für Ihre Berechnungen und das Lesen von / Beständig in die Datenbank von Bedeutung ist, verwenden Sie DateTimeOffset. Wenn es keine Rolle spielt, verwenden Sie DateTime, damit Sie (nur durch Betrachten des Datentyps) verstehen, dass der Versatz keine Peilung haben sollte und die Zeiten relativ zur Lokalität des Servers / Computers bleiben sollten, auf dem Ihr C # -Code ausgeführt wird.
MikeTeeVee
"Wenn Sie jedoch den Verlauf von Alarmereignissen darstellen möchten, verwenden Sie DateTimeOffset." Möchten Sie erklären, warum Sie das für richtig halten? Ich könnte das ausfüllen, indem ich alle Informationen auf dieser Seite lese, aber vielleicht könnten Sie solche Informationen auch auf leicht lesbare Weise bereitstellen. Dies ist eine sehr lesbare Antwort.
Barrosy
77

DateTime kann nur zwei unterschiedliche Zeiten speichern, die Ortszeit und die UTC. Die Kind- Eigenschaft gibt an, welche.

DateTimeOffset erweitert dies, indem es Ortszeiten von überall auf der Welt speichern kann. Es speichert auch den Versatz zwischen dieser Ortszeit und UTC. Beachten Sie, dass DateTime dies nur tun kann, wenn Sie Ihrer Klasse ein zusätzliches Mitglied hinzufügen, um diesen UTC-Offset zu speichern. Oder arbeiten Sie immer nur mit UTC. Was an sich schon eine gute Idee ist.

Hans Passant
quelle
33

Es gibt einige Stellen, an denen DateTimeOffsetSinn macht. Eine ist, wenn Sie mit wiederkehrenden Ereignissen und Sommerzeit zu tun haben. Nehmen wir an, ich möchte jeden Tag um 9 Uhr einen Wecker stellen. Wenn ich die Regel "Als UTC speichern, als Ortszeit anzeigen" verwende, wird der Alarm zu einer anderen Zeit ausgelöst, wenn die Sommerzeit wirksam ist.

Es gibt wahrscheinlich andere, aber das obige Beispiel ist tatsächlich eines, auf das ich in der Vergangenheit DateTimeOffsetgestoßen bin (dies war vor dem Hinzufügen zur BCL - meine damalige Lösung bestand darin, die Zeit explizit in der lokalen Zeitzone zu speichern und zu speichern die Zeitzoneninformationen daneben: im Grunde genommen, was DateTimeOffsetintern funktioniert).

Dean Harding
quelle
12
DateTimeOffset behebt das DST-Problem nicht
JarrettV
2
Die Verwendung der TimeZoneInfo-Klasse enthält Regeln für die Sommerzeit. Wenn Sie mit .net 3.5 oder höher arbeiten, verwenden Sie entweder die Klassen TimeZone oder TimeZoneInfo, um Daten zu verarbeiten, die die Sommerzeit in Verbindung mit dem Zeitzonenversatz verarbeiten müssen.
Zack Jannsen
1
Ja, gutes Beispiel für eine Ausnahme (die Alarm-App), aber wenn die Uhrzeit wichtiger ist als das Datum, sollten Sie diese wirklich in Ihrer Zeitplandatenstruktur für die Anwendung speichern, dh Vorkommenstyp = Täglich und Uhrzeit = 09:00. Hier geht es darum, dass der Entwickler wissen muss, welche Art von Datum er aufzeichnet, berechnet oder den Benutzern präsentiert. Insbesondere Apps sind in der Regel globaler, da wir das Internet als Standard und große App-Stores haben, für die wir Software schreiben können. Als Nebenknoten möchte ich auch, dass Microsoft eine separate Datums- und Uhrzeitstruktur hinzufügt.
Tony Wall
3
Zusammenfassung der Kommentare von Jarrett und Zack: Es klingt so, als würde DateTimeOffset allein das DST-Problem nicht behandeln, aber die Verwendung von DateTimeOffset in Verbindung mit TimeZoneInfo wird es behandeln. Dies unterscheidet sich nicht von DateTime, wo Art Utc ist. In beiden Fällen muss ich die Zeitzone (nicht nur den Versatz) des Kalenders kennen, auf den ich den Moment projiziere. (Ich kann das im Profil eines Benutzers speichern oder es, wenn möglich, vom Client (z. B. Windows) erhalten.) Hört sich richtig an?
Jeremy Cook
"Es gibt einige Stellen, an denen DateTimeOffset Sinn macht." --- Wahrscheinlich macht es öfter Sinn als nicht.
Ronnie Overby
23

Der wichtigste Unterschied besteht darin, dass DateTime keine Zeitzoneninformationen speichert, während DateTimeOffset dies tut.

Obwohl DateTime zwischen UTC und Local unterscheidet, ist absolut kein expliziter Zeitzonenversatz damit verbunden. Wenn Sie irgendeine Art von Serialisierung oder Konvertierung durchführen, wird die Zeitzone des Servers verwendet. Selbst wenn Sie manuell eine Ortszeit erstellen, indem Sie Minuten hinzufügen, um eine UTC-Zeit zu versetzen, können Sie im Serialisierungsschritt dennoch ein Bit erhalten, da (aufgrund des Fehlens eines expliziten Versatzes in DateTime) der Zeitzonenversatz des Servers verwendet wird.

Wenn Sie beispielsweise einen DateTime-Wert mit Kind = Local mithilfe von Json.Net und einem ISO-Datumsformat serialisieren, erhalten Sie eine Zeichenfolge wie 2015-08-05T07:00:00-04. Beachten Sie, dass der letzte Teil (-04) nichts mit Ihrer DateTime oder einem Versatz zu tun hat, den Sie zur Berechnung verwendet haben. Es handelt sich lediglich um den Zeitzonenversatz des Servers.

In der Zwischenzeit enthält DateTimeOffset den Offset explizit. Möglicherweise enthält es nicht den Namen der Zeitzone, aber zumindest den Versatz. Wenn Sie ihn serialisieren, erhalten Sie den explizit enthaltenen Versatz in Ihrem Wert anstelle der Ortszeit des Servers.

Triynko
quelle
14
Bei all den obigen Antworten frage ich mich, warum sich niemand die Mühe gemacht hat, Ihren einzigen Satz zu schreiben, der alles zusammenfasstThe most important distinction is that DateTime does not store time zone information, while DateTimeOffset does.
Korayem,
9
DateTimeOffset speichert KEINE Zeitzoneninformationen. Das MS-Dokument mit dem Titel "Auswahl zwischen DateTime, DateTimeOffset, TimeSpan und TimeZoneInfo" gibt Folgendes an: "Ein DateTimeOffset-Wert ist nicht an eine bestimmte Zeitzone gebunden, kann jedoch aus einer Vielzahl von Zeitzonen stammen." Das heißt, DateTimeOffset ist die Zeitzone AWARE, die den Versatz von UTC enthält, was den Unterschied ausmacht, und deshalb ist es die von MS empfohlene Standardklasse, wenn es um die App-Entwicklung geht, die sich mit Datumsinformationen befasst. Wenn Sie sich wirklich darum kümmern, aus welcher bestimmten Zeitzone die Daten stammen, müssen Sie diese separat aufbewahren
stonedauwg
1
Ja, aber wie an vielen Orten gezeigt wurde, sagen + oder - Stunden nichts darüber aus, in welcher Zeitzone Sie sich befanden, und sind letztendlich nutzlos. Je nachdem, was Sie tun müssen, können Sie eine Datums- / Uhrzeitangabe genauso gut wie Kind.Unspecified speichern und dann die ID der Zeitzone speichern, und ich denke, Sie sind tatsächlich besser dran.
Arwin
13

Dieser Code von Microsoft erklärt alles:

// Find difference between Date.Now and Date.UtcNow
  date1 = DateTime.Now;
  date2 = DateTime.UtcNow;
  difference = date1 - date2;
  Console.WriteLine("{0} - {1} = {2}", date1, date2, difference);

  // Find difference between Now and UtcNow using DateTimeOffset
  dateOffset1 = DateTimeOffset.Now;
  dateOffset2 = DateTimeOffset.UtcNow;
  difference = dateOffset1 - dateOffset2;
  Console.WriteLine("{0} - {1} = {2}", 
                    dateOffset1, dateOffset2, difference);
  // If run in the Pacific Standard time zone on 4/2/2007, the example
  // displays the following output to the console:
  //    4/2/2007 7:23:57 PM - 4/3/2007 2:23:57 AM = -07:00:00
  //    4/2/2007 7:23:57 PM -07:00 - 4/3/2007 2:23:57 AM +00:00 = 00:00:00
Mojtaba
quelle
9

Die meisten Antworten sind gut, aber ich dachte darüber nach, weitere Links zu MSDN hinzuzufügen, um weitere Informationen zu erhalten

dekdev
quelle
7

Ein Hauptunterschied besteht darin, dass DateTimeOffsetin Verbindung mit TimeZoneInfoder Konvertierung in Ortszeiten in anderen als den aktuellen Zeitzonen verwendet werden kann.

Dies ist nützlich für eine Serveranwendung (z. B. ASP.NET), auf die Benutzer in verschiedenen Zeitzonen zugreifen.

Joe
quelle
3
@Bugeo Bugeo ist wahr, aber es besteht ein Risiko. Sie können zwei DateTimes vergleichen, indem Sie zuerst jeweils "ToUniversalTime" aufrufen. Wenn Sie genau einen Wert im Vergleich haben, nämlich DateTimeKind = Nicht angegeben, schlägt Ihre Strategie fehl. Dieses Potenzial für einen Fehler ist ein Grund, DateTimeOffset über DateTime zu berücksichtigen, wenn Konvertierungen in die Ortszeit erforderlich sind.
Zack Jannsen
Wie oben denke ich, dass Sie in diesem Szenario besser dran sind, die TimeZoneId zu speichern, als DateTimeOffset zu verwenden, was letztendlich nichts bedeutet.
Arwin
2

Die einzige negative Seite von DateTimeOffset, die ich sehe, ist, dass Microsoft (von Natur aus) "vergessen" hat, es in seiner XmlSerializer-Klasse zu unterstützen. Inzwischen wurde es jedoch der XmlConvert-Dienstprogrammklasse hinzugefügt.

XmlConvert.ToDateTimeOffset

XmlConvert.ToString

Ich sage, verwenden Sie DateTimeOffset und TimeZoneInfo wegen aller Vorteile. Seien Sie vorsichtig, wenn Sie Entitäten erstellen, die in oder aus XML serialisiert werden oder werden können (dann alle Geschäftsobjekte).

Tony Wall
quelle