sql >> Databáze >  >> RDS >> PostgreSQL

Ukládání schůzek do databáze SQL, jako je Postgres, pro použití s ​​rámcem java.time

Záleží na druhu schůzky.

Existují dva druhy schůzek:

  • Okamžik, konkrétní bod na časové ose, ignorující jakékoli změny pravidel časového pásma.
    Příklad:Start rakety.
  • Datum a denní doba, které by se měly přizpůsobit změnám pravidel časového pásma.
    Příklad:návštěva lékaře/zubaře.

Moment

Pokud si například rezervujeme start rakety, nestaráme se o datum a denní dobu. Zajímá nás pouze okamžik, kdy (a) se nebesa zarovnají a (b) očekáváme příznivé počasí.

Pokud mezitím politici změní pravidla časového pásma používaného na našem odpalovacím místě nebo v našich kancelářích, nemá to žádný vliv na náš termín spuštění. Pokud politici, kteří řídí naši spouštěcí stránku, přijmou letní čas (DST) , okamžik našeho spuštění zůstává stejný. Pokud se politici řídící naše kanceláře rozhodnou změňte hodiny o půl hodiny dříve kvůli diplomatickým vztahům se sousední zemí zůstává okamžik našeho spuštění stejný.

Pro takovou schůzku ano, váš přístup by byl správný. Schůzku byste zaznamenali v UTC pomocí sloupce typu TIMESTAMP WITH TIME ZONE . Po načtení upravte do libovolného časového pásma, které uživatel preferuje.

Databáze jako Postgres použijte jakékoli informace o časovém pásmu doprovázející vstup k úpravě na UTC a poté tyto informace o časovém pásmu zlikvidujte. Když načtete hodnotu z Postgres, bude vždy představovat datum s časem dne, jak je vidět v UTC. Pozor, některé nástroje nebo middleware mohou mít antifunkci použití určitého výchozího časového pásma mezi načtením z databáze a doručením vám programátorovi. Ale buďte jasní:Postgres vždy ukládá a načítá hodnoty typu TIMESTAMP WITH TIME ZONE v UTC, vždy UTC a offset-from-UTC nula hodin-minut-sekund.

Zde je několik příkladů kódu 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 ) ;

Chcete-li vidět stejný okamžik v UTC, převeďte jej na Instant . Instant objekt vždy představuje okamžik, jak je vidět v UTC.

Instant launchInstant = launchMomentAsSeenInRome.toInstant() ;  // Adjust from Rome time zone to UTC.

Z na konci výše uvedeného příkladu řetězce je standardní zápis pro UTC a vyslovuje se „Zulu“.

Bohužel tým JDBC 4.2 opomněl vyžadovat podporu pro Instant nebo ZonedDateTime typy. Takže váš ovladač JDBC může nebo nemusí být schopen číst/zapisovat takové objekty do vaší databáze. Pokud ne, jednoduše převeďte na OffsetDateTime . Všechny tři typy představují okamžik, konkrétní bod na časové ose. Ale OffsetDateTime má podporu vyžadovanou JDBC 4.2 z důvodů, které mi unikají.

OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;

Zápis do databáze.

myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;

Načítání z databáze.

OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;

Upravte časové pásmo New York požadované vaším uživatelem.

ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;

Všechny výše uvedené kód spuštěný můžete vidět živě na IdeOne.com .

Mimochodem, sledování minulých událostí je také považováno za okamžik. Kdy se pacient skutečně dostavil na schůzku, kdy zákazník zaplatil fakturu, kdy nový zaměstnanec podepsal své dokumenty, kdy spadl server… to vše je sledováno jako okamžik v UTC. Jak bylo uvedeno výše, obvykle by to bylo Instant , ačkoli ZonedDateTime &OffsetDateTime také představují okamžik. Pro databázi použijte TIMESTAMP WITH TIME ZONE (nikoli WITHOUT ).

Denní doba

Očekávám, že většina aplikací zaměřených na podnikání se zaměřuje na schůzky jiného typu, kde se zaměřujeme na datum s denní dobou spíše než na konkrétní okamžik.

