sql >> Database >  >> RDS >> PostgreSQL

Afspraken opslaan in een SQL-database zoals Postgres voor gebruik met java.time framework

Het hangt af van het soort afspraak.

Er zijn twee soorten afspraken:

  • Een moment, een specifiek punt op de tijdlijn, waarbij eventuele wijzigingen in de tijdzoneregels worden genegeerd.
    Voorbeeld:raketlancering.
  • Een datum en tijd van de dag die moeten worden aangepast voor wijzigingen in de tijdzoneregels.
    Voorbeeld:medisch/tandartsbezoek.

Moment

Als we bijvoorbeeld de lancering van een raket boeken, geven we niet om de datum en het tijdstip van de dag. We geven alleen om het moment waarop (a) de hemel op één lijn ligt en (b) we gunstig weer verwachten.

Als de politici in de tussentijd de regels wijzigen van de tijdzone die op onze lanceerplaats of op onze kantoren in gebruik is, heeft dat geen invloed op onze lanceerafspraak. Als politici die onze lanceerplaats besturen Daylight Saving Time (DST) aannemen , blijft het moment van onze lancering hetzelfde. Als de politici die onze kantoren besturen besluiten om verander de klok een half uur eerder vanwege diplomatieke betrekkingen met een buurland blijft het moment van onze lancering hetzelfde.

Voor zo'n afspraak, ja, uw aanpak zou correct zijn. U zou de afspraak opnemen in UTC met behulp van een kolom van het type TIMESTAMP WITH TIME ZONE . Pas na het ophalen de tijdzone aan die de gebruiker verkiest.

Databases zoals Postgres gebruik alle tijdzone-info die een invoer vergezelt om in UTC aan te passen, en verwijder vervolgens die tijdzone-info. Wanneer u de waarde uit Postgres ophaalt, vertegenwoordigt deze altijd een datum met de tijd van de dag zoals te zien in UTC. Pas op, sommige tooling of middleware kan de anti-functie hebben om een ​​standaard tijdzone toe te passen tussen het ophalen uit de database en het afleveren aan u de programmeur. Maar wees duidelijk:Postgres bewaart altijd waarden van het type TIMESTAMP WITH TIME ZONE in UTC, altijd UTC, en offset-from-UTC van nul uren-minuten-seconden.

Hier is een voorbeeld van 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 ) ;

Om hetzelfde moment in UTC te zien, converteer je naar een Instant . Een Instant object vertegenwoordigt altijd een moment zoals te zien in UTC.

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

De Z aan het einde van het bovenstaande tekenreeksvoorbeeld is de standaardnotatie voor UTC, en wordt uitgesproken als "Zulu".

Helaas heeft het JDBC 4.2-team verzuimd ondersteuning nodig te hebben voor Instant of ZonedDateTime soorten. Dus je JDBC-stuurprogramma kan dergelijke objecten al dan niet naar uw database lezen/schrijven. Als dat niet het geval is, converteert u het gewoon naar OffsetDateTime . Alle drie de typen vertegenwoordigen een moment, een specifiek punt op de tijdlijn. Maar OffsetDateTime heeft ondersteuning vereist door JDBC 4.2 om redenen die mij ontgaan.

OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;

Schrijven naar database.

myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;

Ophalen uit database.

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

Pas de door uw gebruiker gewenste tijdzone in New York aan.

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

Je kunt alle bovenstaande code live zien lopen op IdeOne.com .

Overigens wordt het volgen van gebeurtenissen uit het verleden ook als een moment beschouwd. Wanneer kwam de patiënt daadwerkelijk voor de afspraak, wanneer heeft de klant de factuur betaald, wanneer heeft een nieuwe medewerker zijn documenten ondertekend, wanneer is de server gecrasht ... dit alles wordt als een moment bijgehouden in UTC. Zoals hierboven besproken, is dat meestal een Instant , hoewel ZonedDateTime &OffsetDateTime ook een moment vertegenwoordigen. Gebruik voor de database TIMESTAMP WITH TIME ZONE (niet WITHOUT ).

Tijd van de dag

Ik verwacht dat de meeste bedrijfsgerichte apps gericht zijn op afspraken van het andere type, waarbij we ons richten op een datum met een tijdstip in plaats van een specifiek moment.

