lokale Zeit bis zoneddatetime

8

Ich habe eine Java 8 Spring Web App, die mehrere Regionen unterstützt. Ich muss Kalendertermine für einen Kundenstandort erstellen. Nehmen wir an, mein Web- und Postgres-Server wird in der MST-Zeitzone gehostet (aber ich denke, es könnte überall sein, wenn wir in die Cloud gehen). Aber der Kunde ist in EST. Nach einigen Best Practices, die ich gelesen habe, dachte ich, dass ich alle Datumsangaben im UTC-Format speichern würde. Alle Datumszeitfelder in der Datenbank werden als TIMESTAMP deklariert.

Also hier ist, wie ich eine LocalDateTime nehme und in UTC umwandele:

%Vor%

Wenn zum Beispiel jetzt nach einem Datum gesucht wird, muss ich vom angeforderten Datum in UTC konvertieren, die Ergebnisse abrufen und in die Benutzerzeitzone konvertieren (in der Datenbank gespeichert):

%Vor%

Ich bin mir nicht sicher, ob das der richtige Ansatz ist. Ich frage mich auch, ob ich, weil ich von LocalDateTime zu ZonedDateTime und umgekehrt gehe, irgendwelche Zeitzoneninformationen verliere.

Hier ist, was ich sehe und es scheint mir nicht richtig. Wenn ich die LocalDateTime von der Benutzeroberfläche erhalte, bekomme ich Folgendes:

%Vor%

Die ZoneDateTime =

%Vor%

Und wenn ich diesen konvertierten Wert meinem Termin LocalDateTime zuordne, speichere ich:

%Vor%

Ich fühle mich wie, weil ich in LocalDateTime speichern Ich verliere die Zeitzone, die ich in ZoneDateTime umgewandelt habe.

Soll ich meine Entität (Termin) statt "LocalDateTime" "ZonedDateTime" verwenden, damit Postgres diese Informationen nicht verliert?

---------------- Bearbeiten ----------------

Nach Basils ausgezeichneter Antwort merkte ich, dass ich den Luxus habe, mich nicht um die Zeitzone der Benutzer zu kümmern - alle Termine sind gegen einen bestimmten Ort, so dass ich alle Datumsangaben als UTC speichern und sie dann in die Zeitzone des Orts umwandeln kann. Ich machte die folgende Frage

    
sonoerin 05.04.2016, 04:46
quelle

1 Antwort

42

Postgres hat keinen solchen Datentyp wie TIMESTAMP . Postgres hat zwei Arten für das Datum und die Uhrzeit: TIMESTAMP WITH TIME ZONE und% Code%. Diese Typen haben ein sehr unterschiedliches Verhalten hinsichtlich der Zeitzoneninformationen.

  • Der TIMESTAMP WITHOUT TIME ZONE -Typ verwendet alle Informationen zum Offset oder zur Zeitzone, um das Datum und die Uhrzeit an UTC anzupassen dieser Offset- oder Zeitzone; Postgres speichert niemals die Offset- / Zoneninformationen.
  • Der WITH -Typ ignoriert alle möglicherweise vorhandenen Offset- oder Zoneninformationen.

Sie möchten praktisch immer den WITHOUT -Typ, wie hier hier erklärt von Experten David E. Wheeler. % Co_de% macht nur dann Sinn, wenn Sie die vage Vorstellung von einem Datum-Zeit-Punkt und nicht von einem festen Punkt auf der Timeline haben. Beispiel: "Weihnachten beginnt dieses Jahr um 2016-12-25T00: 00: 00" würde in WITH gespeichert werden, da es für beliebige Zeitzonen gilt und noch keinem zugewiesen wurde einzelne Zeitzone, um einen tatsächlichen Moment auf der Timeline zu erhalten. Wenn die Elfen des Weihnachtsmanns die Startzeit für Eugene Oregon US verfolgen würden, würden sie den WITHOUT -Typ und einen Input verwenden, der einen Offset oder eine Zeitzone wie WITHOUT enthält, die in Postgres als WITH gespeichert werden co_de% bedeutet Zulu oder UTC).

