Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Handhabung der Zeitzone in der Webanwendung

Lesen Sie die Best Practices für die Frage Sommerzeit und Zeitzone. Ihre ist im Grunde ein Duplikat.

Server in UTC

Ja, im Allgemeinen sollte das Betriebssystem von Servern auf UTC als Zeitzone eingestellt sein oder, falls nicht angegeben, GMT verwenden oder die Zeitzone Reykjavík Island. Ihre Java-Implementierung übernimmt wahrscheinlich diese Einstellung als eigene aktuelle Standardzeitzone.

Zeitzone angeben

Verlassen Sie sich jedoch nicht darauf, dass die Zeitzone auf UTC eingestellt ist. Ein Systemadministrator könnte es ändern. Und jeder Java-Code in jedem Thread einer beliebigen App innerhalb Ihrer JVM kann die aktuelle Standardzeitzone der JVM zur Laufzeit ändern durch Aufrufen von TimeZone.setDefault . Machen Sie es sich stattdessen zur Gewohnheit, immer die gewünschte/erwartete Zeitzone anzugeben, indem Sie das optionale Argument in Ihrem Java-Code übergeben.

Ich halte es für einen Designfehler, dass jedes Datum-Uhrzeit-Framework die Zeitzone optional machen würde. Optional zu sein, schafft endlose Mengen an Verwirrung, weil Programmierer, wie alle anderen, unbewusst in Bezug auf ihre eigene persönliche Zeitzone denken, wenn sie nicht dazu aufgefordert werden. Daher wird dem Thema in der Date-Time-Arbeit allzu oft keine Beachtung geschenkt. Fügen Sie das Problem hinzu, dass der JVM-Standard variiert. Dasselbe gilt übrigens für Locale , gleiche Probleme, sollten immer explizit angegeben werden.

UTC

Ihre Geschäftslogik, Datenspeicherung und Datenaustausch sollten fast immer in UTC erfolgen. Nahezu jede Datenbank verfügt über eine Funktion zum Anpassen aller Eingaben in UTC und zum Speichern in UTC.

Wenn Sie einem Benutzer Datum und Uhrzeit präsentieren, passen Sie es an die erwartete Zeitzone an. Verwenden Sie beim Serialisieren eines Datum-Uhrzeit-Werts die ISO 8601-Zeichenfolgenformate. Siehe die Antwort von VickyArora speziell für Oracle (ich bin eine Postgres-Person). Lesen Sie das Dokument sorgfältig durch und üben Sie, indem Sie experimentieren, um das Verhalten Ihrer Datenbank vollständig zu verstehen. Die SQL-Spezifikation sagt diesbezüglich nicht viel aus, und das Verhalten ist sehr unterschiedlich.

java.sql

Denken Sie daran, dass Sie bei Verwendung von Java und JDBC den java.sql.Timestamp verwenden und verwandte Datentypen. Sie sind automatisch immer in UTC. Erwarten Sie in Zukunft, dass JDBC-Treiber aktualisiert werden, um die neuen Datentypen, die im java.time-Framework definiert sind, das in Java 8 und höher integriert ist, direkt zu verwenden.

java.time

Die alten Klassen sind von java.time veraltet. Lernen Sie, java.time zu verwenden während Sie das alte java.util.Date/.Calendar vermeiden und Ihr Programmierleben viel angenehmer machen.

Bis Ihr JDBC-Treiber aktualisiert ist, können Sie die in java.time integrierten Konvertierungsmethoden verwenden. Siehe Beispiele weiter unten, wobei Instant ist ein Moment in UTC und ZonedDateTime ist ein in eine Zeitzone angepasster Instant.

Instant instant = myJavaSqlTimestamp.toInstant();
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Um in die andere Richtung zu gehen.

java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from( zdt.toInstant() );

Wenn Sie die ursprüngliche Zeitzone benötigen, speichern Sie sie

Wenn Ihre Geschäftsanforderungen die Zeitzone der ursprünglichen Eingabedaten als wichtig erachten, die Sie sich merken müssen, speichern Sie diese explizit als separate Spalte in Ihrer Datenbanktabelle. Sie können einen Offset von UTC verwenden, aber das liefert keine vollständigen Informationen. Eine Zeitzone ist ein Offset plus eine Reihe von Regeln für die vergangene, gegenwärtige und zukünftige Behandlung von Anomalien wie Sommerzeit. Daher ist ein richtiger Zeitzonenname am besten geeignet, z. B. America/Montreal .