Als de gebruiker een afspraak maakt met zijn zorgverlener om de resultaten van een test te bekijken, doet hij dat voor een bepaald tijdstip van de dag op die datum. Als de politici in de tussentijd de regels van hun tijdzone wijzigen door de klok een uur of een half uur of een andere hoeveelheid tijd vooruit of achteruit te zetten, blijven de datum en het tijdstip van die medische afspraak hetzelfde. In werkelijkheid zal het punt van de tijdlijn van de oorspronkelijke afspraak worden gewijzigd nadat de politici de tijdzone hebben gewijzigd en verschuiven naar een eerder/later punt op de tijdlijn.

Voor dergelijke afspraken doen we niet sla de datum en tijd van de dag op zoals te zien in UTC. We doen niet gebruik het type databasekolom TIMESTAMP WITH TIME ZONE .

Voor dergelijke afspraken slaan we de datum op met de tijd van de dag, ongeacht de tijdzone. We gebruiken een databasekolom van het type TIMESTAMP WITHOUT TIME ZONE (let op WITHOUT in plaats van WITH ). Het overeenkomende type in Java is LocalDateTime .

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

Schrijf dat naar de database.

myPreparedStatement.setObject( … , medicalApptDateTime ) ;

Wees hier duidelijk over:een LocalDateTime object doet niet een moment vertegenwoordigen, is niet een bepaald punt op de tijdlijn. Een LocalDateTime object vertegenwoordigt een bereik van mogelijk momenten langs ongeveer 26-27 uur van de tijdlijn (het bereik van tijdzones over de hele wereld). Om echte betekenis te geven aan een LocalDateTime , moeten we een beoogde tijdzone koppelen.

Gebruik voor die beoogde tijdzone een tweede kolom om de zone-ID op te slaan. Bijvoorbeeld de strings Europe/Rome of America/New_York . Zie lijst met zonenamen .

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

Schrijf dat naar de database als tekst.

myPreparedStatement.setString( … , zoneEuropeRome ) ;

Ophalen. Haal de zonenaam op als tekst en maak een ZoneId voorwerp.

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

Leg die twee stukken bij elkaar om een ​​moment te bepalen dat wordt weergegeven als een ZonedDateTime object. Doe dit dynamisch wanneer u een kalender moet plannen. Maar doe niet het moment opslaan. Als de politici in de toekomst de tijdzone(s) opnieuw definiëren, moet dan een ander moment worden berekend.

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

De gebruiker reist naar New York, VS. Ze moeten weten wanneer ze de zorgverlener in Milaan, Italië moeten bellen, volgens de klokken aan de muur in hun tijdelijke locatie in New York. Pas je dus aan van de ene tijdzone naar de andere. Zelfde moment, andere wandkloktijd.

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

tzdata

Houd er rekening mee dat als de regels van de door u gewenste tijdzones veranderen, u de kopie van de tijdzonedefinities op uw computers moet bijwerken.

Java bevat een eigen kopie van de tzdata , evenals de Postgres-database-engine. En ook uw hostbesturingssysteem. Deze specifieke code die hier wordt getoond, vereist alleen dat Java up-to-date is. Als u Postgres gebruikt om tijdzone-aanpassingen te maken, zijn tzdata moet ook actueel zijn. En voor logboekregistratie en dergelijke moet uw host-besturingssysteem up-to-date worden gehouden. Voor een juiste klokkijken door de gebruiker, moet het besturingssysteem van hun clientcomputer ook up-to-date zijn.

Pas op:politici over de hele wereld hebben een voorliefde getoond om hun tijdzones verrassend vaak te veranderen, en vaak met weinig waarschuwing vooraf.

Over java.time

De java.time framework is ingebouwd in Java 8 en hoger. Deze klassen vervangen de lastige oude legacy date-time klassen zoals java.util.Date , Calendar , &SimpleDateFormat .

Zie voor meer informatie de Oracle-zelfstudie . En zoek Stack Overflow voor veel voorbeelden en uitleg. Specificatie is JSR 310 .

De Joda-Time project, nu in onderhoudsmodus , adviseert migratie naar de java.time lessen.

Je mag java.time ruilen objecten rechtstreeks met uw database. Gebruik een JDBC-stuurprogramma voldoet aan JDBC 4.2 of later. Geen strings nodig, geen java.sql.* klassen. Hibernate 5 &JPA 2.2 ondersteunen java.time .

Waar zijn de java.time-klassen te verkrijgen?



  1. Hoe de buffergrootte in Oracle SQL Developer te vergroten om alle records te bekijken?

  2. Is het verhogen van een veld in MySQL atomair?

  3. PDO en caching, hoe implementeer je het in een databaseklasse?

  4. SQLAlchemy set lidmaatschap voor zeer grote sets