To zależy od rodzaju spotkania.
Istnieją dwa rodzaje spotkań:
- Chwila, konkretny punkt na osi czasu, ignorujący wszelkie zmiany zasad strefy czasowej.
Przykład:start rakiety. - Data i pora dnia, które powinny zostać dostosowane do zmian w zasadach strefy czasowej.
Przykład:wizyta medyczna/dentystyczna.
Moment
Jeśli rezerwujemy np. start rakiety, nie obchodzi nas data i pora dnia. Dbamy tylko o moment, w którym (a) niebiosa wyrównają się i (b) spodziewamy się sprzyjającej pogody.
Jeśli w międzyczasie politycy zmienią zasady strefy czasowej używanej na naszej stronie startowej lub w naszych biurach, nie ma to wpływu na nasz termin rozpoczęcia. Jeśli politycy zarządzający naszą witryną startową przyjmą Czas letni (DST) , moment naszej premiery pozostaje ten sam. Jeśli politycy rządzący naszymi biurami zdecydują się zmień zegar pół godziny wcześniej ze względu na stosunki dyplomatyczne z sąsiednim krajem, moment naszej premiery pozostaje taki sam.
W przypadku takiego spotkania tak, twoje podejście byłoby poprawne. Zarejestrowałbyś spotkanie w UTC
używając kolumny typu TIMESTAMP WITH TIME ZONE
. Po pobraniu dostosuj się do dowolnej preferowanej przez użytkownika strefy czasowej.
Bazy danych, takie jak Postgres
użyj dowolnych informacji o strefie czasowej towarzyszących wejściu, aby dostosować się do czasu UTC, a następnie usuń te informacje o strefie czasowej. Gdy pobierzesz wartość z Postgres, zawsze będzie ona reprezentować datę z porą dnia, jak widać w UTC. Uwaga, niektóre narzędzia lub oprogramowanie pośredniczące mogą mieć funkcję antywirusową polegającą na zastosowaniu domyślnej strefy czasowej między pobraniem z bazy danych a dostarczeniem programiście. Ale pamiętaj:Postgres zawsze zapisuje i pobiera wartości typu TIMESTAMP WITH TIME ZONE
w UTC, zawsze UTC i przesunięcie od UTC
zero godzin-minut-sekund.
Oto przykładowy kod Java.
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 ) ;
Aby zobaczyć ten sam moment w UTC, przekonwertuj na Instant
. Instant
obiekt zawsze reprezentuje moment widziany w UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
WITH
na końcu powyższego przykładu ciągu znajduje się standardowa notacja dla UTC i jest wymawiana „Zulu”.
Niestety zespół JDBC 4.2 nie wymagał wsparcia dla Instant
lub ZonedDateTime
typy. Twój sterownik JDBC
może, ale nie musi, być w stanie odczytywać/zapisywać takie obiekty w Twojej bazie danych. Jeśli nie, po prostu przekonwertuj na OffsetDateTime
. Wszystkie trzy typy reprezentują moment, konkretny punkt na osi czasu. Ale OffsetDateTime
ma wsparcie wymagane przez JDBC
4.2
z powodów, które mi umykają.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
Zapis do bazy danych.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Pobieranie z bazy danych.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Dostosuj strefę czasową Nowego Jorku żądaną przez użytkownika.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Możesz zobaczyć wszystkie powyższe kod uruchamiany na żywo na IdeOne.com .
Nawiasem mówiąc, śledzenie przeszłych wydarzeń również traktowane jest jako chwila. Kiedy pacjent rzeczywiście przybył na wizytę, kiedy klient zapłacił fakturę, kiedy nowy pracownik podpisał swoje dokumenty, kiedy nastąpiła awaria serwera… wszystko to jest śledzone jako moment w UTC. Jak omówiono powyżej, zwykle będzie to Instant
, chociaż ZonedDateTime
&OffsetDateTime
również reprezentują chwilę. Dla bazy danych użyj TIMESTAMP WITH TIME ZONE
(nie WITHOUT
).
Pora dnia
Spodziewam się, że większość aplikacji biznesowych koncentruje się na spotkaniach innego typu, w których celujemy w randkę z porą dnia, a nie konkretnym momentem.
Jeśli użytkownik umówi się na wizytę u swojego lekarza, aby przejrzeć wyniki testu, robi to na konkretną porę dnia w tym dniu. Jeśli w międzyczasie politycy zmienią zasady swojej strefy czasowej, przesuwając zegar do przodu lub do tyłu o godzinę, pół godziny lub o jakąkolwiek inną ilość czasu, data i godzina wizyty lekarskiej pozostają takie same. W rzeczywistości punkt na osi czasu pierwotnego spotkania zostanie zmieniony po tym, jak politycy zmienią strefę czasową, przesuwając się do wcześniejszego/późniejszego punktu na osi czasu.
W przypadku takich spotkań nie przechowywać datę i godzinę w formacie UTC. nie użyj bazy danych typu kolumny TIMESTAMP WITH TIME ZONE
.
W przypadku takich spotkań przechowujemy datę z porą dnia bez względu na strefę czasową. Używamy kolumny bazy danych typu TIMESTAMP WITHOUT TIME ZONE
(zwróć uwagę WITHOUT
zamiast WITH
). Dopasowany typ w Javie to LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Zapisz to do bazy danych.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Wyjaśnij to:LocalDateTime
obiekt nie reprezentować chwilę, to nie określony punkt na osi czasu. LocalDateTime
obiekt reprezentuje zakres możliwych momenty wzdłuż około 26-27 godzin osi czasu (zakres stref czasowych na całym świecie). Aby nadać prawdziwe znaczenie LocalDateTime
, musimy powiązać zamierzoną strefę czasową.
Dla tej zamierzonej strefy czasowej użyj drugiej kolumny do przechowywania identyfikatora strefy. Na przykład ciągi Europe/Rome
lub America/New_York
. Zobacz listę nazw stref
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Zapisz to w bazie danych jako tekst.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Wyszukiwanie. Pobierz nazwę strefy jako tekst i stwórz instancję ZoneId
obiekt.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Połącz te dwa kawałki, aby określić moment reprezentowany jako ZonedDateTime
obiekt. Rób to dynamicznie, gdy musisz zaplanować kalendarz. Ale nie zapisz tę chwilę. Jeśli politycy przedefiniują strefy czasowe w przyszłości, wówczas należy obliczyć inny moment.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
Użytkownik podróżuje do Nowego Jorku w Stanach Zjednoczonych. Muszą wiedzieć, kiedy zadzwonić do lekarza w Mediolanie we Włoszech, zgodnie z zegarami ściennymi w ich tymczasowej lokalizacji w Nowym Jorku. Dostosuj się więc z jednej strefy czasowej do drugiej. Ten sam moment, inny czas zegara ściennego.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
tzdata
Pamiętaj, że jeśli reguły żądanych stref czasowych mogą ulec zmianie, musisz zaktualizować kopię definicji stref czasowych na swoich komputerach.
Java zawiera własną kopię tzdata , podobnie jak silnik bazy danych Postgres. A także system operacyjny hosta. Ten konkretny kod pokazany tutaj wymaga tylko aktualności Java. Jeśli używasz Postgresa do dostosowywania stref czasowych, jego tzdata muszą być również aktualne. W przypadku logowania itp. system operacyjny hosta powinien być aktualizowany. Aby użytkownik mógł prawidłowo obserwować zegar, system operacyjny komputera klienckiego również musi być aktualny.
Uwaga:politycy na całym świecie wykazali się zamiłowaniem do zmiany swoich stref czasowych z zaskakującą częstotliwością i często bez ostrzeżenia.
O java.time
java.time
Framework jest wbudowany w Javę 8 i nowsze. Te klasy zastępują kłopotliwe stare dziedzictwo
klasy data-godzina, takie jak java.util.Date
, Calendar
, &SimpleDateFormat
.
Aby dowiedzieć się więcej, zapoznaj się z Samouczkiem Oracle . I przeszukaj Stack Overflow, aby znaleźć wiele przykładów i wyjaśnień. Specyfikacja to JSR 310 .
Joda-Time projekt, teraz w trybie konserwacji , zaleca migrację do java.time zajęcia.
Możesz wymienić java.time obiekty bezpośrednio z bazą danych. Użyj sterownika JDBC
zgodny z JDBC 4.2
lub później. Nie potrzeba ciągów, nie potrzeba java.sql.*
zajęcia. Obsługa Hibernate 5 i JPA 2.2 java.time .
Skąd wziąć klasy java.time?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
i nowsze — część standardowego interfejsu Java API z dołączoną implementacją.
- Java 9 przyniósł kilka drobnych funkcji i poprawek.
- Java SE 6
i Java SE 7
- Większość java.time funkcjonalność została przeniesiona do Javy 6 i 7 w ThreeTen-Backport .
- Android
- Późniejsze wersje pakietu Android (26+) implementacje pakietu java.time zajęcia.
- W przypadku wcześniejszych wersji Androida (<26), proces znany jako Odcukierowywanie interfejsu API
udostępnia podzbiór java.time
funkcjonalność, która nie była pierwotnie wbudowana w Androida.
- Jeśli odcukrzanie nie zapewnia tego, czego potrzebujesz, ThreeTenABP projekt dostosowuje Backport ThreeTen (wspomniane powyżej) na Androida. Zobacz Jak korzystać z ThreeTenABP… .