Nur Datum ist mehrdeutig

Sie sagten, Sie sammeln viele reine Datumswerte, ohne Tageszeit und ohne Zeitzone. Die Klasse dafür in java.time ist LocalDate . Wie bei LocalTime und LocalDateTime , der Teil „Lokal…“ bedeutet keinen bestimmten Ort, also keine Zeitzone und somit keinen Punkt auf der Zeitachse – hat keine wirkliche Bedeutung.

Denken Sie daran, dass ein Nur-Datum-Wert per Definition mehrdeutig ist. Zu jedem Zeitpunkt variiert das Datum auf der ganzen Welt. Zum Beispiel ist kurz nach Mitternacht in Paris Frankreich ein neuer Tag, aber in Montréal Québec ist das Datum immer noch „gestern“.

Normalerweise ist in der Geschäftswelt eine Zeitzone implizit, sogar unbewusst intuitiv. Unbewusste Intuition über Datenpunkte funktioniert vor allem in Software auf Dauer nicht gut. Besser deutlich machen, welche Zeitzone beabsichtigt war. Sie könnten die beabsichtigte Zone neben dem Datum speichern, z. B. eine andere Spalte in der Datenbanktabelle, oder Sie könnten einen Kommentar in Ihren Programmiercode einfügen. Ich glaube, es wäre weitaus besser und sicherer, einen Datum-Uhrzeit-Wert zu speichern. Wie wandeln wir also ein reines Datum in ein Datum und eine Uhrzeit um?

Oft ist ein neuer Tag der Moment nach Mitternacht, der erste Moment des Tages. Man könnte meinen, das bedeutet die Tageszeit 00:00:00.0 aber nicht immer. Die Sommerzeit (DST) und möglicherweise andere Anomalien können den ersten Moment auf eine andere Uhrzeit verschieben. Lassen Sie java.time die korrekte Tageszeit für den ersten Moment bestimmen, indem Sie das LocalDate durchlaufen Klasse und ihr atStartOfDay Methode.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
ZonedDateTime todayStart = today.atStartOfDay( zoneId );

In einigen Geschäftskontexten kann ein neuer Tag als Geschäftszeit definiert (oder angenommen) werden. Angenommen, ein Verlag in New York meint 9 Uhr morgens in seiner Ortszeit, wenn er sagt:„Der Buchentwurf ist bis zum 2. Januar fällig“. Lassen Sie uns diese Tageszeit für dieses Datum in dieser Zeitzone abrufen.

ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.of( 2016 , 1 , 2 , 9 , 0 , 0 , 0 , zoneId );

Was bedeutet das für den in Neuseeland arbeitenden Autor? Passen Sie sich für die Präsentation an ihre spezielle Zeitzone an, indem Sie withZoneSameInstant aufrufen .

ZoneId zoneId_Pacific_Auckland = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt_Pacific_Auckland = zdt.withZoneSameInstant( zoneId_Pacific_Auckland );

Datenbank

Für die Datenbankspeicherung verwandeln wir uns in einen Instant (ein Moment auf der Zeitleiste in UTC) und als java.sql.Timestamp übergeben wie oben gesehen.

java.sql.Timestamp ts = java.sql.Timestamp.from( zdt.toInstant() );

Beim Abrufen aus der Datenbank zurück in ein New Yorker Datum und eine Uhrzeit transformieren. Konvertieren von java.sql.Timestamp zu einem Instant , und wenden Sie dann eine Zeitzone ZoneId an um eine ZonedDateTime zu erhalten .

Instant instant = ts.toInstant();
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Wenn Ihr Datenbanktreiber mit JDBC 4.2 oder höher kompatibel ist, können Sie die java.time-Typen möglicherweise direkt übergeben/abrufen, anstatt sie in/von java.sql-Typen zu konvertieren. Probieren Sie PreparedStatement::setObject aus und ResultSet::getObject Methoden.