PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Съхраняване на срещи в SQL база данни като Postgres за използване с java.time framework

Зависи от вида на срещата.

Има два вида срещи:

  • Момент, конкретна точка от времевата линия, игнориране на всички промени в правилата за часовата зона.
    Пример:изстрелване на ракета.
  • Дата и час от деня, които трябва да се коригират за промени в правилата за часовата зона.
    Пример:посещение при лекар/стоматолог.

Момент

Ако резервираме изстрелване на ракета, например, не ни интересува датата и часът. Ние се интересуваме само от момента, в който (а) небето се изравни и (б) очакваме благоприятно време.

Ако през това време политиците променят правилата на часовата зона, използвана на нашата площадка за изстрелване или в нашите офиси, това няма ефект върху нашата среща за изстрелване. Ако политиците, управляващи нашия сайт за стартиране, приемат лятно часово време (DST) , моментът на стартирането ни остава същият. Ако политиците, управляващи нашите офиси, решат да сменете часовника с половин час по-рано заради дипломатическите отношения със съседна държава моментът на стартиране остава същият.

За такова назначение, да, вашият подход би бил правилен. Ще запишете срещата в UTC използвайки колона от тип TIMESTAMP WITH TIME ZONE . При извличане настройте всяка часова зона, която потребителят предпочита.

Бази данни като Postgres използвайте всяка информация за часовата зона, придружаваща въвеждането, за да коригирате в UTC, и след това изхвърля тази информация за часовата зона. Когато извлечете стойността от Postgres, тя винаги ще представлява дата с час от деня, както се вижда в UTC. Внимавайте, някои инструменти или междинен софтуер може да имат антифункцията да прилагат някаква часова зона по подразбиране между извличането от базата данни и доставката до вас като програмист. Но бъдете ясни:Postgres винаги запазва и извлича стойности от тип TIMESTAMP WITH TIME ZONE в UTC, винаги UTC и offset-from-UTC от нула часа-минути-секунди.

Ето примерен 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 ) ;

За да видите същия момент в UTC, конвертирайте в Instant . Instant обектът винаги представлява момент, както се вижда в UTC.

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

Z в края на горния пример за низ е стандартна нотация за UTC и се произнася „зулу“.

За съжаление екипът на JDBC 4.2 пренебрегна изискването за поддръжка за Instant или ZonedDateTime видове. Така че вашият JDBC драйвер може или не може да чете/записва такива обекти във вашата база данни. Ако не, просто конвертирайте в OffsetDateTime . И трите типа представляват момент, конкретна точка от времевата линия. Но OffsetDateTime има поддръжка, изисквана от JDBC 4.2 по причини, които ми убягват.

OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;

Писане в база данни.

myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;

Извличане от база данни.

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

Настройте часовата зона на Ню Йорк, желана от вашия потребител.

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

Можете да видите всички горепосочени кодове, изпълнявани на живо на IdeOne.com .

Между другото, проследяването на минали събития също се третира като момент. Кога пациентът всъщност е пристигнал за среща, кога клиентът е платил фактурата, кога нов нает е подписал документите си, кога сървърът се е сринал… всичко това се проследява като момент в UTC. Както беше обсъдено по-горе, обикновено това би било Instant , въпреки че ZonedDateTime &OffsetDateTime също представляват момент. За базата данни използвайте TIMESTAMP WITH TIME ZONE (не WITHOUT ).

Час от деня

Очаквам, че повечето бизнес-ориентирани приложения са фокусирани върху срещи от друг тип, където се стремим към дата с час от деня, а не конкретен момент.

Ако потребителят си уговори среща със своя доставчик на здравни услуги, за да прегледа резултатите от тест, той прави това за определен час от деня на тази дата. Ако междувременно политиците променят правилата на своята часова зона, премествайки часовника напред или назад с час, половин час или друг период от време, датата и часът от деня на тази медицинска среща остават същите. В действителност точката от времевата линия на първоначалното назначение ще бъде променена, след като политиците сменят часовата зона, премествайки се към по-ранна/по-късна точка от времевата линия.

