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.