Das Äquivalent von Postgres ' 2016-12-25T00:00:00-08:00 in java.time ist 2016-12-25T08:00.00Z . Da Sie in UTC arbeiten wollten (eine gute Sache), sollten Sie nicht Z verwenden (eine schlechte Sache). Das könnte der Hauptpunkt für Verwirrung und Ärger für dich sein. Sie denken immer daran, TIMESTAMP WITHOUT TIME ZONE oder java.time.LocalDateTime zu verwenden, aber Sie sollten keines davon verwenden; Stattdessen sollten Sie LocalDateTime verwenden (siehe unten).

  

Ich frage mich auch, ob ich, weil ich von LocalDateTime nach ZonedDateTime und umgekehrt gehe, irgendwelche Zeitzoneninformationen verliere.

Tatsächlich bist du es. Der gesamte Punkt auf LocalDateTime dient zum Verlieren der Zeitzoneninformationen. Daher verwenden wir diese Klasse in den meisten Apps nur selten. Wieder das Weihnachtsbeispiel. Oder ein anderes Beispiel: "Unternehmenspolitik: Alle unsere Fabriken auf der ganzen Welt nehmen um 12:30 Uhr Mittag zu Mittag". Das wäre ZonedDateTime und für ein bestimmtes Datum Instant . Aber das hat keine wirkliche Bedeutung, kein tatsächlicher Punkt auf der Timeline, bis Sie eine Zeitzone anwenden, um eine LocalDateTime . Diese Mittagspause wird an verschiedenen Stellen auf der Timeline in der Fabrik in Delhi stattfinden als in der Fabrik in Düsseldorf und anders in der Fabrik in Detroit.

Das Wort "Local" in LocalTime kann kontraintuitiv sein, da es keine bestimmte Lokalität bedeutet. Wenn Sie "Local" in einem Klassennamen lesen, denken Sie "Kein Moment ... nicht auf der Timeline ... nur eine verschwommene Idee über eine Art-sorta Datum-Zeit".

Ihre Server sollten in ihrer Betriebssystem-Zeitzone fast immer auf UTC eingestellt sein. Aber Ihre Programmierung sollte niemals von dieser Externalität abhängen, da es für einen Sysadmin einfach zu ändern ist oder für jede andere Java-App, die aktuelle Standardzeitzone innerhalb der JVM zu ändern. Geben Sie daher immer die gewünschte / erwartete Zeitzone an. (Gleiches gilt übrigens für LocalDateTime .)

Upshot:

  • Sie arbeiten viel zu hart.
  • Programmierer / Systemadministratoren müssen lernen, "global zu denken, lokal zu präsentieren".

Denken Sie während des Arbeitstages, während Sie Ihren Geek-Schutzhelm tragen, an UTC. Erst am Ende des Tages, wenn Sie zu Ihrem Laien gewechselt haben, sollten Sie wieder an die Ortszeit Ihrer Stadt denken.

Ihre Geschäftslogik sollte sich auf UTC konzentrieren. Ihr Datenbankspeicher, Ihre Geschäftslogik, Datenaustausch, Serialisierung, Protokollierung und Ihr eigenes Denken sollten alle in der UTC-Zeitzone (und übrigens im 24-Stunden-Format) erfolgen. Wenn Sie den Benutzern Daten präsentieren, wenden Sie nur eine bestimmte Zeitzone an. Stellen Sie sich gezonte Datum-Zeiten als eine externe Sache vor, nicht als funktionierender Teil der Interna Ihrer App.

Auf der Java-Seite verwenden Sie ZonedDateTime (a Moment in der Timeline in UTC) in vielen Ihrer Geschäftslogik.

%Vor%