За такива назначения не съхранявайте датата и часа от деня, както се вижда в UTC. Ние не използвайте тип колона на базата данни TIMESTAMP WITH TIME ZONE .

За такива срещи ние съхраняваме датата с час от деня без никакво отношение към часовата зона. Използваме колона от база данни от тип TIMESTAMP WITHOUT TIME ZONE (забележете WITHOUT вместо WITH ). Съвпадащият тип в Java е LocalDateTime .

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

Запишете това в базата данни.

myPreparedStatement.setObject( … , medicalApptDateTime ) ;

Изяснете това:LocalDateTime обект не представлява момент, не е конкретна точка от времевата линия. LocalDateTime обектът представлява диапазон от възможни моменти около 26-27 часа от времевата линия (диапазонът от часови зони по света). За да придадете истинско значение на LocalDateTime , трябва да свържем предвидена часова зона.

За предвидената часова зона използвайте втора колона, за да съхраните идентификатора на зоната. Например низовете Europe/Rome или America/New_York . Вижте списък с имена на зони .

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

Запишете това в базата данни като текст.

myPreparedStatement.setString( … , zoneEuropeRome ) ;

Извличане. Извлечете името на зоната като текст и създайте ZoneId обект.

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

Съберете тези две части заедно, за да определите момент, представен като ZonedDateTime обект. Направете това динамично, когато трябва да планирате календар. Но не съхрани момента. Ако политиците предефинират часовата(ите) зона(и) в бъдеще, тогава трябва да се изчисли различен момент.

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

Потребителят пътува до Ню Йорк, САЩ. Те трябва да знаят кога да се обадят на доставчика на здравни услуги в Милано Италия според часовниците на стената във временното им местоположение в Ню Йорк. Така че коригирайте от една часова зона в друга. Същият момент, различно време на стенен часовник.

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

tzdata

Имайте предвид, че ако правилата на желаните от вас часови зони може да се променят, трябва да актуализирате копието на дефинициите на часовите зони на вашите компютри.

Java съдържа собствено копие на tzdata , както и двигателя на базата данни на Postgres. И вашата хост операционна система също. Този конкретен код, показан тук, изисква само Java да бъде актуална. Ако използвате Postgres, за да направите корекции на часовата зона, неговите tzdata също трябва да е актуален. И за регистриране и подобни, вашата хост ОС трябва да се поддържа актуална. За правилното гледане на часовника от потребителя, операционната система на клиентската му машина също трябва да е актуална.

Внимавайте:Политиците по света са показали склонност към промяна на часовите си зони с изненадваща честота и често с малко предупреждение.

Относно java.time

java.time framework е вграден в Java 8 и по-нови версии. Тези класове изместват проблемното старо наследство класове дата-час като java.util.Date , Calendar , &SimpleDateFormat .

За да научите повече, вижте урок за Oracle . И потърсете в Stack Overflow много примери и обяснения. Спецификацията е JSR 310 .

Joda-Time проект, сега в режим на поддръжка , съветва мигриране към java.time класове.

Можете да обменяте java.time обекти директно с вашата база данни. Използвайте JDBC драйвер съвместим с JDBC 4.2 или по-късно. Няма нужда от низове, няма нужда от java.sql.* класове. Hibernate 5 и JPA 2.2 поддържат java.time .

Къде да получите класовете java.time?



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Трябва да инсталирате postgresql-server-dev-X.Y за изграждане на разширение от страна на сървъра или libpq-dev за изграждане на приложение от страна на клиента

  2. Има ли изчакване за неактивни PostgreSQL връзки?

  3. MultipleActiveResultSets за модел на данни за postgresql и ado.net

  4. Как да промените поведението на нулево сортиране по подразбиране от PostgreSQL в Django ORM

  5. Разлика между индекса GiST и GIN