Das hängt von der Art des Termins ab.
Es gibt zwei Arten von Terminen:
- Ein Moment, ein bestimmter Punkt auf der Zeitachse, wobei alle Änderungen an den Zeitzonenregeln ignoriert werden.
Beispiel:Raketenstart. - Ein Datum und eine Uhrzeit, die an Änderungen der Zeitzonenregeln angepasst werden sollten.
Beispiel:Arzt-/Zahnarztbesuch.
Augenblick
Wenn wir zum Beispiel einen Raketenstart buchen, ist uns das Datum und die Uhrzeit egal. Wir kümmern uns nur um den Moment, wenn (a) der Himmel sich ausrichtet und (b) wir günstiges Wetter erwarten.
Sollten die Politiker in der Zwischenzeit die Regeln der Zeitzone an unserem Startplatz oder in unseren Büros ändern, hat dies keine Auswirkungen auf unseren Starttermin. Wenn Politiker, die unsere Startsite verwalten, die Daylight Saving Time (DST) übernehmen , der Moment unseres Starts bleibt gleich. Wenn die Politiker, die unsere Büros leiten, beschließen, wegen diplomatischer Beziehungen die Uhr eine halbe Stunde früher stellen mit einem Nachbarland bleibt der Zeitpunkt unseres Starts derselbe.
Für einen solchen Termin, ja, Ihr Ansatz wäre richtig. Sie würden den Termin in UTC
aufzeichnen mit einer Spalte vom Typ TIMESTAMP WITH TIME ZONE
. Passen Sie sich beim Abrufen an eine beliebige Zeitzone an, die der Benutzer bevorzugt.
Datenbanken wie Postgres
Verwenden Sie alle Zeitzoneninformationen, die einer Eingabe beigefügt sind, um sie an UTC anzupassen, und verwerfen Sie dann diese Zeitzoneninformationen. Wenn Sie den Wert von Postgres abrufen, stellt er immer ein Datum mit Uhrzeit in UTC dar. Beachten Sie, dass einige Tools oder Middleware möglicherweise die Antifunktion haben, eine Standardzeitzone zwischen dem Abrufen aus der Datenbank und der Lieferung an Sie, den Programmierer, anzuwenden. Aber seien Sie klar:Postgres speichert und ruft immer Werte vom Typ TIMESTAMP WITH TIME ZONE
ab in UTC, immer UTC, und offset-from-UTC
von null Stunden-Minuten-Sekunden.
Hier ist ein Beispiel für Java-Code.
LocalDate launchDateAsSeenInRome = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime launchTimeAsSeenInRome = LocalTime.of( 21 , 0 ) ;
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
// Assemble those three parts to determine a moment.
ZonedDateTime launchMomentAsSeenInRome = ZonedDateTime.of( launchDateAsSeenInRome , launchTimeAsSeenInRome , zoneEuropeRome ) ;
Um denselben Moment in UTC zu sehen, wandeln Sie ihn in einen Instant
um . Ein Instant
Objekt stellt immer einen Moment dar, wie er in UTC gesehen wird.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
Das Z
am Ende des obigen Zeichenfolgenbeispiels ist die Standardnotation für UTC und wird „Zulu“ ausgesprochen.
Leider hat das JDBC 4.2-Team es versäumt, Unterstützung für entweder Instant
oder ZonedDateTime
Typen. Also Ihr JDBC-Treiber
möglicherweise nicht in der Lage sein, solche Objekte in Ihrer Datenbank zu lesen/zu schreiben. Wenn nicht, konvertieren Sie einfach in OffsetDateTime
. Alle drei Typen repräsentieren einen Moment, einen bestimmten Punkt auf der Zeitachse. Aber OffsetDateTime
unterstützt von JDBC
4.2
aus Gründen, die mir entgehen.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
In Datenbank schreiben.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Abruf aus der Datenbank.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Passen Sie die von Ihrem Benutzer gewünschte New Yorker Zeitzone an.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Sie können den gesamten oben genannten Code live auf IdeOne.com ausführen .
Übrigens wird auch das Verfolgen vergangener Ereignisse als Moment behandelt. Wann ist der Patient tatsächlich zum Termin erschienen, wann hat der Kunde die Rechnung bezahlt, wann hat ein neuer Mitarbeiter seine Dokumente unterschrieben, wann ist der Server abgestürzt … all dies wird als Moment in UTC verfolgt. Wie oben besprochen, wäre das normalerweise ein Instant
, obwohl ZonedDateTime
&OffsetDateTime
stellen auch einen Moment dar. Verwenden Sie für die Datenbank TIMESTAMP WITH TIME ZONE
(nicht WITHOUT
).
Tageszeit
Ich gehe davon aus, dass sich die meisten geschäftsorientierten Apps auf Termine der anderen Art konzentrieren, bei denen wir eher auf ein Datum mit einer Tageszeit als auf einen bestimmten Zeitpunkt abzielen.
Wenn der Benutzer einen Termin mit seinem Gesundheitsdienstleister vereinbart, um die Ergebnisse eines Tests zu überprüfen, tut er dies für eine bestimmte Tageszeit an diesem Datum. Wenn die Politiker in der Zwischenzeit die Regeln ihrer Zeitzone ändern, indem sie die Uhr um eine oder eine halbe Stunde oder eine andere Zeitspanne vor- oder zurückstellen, bleiben Datum und Uhrzeit dieses Arzttermins gleich. Tatsächlich wird der Punkt der Zeitachse des ursprünglichen Termins geändert, nachdem die Politiker die Zeitzone geändert haben, und sich zu einem früheren/späteren Punkt auf der Zeitachse verschieben.
Für solche Termine tun wir nicht Speichern Sie das Datum und die Uhrzeit, wie sie in UTC angezeigt werden. Wir nicht Verwenden Sie den Datenbankspaltentyp TIMESTAMP WITH TIME ZONE
.
Bei solchen Terminen speichern wir das Datum mit Uhrzeit ohne Rücksicht auf die Zeitzone. Wir verwenden eine Datenbankspalte vom Typ TIMESTAMP WITHOUT TIME ZONE
(beachte WITHOUT
statt WITH
). Der übereinstimmende Typ in Java ist LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Schreiben Sie das in die Datenbank.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Machen Sie sich klar:eine LocalDateTime
Objekt nicht einen Moment darstellen, nicht ein bestimmter Punkt auf der Zeitachse. Eine LocalDateTime
Objekt repräsentiert einen Bereich möglicher Momente entlang etwa 26-27 Stunden der Zeitachse (der Bereich der Zeitzonen rund um den Globus). Um einer LocalDateTime
eine echte Bedeutung zu verleihen , müssen wir eine beabsichtigte Zeitzone zuordnen.
Verwenden Sie für diese beabsichtigte Zeitzone eine zweite Spalte, um die Zonenkennung zu speichern. Beispielsweise die Zeichenfolgen Europe/Rome
oder America/New_York
. Siehe Liste der Zonennamen
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Schreiben Sie das als Text in die Datenbank.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Abruf. Rufen Sie den Zonennamen als Text ab und instanziieren Sie eine ZoneId
Objekt.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Fügen Sie diese beiden Teile zusammen, um einen Moment zu bestimmen, der als ZonedDateTime
dargestellt wird Objekt. Tun Sie dies dynamisch, wenn Sie einen Kalender planen müssen. Aber nicht Moment speichern. Wenn die Politiker in Zukunft die Zeitzone(n) neu definieren, muss dann ein anderer Zeitpunkt berechnet werden.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
Der Benutzer reist nach New York USA. Sie müssen wissen, wann sie den Gesundheitsdienstleister in Mailand, Italien, gemäß den Uhren an der Wand an ihrem vorübergehenden Standort in New York anrufen müssen. Passen Sie also von einer Zeitzone zur anderen an. Gleicher Moment, andere Uhrzeit.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
tzdata
Beachten Sie, dass Sie die Kopie der Zeitzonendefinitionen auf Ihren Computern aktualisieren müssen, wenn sich die Regeln Ihrer gewünschten Zeitzonen möglicherweise ändern.
Java enthält eine eigene Kopie des tzdata , ebenso wie die Postgres-Datenbank-Engine. Und auch Ihr Host-Betriebssystem. Dieser spezielle hier gezeigte Code erfordert nur Java, um auf dem neuesten Stand zu sein. Wenn Sie Postgres verwenden, um Zeitzonenanpassungen vorzunehmen, werden die tzdata müssen auch aktuell sein. Und für die Protokollierung und dergleichen sollte Ihr Host-Betriebssystem auf dem neuesten Stand gehalten werden. Damit der Benutzer die Uhrzeit richtig einsehen kann, muss auch das Betriebssystem seines Client-Rechners auf dem neuesten Stand sein.
Achtung:Politiker auf der ganzen Welt haben eine Vorliebe dafür gezeigt, ihre Zeitzonen mit überraschender Häufigkeit und oft ohne Vorwarnung zu ändern.
Über java.time
Der java.time
Framework ist in Java 8 und höher integriert. Diese Klassen ersetzen das lästige alte Legacy
Datumszeitklassen wie java.util.Date
, Calendar
, &SimpleDateFormat
.
Weitere Informationen finden Sie im Oracle-Tutorial . Und durchsuchen Sie Stack Overflow nach vielen Beispielen und Erklärungen. Die Spezifikation ist JSR 310 .
Die Joda-Time Projekt, jetzt im Wartungsmodus , empfiehlt die Migration zu java.time Klassen.
Sie können java.time austauschen Objekte direkt mit Ihrer Datenbank. Verwenden Sie einen JDBC-Treiber
kompatibel mit JDBC 4.2
oder später. Keine Notwendigkeit für Strings, keine Notwendigkeit für java.sql.*
Klassen. Hibernate 5 und JPA 2.2 unterstützen java.time .
Wo erhalte ich die java.time-Klassen?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
, und höher – Teil der Standard-Java-API mit einer gebündelten Implementierung.
- Java 9 brachte einige kleinere Funktionen und Korrekturen.
- Java SE 6
und Java SE 7
- Die meiste Zeit java.time Die Funktionalität wird in ThreeTen-Backport auf Java 6 &7 zurückportiert .
- Android
- Spätere Versionen von Android (26+) bündeln Implementierungen von java.time Klassen.
- Für ältere Android-Versionen (<26) ein Prozess namens API-Entzuckerung
bringt eine Teilmenge der java.time
Funktionalität, die ursprünglich nicht in Android integriert war.
- Wenn das Desugaring nicht das bietet, was Sie brauchen, das ThreeTenABP Projekt adaptiert ThreeTen-Backport (oben erwähnt) auf Android. Siehe How to use ThreeTenABP… .