Ich habe eine Website, die in einer anderen Zeitzone gehostet wird als die Benutzer, die die Anwendung verwenden. Darüber hinaus können Benutzer eine bestimmte Zeitzone haben. Ich habe mich gefragt, wie andere SO-Benutzer und -Anwendungen dies angehen. Der offensichtlichste Teil ist, dass in der Datenbank Datum und Uhrzeit in UTC gespeichert sind. Auf dem Server sollten alle Datums- und Uhrzeitangaben in UTC behandelt werden. Ich sehe jedoch drei Probleme, die ich zu überwinden versuche:
Abrufen der aktuellen Uhrzeit in UTC (einfach zu lösen mit
DateTime.UtcNow
).Datum und Uhrzeit aus der Datenbank abrufen und dem Benutzer anzeigen. Es gibt möglicherweise viele Anrufe, um Daten in verschiedenen Ansichten zu drucken. Ich dachte an eine Ebene zwischen der Ansicht und den Controllern, die dieses Problem lösen könnte. Oder mit einer benutzerdefinierten Erweiterungsmethode
DateTime
(siehe unten). Der Hauptnachteil ist, dass an jedem Ort, an dem eine Datums- / Uhrzeitangabe in einer Ansicht verwendet wird, die Erweiterungsmethode aufgerufen werden muss!Dies würde auch die Verwendung von etwas wie dem erschweren
JsonResult
. Man konnte nicht mehr einfach anrufenJson(myEnumerable)
, es müsste seinJson(myEnumerable.Select(transformAllDates))
. Vielleicht könnte AutoMapper in dieser Situation helfen?Eingaben vom Benutzer erhalten (Lokal zu UTC). Wenn Sie beispielsweise ein Formular mit einem Datum veröffentlichen möchten, müssen Sie das Datum zuvor in UTC konvertieren. Das erste, was mir in den Sinn kommt, ist das Erstellen eines Brauchs
ModelBinder
.
Hier sind die Erweiterungen, die ich in den Ansichten verwenden wollte:
public static class DateTimeExtensions
{
public static DateTime UtcToLocal(this DateTime source,
TimeZoneInfo localTimeZone)
{
return TimeZoneInfo.ConvertTimeFromUtc(source, localTimeZone);
}
public static DateTime LocalToUtc(this DateTime source,
TimeZoneInfo localTimeZone)
{
source = DateTime.SpecifyKind(source, DateTimeKind.Unspecified);
return TimeZoneInfo.ConvertTimeToUtc(source, localTimeZone);
}
}
Ich würde denken, dass der Umgang mit Zeitzonen so häufig vorkommt, wenn man bedenkt, dass viele Anwendungen jetzt Cloud-basiert sind und die Ortszeit des Servers stark von der erwarteten Zeitzone abweichen kann.
Wurde dies schon einmal elegant gelöst? Fehlt mir etwas? Ideen und Gedanken werden sehr geschätzt.
EDIT: Um einige Verwirrung zu beseitigen, dachte ich, einige weitere Details hinzuzufügen. Im Moment geht es nicht darum, wie UTC-Zeiten in der Datenbank gespeichert werden, sondern vielmehr darum, von UTC-> Lokal und Lokal-> UTC zu wechseln. Wie @Max Zerbini betont, ist es natürlich klug, den UTC-> Lokalen Code in die Ansicht aufzunehmen, aber ist die DateTimeExtensions
wirklich die Antwort? Ist es sinnvoll, beim Abrufen von Eingaben vom Benutzer Datumsangaben als Ortszeit des Benutzers zu akzeptieren (da dies von JS verwendet wird) und dann eine ModelBinder
zu UTC-Transformation zu verwenden? Die Zeitzone des Benutzers wird in der Datenbank gespeichert und kann leicht abgerufen werden.
quelle
ModelBinder
.Antworten:
Nicht, dass dies eine Empfehlung wäre, sondern vielmehr ein Paradigma, aber die aggressivste Art und Weise, wie ich mit Zeitzoneninformationen in einer Web-App (die nicht nur für ASP.NET MVC verfügbar ist) umgegangen bin, war die folgende:
Alle Datums- und Uhrzeitangaben auf dem Server sind UTC. Das bedeutet, wie Sie sagten ,
DateTime.UtcNow
.Versuchen Sie, dem Client zu vertrauen, der so wenig wie möglich Daten an den Server übergibt. Wenn Sie beispielsweise "jetzt" benötigen, erstellen Sie kein Datum auf dem Client und übergeben Sie es dann an den Server. Erstellen Sie entweder ein Datum in Ihrem GET und übergeben Sie es an das ViewModel oder auf POST
DateTime.UtcNow
.Bisher ziemlich normaler Tarif, aber hier wird es „interessant“.
Wenn Sie ein Datum vom Client akzeptieren müssen, verwenden Sie Javascript, um sicherzustellen, dass die Daten, die Sie auf dem Server veröffentlichen, in UTC vorliegen. Der Kunde weiß, in welcher Zeitzone er sich befindet, sodass er die Zeiten mit angemessener Genauigkeit in UTC umrechnen kann.
Beim Rendern von Ansichten verwendeten sie das HTML5-
<time>
Element. Sie renderten niemals Datumsangaben direkt im ViewModel. Es wurde alsHtmlHelper
Erweiterung implementiert , so etwas wieHtml.Time(Model.when)
. Es würde rendern<time datetime='[utctime]' data-date-format='[datetimeformat]'></time>
.Dann würden sie Javascript verwenden, um die UTC-Zeit in die Ortszeit des Clients zu übersetzen. Das Skript würde alle
<time>
Elemente finden und diedate-format
data-Eigenschaft verwenden, um das Datum zu formatieren und den Inhalt des Elements zu füllen.Auf diese Weise mussten sie nie die Zeitzone eines Kunden verfolgen, speichern oder verwalten. Dem Server war es egal, in welcher Zeitzone sich der Client befand, und er musste auch keine Zeitzonenübersetzungen durchführen. Es spuckte einfach UTC aus und ließ den Kunden das in etwas Vernünftiges umwandeln. Dies ist vom Browser aus einfach, da er weiß, in welcher Zeitzone er sich befindet. Wenn der Client seine Zeitzone ändert, aktualisiert sich die Webanwendung automatisch. Das einzige, was sie gespeichert haben, war die Datums- / Uhrzeitformatzeichenfolge für das Gebietsschema des Benutzers.
Ich sage nicht, dass es der beste Ansatz war, aber es war ein anderer, den ich vorher nicht gesehen hatte. Vielleicht finden Sie daraus einige interessante Ideen.
quelle
<time>
Idee ziemlich cool. Der einzige Nachteil ist, dass ich das gesamte DOM nach diesen Elementen abfragen muss ist nicht gerade schön, nur um Daten zu transformieren (was ist mit JS deaktiviert?).Nach mehreren Rückmeldungen ist hier meine endgültige Lösung, die meiner Meinung nach sauber und einfach ist und Probleme mit der Sommerzeit abdeckt.
1 - Wir übernehmen die Konvertierung auf Modellebene. In der Model-Klasse schreiben wir also:
2 - In einem globalen Helfer erstellen wir unsere benutzerdefinierte Funktion "ToLocalTime":
3 - Wir können dies weiter verbessern, indem wir die Zeitzonen-ID in jedem Benutzerprofil speichern, damit wir sie aus der Benutzerklasse abrufen können, anstatt die konstante "China Standard Time" zu verwenden:
4 - Hier können wir die Liste der Zeitzonen abrufen, die dem Benutzer zur Auswahl aus einer Dropdown-Box angezeigt werden sollen:
Jetzt, um 9:25 Uhr in China, Website in den USA gehostet, Datum in UTC in der Datenbank gespeichert, hier ist das Endergebnis:
BEARBEITEN
Vielen Dank an Matt Johnson für den Hinweis auf die Schwachstellen der ursprünglichen Lösung und für das Löschen des ursprünglichen Beitrags, aber Probleme beim Erhalten des richtigen Code-Anzeigeformats. Es stellte sich heraus, dass der Editor Probleme beim Mischen von "Aufzählungszeichen" mit "Vorcode" hat, also ich entfernte die Bulles und es war in Ordnung.
quelle
Im Abschnitt "Ereignisse" auf sf4answers geben Benutzer eine Adresse für ein Ereignis sowie ein Startdatum und ein optionales Enddatum ein. Diese Zeiten werden in einen
datetimeoffset
In-SQL-Server übersetzt, der den Versatz von UTC berücksichtigt.Dies ist das gleiche Problem, mit dem Sie konfrontiert sind (obwohl Sie einen anderen Ansatz wählen, indem Sie es verwenden
DateTime.UtcNow
). Sie haben einen Ort und müssen eine Zeit von einer Zeitzone in eine andere übersetzen.Es gibt zwei wichtige Dinge, die ich getan habe und die für mich funktioniert haben. Verwenden Sie zunächst immer die
DateTimeOffset
Struktur . Es berücksichtigt den Offset von UTC und wenn Sie diese Informationen von Ihrem Kunden erhalten können, erleichtert es Ihnen das Leben ein wenig.Zweitens können Sie bei der Durchführung der Übersetzungen unter der Annahme, dass Sie den Ort / die Zeitzone kennen, in der sich der Client befindet, die Zeitzonendatenbank für öffentliche Informationen verwenden , um eine Zeit von UTC in eine andere Zeitzone zu übersetzen (oder, wenn Sie so wollen, zwischen zwei zu triangulieren Zeitzonen). Das Tolle an der tz-Datenbank (manchmal auch als Olson-Datenbank bezeichnet ) ist, dass sie die Änderungen der Zeitzonen im Laufe der Geschichte berücksichtigt. Das Erhalten eines Versatzes ist eine Funktion des Datums, an dem Sie den Versatz erhalten möchten (siehe den Energy Policy Act von 2005, der die Daten geändert hat, an denen die Sommerzeit in den USA in Kraft tritt ).
Mit der vorliegenden Datenbank können Sie die .NET-API von ZoneInfo (tz-Datenbank / Olson-Datenbank) verwenden . Beachten Sie, dass es keine Binärdistribution gibt. Sie müssen die neueste Version herunterladen und selbst kompilieren.
Zum Zeitpunkt dieses Schreibens werden derzeit alle Dateien in der neuesten Datenverteilung analysiert (ich habe sie am 25. September gegen die Datei ftp://elsie.nci.nih.gov/pub/tzdata2011k.tar.gz ausgeführt) . 2011; im März 2017 erhalten Sie es über https://iana.org/time-zones oder über ftp://fpt.iana.org/tz/releases/tzdata2017a.tar.gz ).
Bei sf4answers wird die Adresse nach Erhalt der Adresse in eine Längen- / Breitengradkombination geokodiert und dann an einen Webdienst eines Drittanbieters gesendet, um eine Zeitzone abzurufen, die einem Eintrag in der tz-Datenbank entspricht. Von dort werden die Start- und Endzeiten in
DateTimeOffset
Instanzen mit dem richtigen UTC-Offset konvertiert und dann in der Datenbank gespeichert.Der Umgang damit auf SO und Websites hängt von der Zielgruppe und dem ab, was Sie anzeigen möchten. Wenn Sie bemerken, dass auf den meisten sozialen Websites (und SO sowie im Abschnitt "Ereignisse" auf sf4answers) Ereignisse in relativer Zeit angezeigt werden oder wenn ein absoluter Wert verwendet wird, ist dies normalerweise UTC.
Wenn Ihr Publikum jedoch Ortszeiten erwartet, ist die Verwendung
DateTimeOffset
zusammen mit einer Erweiterungsmethode, bei der die Zeitzone für die Konvertierung benötigt wird, in Ordnung. Der SQL-Datentyp wirddatetimeoffset
in .NET übersetzt, sodassDateTimeOffset
Sie die universelle Zeit für die Verwendung derGetUniversalTime
Methode erhalten . Von dort aus verwenden Sie einfach die Methoden derZoneInfo
Klasse, um von UTC in die Ortszeit zu konvertieren (Sie müssen ein wenig arbeiten, um sie in eine zu verwandelnDateTimeOffset
, aber es ist einfach genug, dies zu tun).Wo soll die Transformation durchgeführt werden? Das sind Kosten, die Sie irgendwo bezahlen müssen , und es gibt keinen "besten" Weg. Ich würde mich jedoch für die Ansicht entscheiden, wobei der Zeitzonenversatz Teil des Ansichtsmodells ist, das der Ansicht präsentiert wird. Auf diese Weise müssen Sie Ihr Ansichtsmodell nicht ändern, wenn sich die Anforderungen für die Ansicht ändern, um die Änderung zu berücksichtigen. Sie
JsonResult
würden einfach ein Modell mit dem und dem Versatz enthalten.IEnumerable<T>
Auf der Eingabeseite mit einem Modellbinder? Ich würde absolut keinen Weg sagen. Sie können nicht garantieren, dass alle Daten (jetzt oder in Zukunft) auf diese Weise transformiert werden müssen. Es sollte eine explizite Funktion Ihres Controllers sein, diese Aktion auszuführen. Wenn sich die Anforderungen ändern, müssen Sie nicht eine oder mehrere
ModelBinder
Instanzen optimieren , um Ihre Geschäftslogik anzupassen. und es ist Geschäftslogik, was bedeutet, dass es in der Steuerung sein sollte.quelle
datetimeoffset
in SQL Server undDateTimeOffset
in .NET hier nicht genug betonen , sie vereinfachen die Dinge wirklich enorm), die .NET meiner Meinung nach nicht angemessen handhabt überhaupt aus den oben genannten Gründen. Wenn Sie ein Datum in NYC haben, das 2003 eingegeben wurde, und dann möchten, dass es 2011 in ein Datum in LA übersetzt wird, schlägt .NET in diesem Fall schwer fehl.DateTimeOffset
Sie dies sehr mildern werden, IMO).Dies ist nur meine Meinung. Ich denke, dass die MVC-Anwendung das Problem der Datenpräsentation vom Datenmodellmanagement trennen sollte. Eine Datenbank kann Daten in lokaler Serverzeit speichern, es ist jedoch eine Aufgabe der Präsentationsschicht, die Datenzeit mithilfe der Zeitzone des lokalen Benutzers zu rendern. Dies scheint mir das gleiche Problem wie I18N und Zahlenformat für verschiedene Länder zu sein. In Ihrem Fall sollte Ihre Anwendung die
Culture
Zeitzone des Benutzers erkennen und die Ansicht ändern, in der unterschiedliche Text-, Zahlen- und Datumsdarstellungen angezeigt werden. Die gespeicherten Daten können jedoch dasselbe Format haben.quelle
DateTimeExtensions
.Erstellen Sie für die Ausgabe eine Anzeige- / Editorvorlage wie diese
Sie können sie basierend auf Attributen in Ihrem Modell binden, wenn nur bestimmte Modelle diese Vorlagen verwenden sollen.
Weitere Informationen zum Erstellen benutzerdefinierter Editorvorlagen finden Sie hier und hier .
Da Sie möchten, dass es sowohl für die Eingabe als auch für die Ausgabe funktioniert, würde ich alternativ empfehlen, ein Steuerelement zu erweitern oder sogar ein eigenes zu erstellen. Auf diese Weise können Sie sowohl die Ein- als auch die Ausgaben abfangen und den Text / Wert nach Bedarf konvertieren.
Dieser Link wird Sie hoffentlich in die richtige Richtung bringen, wenn Sie diesen Weg gehen möchten.
In jedem Fall ist es ein bisschen Arbeit, wenn Sie eine elegante Lösung suchen. Auf der positiven Seite, wenn Sie es einmal getan haben, können Sie es für die zukünftige Verwendung in Ihrer Codebibliothek aufbewahren!
quelle
DisplayFor
undEditorFor
und es wird jedes Mal arbeiten. +1Dies ist wahrscheinlich ein Vorschlaghammer, um eine Nuss zu knacken, aber Sie könnten eine Ebene zwischen der Benutzeroberfläche und der Business-Ebene einfügen, die Datumszeiten in zurückgegebenen Objektdiagrammen transparent in die Ortszeit und in eingegebenen Datums- / Uhrzeitparametern in UTC konvertiert.
Ich stelle mir vor, dass dies mit PostSharp oder einer Inversion des Kontrollcontainers erreicht werden könnte.
Persönlich würde ich einfach Ihre Datenzeiten explizit in der Benutzeroberfläche konvertieren ...
quelle