Pokud si uživatel domluví schůzku se svým poskytovatelem zdravotní péče, aby zkontroloval výsledky testu, učiní tak pro konkrétní denní dobu v daný den. Pokud mezitím politici změní pravidla svého časového pásma, posunou hodiny dopředu nebo dozadu o hodinu, půl hodiny nebo o jakýkoli jiný čas, datum a denní doba lékařské návštěvy zůstanou stejné. Ve skutečnosti se bod časové osy původního jmenování změní poté, co politici změní časové pásmo, a posune se na dřívější/pozdější bod na časové ose.

U takových schůzek neděláme uložte datum a čas v UTC. neděláme použijte typ sloupce databáze TIMESTAMP WITH TIME ZONE .

U takových schůzek ukládáme datum s časem dne bez ohledu na časové pásmo. Používáme databázový sloupec typu TIMESTAMP WITHOUT TIME ZONE (všimněte si WITHOUT spíše než WITH ). Typ shody v jazyce Java je LocalDateTime .

LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;

Zapište to do databáze.

myPreparedStatement.setObject( … , medicalApptDateTime ) ;

Ujasněte si to:a LocalDateTime objekt není představují okamžik, není konkrétní bod na časové ose. LocalDateTime objekt představuje rozsah možných momenty podél přibližně 26–27 hodin časové osy (rozsah časových pásem po celé zeměkouli). Chcete-li dát skutečný význam LocalDateTime , musíme přiřadit zamýšlené časové pásmo.

Pro zamýšlené časové pásmo použijte druhý sloupec k uložení identifikátoru zóny. Například řetězce Europe/Rome nebo America/New_York . Viz seznam názvů zón .

ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;

Zapište to do databáze jako text.

myPreparedStatement.setString( … , zoneEuropeRome ) ;

Získávání. Získejte název zóny jako text a vytvořte instanci ZoneId objekt.

LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;

Dejte tyto dva kusy dohromady a určete okamžik reprezentovaný jako ZonedDateTime objekt. Udělejte to dynamicky, když potřebujete naplánovat kalendář. Ale ne uložit okamžik. Pokud politici v budoucnu předefinují časové pásmo (pásma), musí se vypočítat jiný okamžik.

ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;

Uživatel cestuje do New Yorku v USA. Potřebují vědět, kdy zavolat poskytovateli zdravotní péče v Miláně v Itálii podle hodin na zdi v jejich dočasném umístění v New Yorku. Takže upravte z jednoho časového pásma do druhého. Stejný okamžik, jiný čas nástěnných hodin.

ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;

tzdata

Uvědomte si, že pokud se pravidla vašich požadovaných časových pásem mohou měnit, musíte aktualizovat kopii definic časových pásem na svých počítačích.

Java obsahuje vlastní kopii tzdata , stejně jako databázový stroj Postgres. A také váš hostitelský operační systém. Tento konkrétní kód, který je zde zobrazen, vyžaduje, aby byla aktuální pouze Java. Pokud používáte Postgres k úpravám časového pásma, jeho tzdata musí být také aktuální. A pro protokolování a podobně by měl být váš hostitelský operační systém udržován v aktuálním stavu. Aby uživatel mohl správně sledovat hodiny, musí být operační systém jeho klientského počítače také aktuální.

Pozor:Politici po celém světě projevili zálibu ve změně časových pásem s překvapivou frekvencí a často s malým varováním.

O java.time

java.time framework je zabudován do Javy 8 a novější. Tyto třídy nahrazují staré problematické dědictví třídy podle data a času, jako je java.util.Date , Calendar , &SimpleDateFormat .

Další informace naleznete v Výukovém programu Oracle . A prohledejte Stack Overflow pro mnoho příkladů a vysvětlení. Specifikace je JSR 310 .

Čas Joda projekt, nyní v režimu údržby , doporučuje migraci na java.time třídy.

Můžete si vyměnit java.time objektů přímo s vaší databází. Použijte ovladač JDBC v souladu s JDBC 4.2 nebo později. Není potřeba řetězců, není potřeba java.sql.* třídy. Hibernate 5 a JPA 2.2 podporují java.time .

Kde získat třídy java.time?



  1. Dotaz MySql, vyberte větší než

  2. SQL ORDER BY Klauzule pro začátečníky

  3. je rychlejší vkládat řádky v pořadí primárního klíče?

  4. TNS-12505:TNS:listener aktuálně nezná SID uvedené v deskriptoru připojení