Oracle
 sql >> Baza danych >  >> RDS >> Oracle

Obsługa strefy czasowej w aplikacji internetowej

Przeczytaj najlepsze praktyki dotyczące czasu letniego i stref czasowych. Twój jest w zasadzie duplikatem.

Serwery w UTC

Tak, generalnie serwery powinny mieć swój system operacyjny ustawiony na UTC jako strefę czasową, lub jeśli nie podano, użyj GMT lub strefę czasową Reykjavík Islandia. Twoja implementacja Javy prawdopodobnie wybiera to ustawienie jako swoją własną domyślną strefę czasową.

Określ strefę czasową

Ale nie polegaj na tym, że strefa czasowa jest ustawiona na UTC. Administrator mógłby to zmienić. A każdy kod Java w dowolnym wątku dowolnej aplikacji w Twojej JVM może zmienić bieżącą domyślną strefę czasową JVM w czasie wykonywania dzwoniąc TimeZone.setDefault . Zamiast tego wyrób sobie nawyk, aby zawsze określać pożądaną/oczekiwaną strefę czasową, przekazując opcjonalny argument w kodzie Java.

Uważam, że wadą projektową jest to, że jakiekolwiek ramy daty i czasu sprawią, że strefa czasowa będzie opcjonalna. Bycie opcjonalnym powoduje nieskończoną ilość zamieszania, ponieważ programiści, podobnie jak wszyscy, nieświadomie myślą w kategoriach swojej osobistej strefy czasowej, o ile nie zostaną o to poproszeni. Dlatego zbyt często w pracy na randki nie zwraca się uwagi na ten problem. Dodaj problem polegający na tym, że domyślna maszyna JVM jest zróżnicowana. Przy okazji, jw dla Locale , te same problemy, zawsze powinny być wyraźnie określone.

UTC

Logika biznesowa, przechowywanie danych i wymiana danych powinny prawie zawsze odbywać się w UTC. Prawie każda baza danych ma funkcję dostosowywania dowolnych danych wejściowych do UTC i przechowywania w UTC.

Prezentując użytkownikowi datę i godzinę, dostosuj się do oczekiwanej strefy czasowej. Podczas serializacji wartości daty i godziny użyj formatów ciągów ISO 8601. Zobacz odpowiedź VickyArora dla Oracle (jestem osobą z Postgres). Przeczytaj uważnie dokument i przećwicz eksperymentowanie, aby w pełni zrozumieć zachowanie bazy danych. Specyfikacja SQL nie wyjaśnia tego zbyt wiele, a zachowanie jest bardzo zróżnicowane.

java.sql

Pamiętaj, że korzystając z Javy i JDBC, będziesz używać java.sql.Timestamp i powiązane typy danych. Są zawsze w UTC, automatycznie. W przyszłości można spodziewać się aktualizacji sterowników JDBC, aby bezpośrednio korzystały z nowych typów danych zdefiniowanych we frameworku java.time wbudowanym w Javę 8 i nowsze.

java.time

Stare klasy są przestarzałe przez java.time. Naucz się korzystać z java.time unikając starego java.util.Date/.Calendar i umil sobie programowanie.

Dopóki sterownik JDBC nie zostanie zaktualizowany, możesz korzystać z wygodnych metod konwersji wbudowanych w java.time. Zobacz przykłady dalej, gdzie Instant to chwila w UTC i ZonedDateTime to wyszukiwanie dynamiczne dostosowane do strefy czasowej.

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

Aby iść w innym kierunku.

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

Jeśli potrzebujesz oryginalnej strefy czasowej, zapisz ją

Jeśli wymagania biznesowe uznają strefę czasową oryginalnych danych wejściowych za ważną, należy ją zapamiętać, zapisz ją jawnie jako oddzielną kolumnę w tabeli bazy danych. Możesz użyć przesunięcia z UTC, ale to nie zapewnia pełnych informacji. Strefa czasowa to przesunięcie plus zestaw reguł dotyczących przeszłego, obecnego i przyszłego postępowania z anomaliami, takimi jak czas letni. Dlatego najbardziej odpowiednia jest właściwa nazwa strefy czasowej, taka jak America/Montreal .