Hoffentlich werden JDBC Treiber später aktualisiert, um java.time Typen wie LocalDateTime direkt zu bearbeiten. Bis dahin müssen wir java.sql-Typen verwenden. Die alte Klasse java.sql enthält neue Methoden für die Konvertierung in / aus java.time.

%Vor%

Übergeben Sie jetzt das Locale Objekt über java.time.Instant auf einem Instant , das in einer als% definierten Spalte gespeichert wird co_de% in Postgres.

Um in die andere Richtung zu gehen:

%Vor%

Das ist also einfach, von java.sql.TimeStamp zu setTimestamp bis PreparedStatement , alles in UTC. Keine Zeitzonen. Die aktuelle Standardzeitzone Ihres Server-Betriebssystems, Ihrer JVM und Ihrer Clients ist irrelevant.

Um dem Benutzer zu präsentieren, wenden Sie eine Zeitzone an. Verwenden Sie richtige Zeitzonennamen , niemals die 3-4 Buchstabencodes wie TIMESTAMP WITH TIME ZONE oder Instant .

%Vor%

Sie können bei Bedarf eine andere Zone einstellen.

%Vor%

Um zu einem java.sql.Timestamp zurückzukehren, können Sie einen Moment auf der Timeline in UTC aus dem TIMESTAMP WITH TIME ZONE extrahieren.

%Vor%

Nein, wo haben wir EST verwendet.

Wenn Sie Daten ohne UTC-Zeitzone oder Zeitzone erhalten, wie zB IST , sind diese Daten für Sie völlig nutzlos (vorausgesetzt, wir sprechen nicht über die besprochenen Weihnachts- oder Firmenessenzen) über). Ein Datum-Zeit ohne Offset / Zoneninfo ist wie ein Geldbetrag ohne Angabe der Währung: Instant oder sogar ZonedDateTime - unbrauchbar. Aber LocalDateTime , oder 2016-04-04T08:00 , oder 142.70 ... diese sind nützlich.

Wenn Sie diesen 2.70 -Wert erhalten und Sie absolut sicher des beabsichtigten Versatz- / Zonenkontexts sind, dann:

  1. Parsen Sie diese Zeichenfolge als USD 142.70 .
  2. Übernehmen Sie einen Offset von UTC, um ein CAD 142.70 zu erhalten, oder (besser) wenden Sie eine Zeitzone an, um ein MXN 142.70 zu erhalten.

Wie dieser Code.

%Vor%

Ihre Frage ist wirklich ein Duplikat von vielen anderen. Diese Probleme wurden in anderen Fragen und Antworten oft diskutiert. Ich fordere Sie auf, Stack Overflow zu durchsuchen und zu studieren, um mehr zu diesem Thema zu erfahren.

Über java.time

Die java.time Legacy Datum-Uhrzeit-Klassen wie 2016-04-04T08:00 , LocalDateTime , & amp; OffsetDateTime .

Das Joda-Time Projekt, jetzt in Wartungsmodus , rät Migration zum java.time Klassen.

Weitere Informationen finden Sie im Oracle-Lernprogramm . Und suchen Sie nach Stack Overflow für viele Beispiele und Erklärungen. Spezifikation ist JSR 310 .

Sie können java.time Objekte direkt mit Ihrer Datenbank austauschen. Verwenden Sie einen JDBC-Treiber , der mit kompatibel ist JDBC 4.2 oder später. Keine Notwendigkeit für Strings, keine Notwendigkeit für ZonedDateTime -Klassen.

Wo erhalten Sie die Klassen java.time?

Das ThreeTen-Extra -Projekt erweitert java.time um zusätzliche Klassen. Dieses Projekt ist ein Testfeld für mögliche zukünftige Erweiterungen von java.time. Hier finden Sie einige nützliche Klassen wie java.util.Date , < a href="http://www.threeten.org/threeten-extra/apidocs/org/threeten/extra/YearWeek.html "> Calendar , SimpleDateFormat und mehr .

    
Basil Bourque 05.04.2016, 05:41
quelle