Tylko data jest niejednoznaczna

Powiedziałeś, że zbierasz wiele wartości zawierających tylko datę, bez pory dnia i bez strefy czasowej. Klasa do tego w java.time to LocalDate . Tak jak w przypadku LocalTime i LocalDateTime , część „Lokalna…” oznacza brak konkretnej miejscowości, a zatem żadna strefa czasowa, a więc żaden punkt na osi czasu – nie ma prawdziwego znaczenia.

Należy pamiętać, że wartość zawierająca tylko datę jest z definicji niejednoznaczna. W każdej chwili data zmienia się na całym świecie. Na przykład tuż po północy w Paryżu Francja jest nowym dniem, ale w Montrealu Québec data wciąż jest „wczoraj”.

Zwykle w biznesie jakaś strefa czasowa jest ukryta, nawet nieświadomie intuicyjnie. Nieświadoma intuicja dotycząca punktów danych zwykle nie działa dobrze w dłuższej perspektywie, zwłaszcza w oprogramowaniu. Lepiej sprecyzować, jaka strefa czasowa była zamierzona. Możesz przechowywać zamierzoną strefę obok daty, takiej jak kolejna kolumna w tabeli bazy danych, lub możesz dodać komentarz w swoim kodzie programowania. Uważam, że znacznie lepiej i bezpieczniej byłoby przechowywać wartość daty i godziny. Jak więc przekształcić datę-tylko w datę-godzinę?

Często nowy dzień to chwila po północy, pierwsza chwila dnia. Możesz pomyśleć, że oznacza to porę dnia 00:00:00.0 ale nie zawsze. Czas letni (DST) i prawdopodobnie inne anomalie mogą popchnąć pierwszy moment do innego czasu na ścianie. Niech java.time określi poprawną porę dnia dla pierwszej chwili, przechodząc przez LocalDate klasa i jej atStartOfDay metoda.

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

W niektórych kontekstach biznesowych nowy dzień może być zdefiniowany (lub założony) jako godziny pracy. Załóżmy na przykład, że wydawca w Nowym Jorku ma na myśli 9 rano swojego czasu lokalnego, gdy mówi „wersja robocza książki ma zostać wydana do 2 stycznia”. Znajdźmy tę porę dnia dla tej daty w tej strefie czasowej.

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

Co to oznacza dla autora pracującego w Nowej Zelandii? Dostosuj się do jej konkretnej strefy czasowej do prezentacji dla niej, wywołując withZoneSameInstant .

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

Baza danych

Do przechowywania bazy danych przekształcamy się w Instant (chwila na osi czasu w UTC) i przekaż jako java.sql.Timestamp jak widać powyżej.

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

Po pobraniu z bazy danych przekształć z powrotem na datę i godzinę z Nowego Jorku. Konwertuj z java.sql.Timestamp do Instant , a następnie zastosuj strefę czasową ZoneId aby uzyskać ZonedDateTime .

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

Jeśli sterownik bazy danych jest zgodny z JDBC 4.2 lub nowszym, możesz być w stanie przekazać/pobrać typy java.time bezpośrednio, zamiast konwertować do/z typów java.sql. Wypróbuj PreparedStatement::setObject i ResultSet::getObject metody.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PRZYPADEK vs. DEKODOWANIE

  2. Sprawdź ograniczenie Wywoływanie funkcji Programista Oracle SQL

  3. 11 sposobów na znalezienie zduplikowanych wierszy, które mają klucz podstawowy w Oracle

  4. Oracle 11g pobiera wszystkie dopasowane wystąpienia za pomocą wyrażenia regularnego

  5. Tabela mutuje, wyzwalacz/funkcja może jej nie widzieć (powstrzymując średnią ocenę przed spadkiem poniżej 2,5)