sql >> Databasteknik >  >> RDS >> PostgreSQL

Lagring av möten i en SQL-databas som Postgres för användning med java.time framework

Det beror på vilken typ av möte.

Det finns två typer av möten:

  • Ett ögonblick, en specifik punkt på tidslinjen, ignorerar eventuella ändringar av tidszonsregler.
    Exempel:raketuppskjutning.
  • Ett datum och en tid på dagen som bör anpassas för ändringar av tidszonsregler.
    Exempel:läkar-/tandläkarbesök.

Ögonblick

Om vi ​​bokar uppskjutning av en raket, till exempel, bryr vi oss inte om datum och tid på dagen. Vi bryr oss bara om ögonblicket när (a) himlen anpassar sig, och (b) vi förväntar oss gynnsamt väder.

Om politikerna under tiden ändrar reglerna för den tidszon som används på vår lanseringsplats eller på våra kontor, har det ingen effekt på vårt lanseringsutnämning. Om politiker som styr vår lanseringssida antar sommartid (DST) , ögonblicket för vår lansering förblir detsamma. Om politikerna som styr våra kontor beslutar att ändra klockan en halvtimme tidigare på grund av diplomatiska förbindelser med ett grannland förblir ögonblicket för vår lansering detsamma.

För ett sådant möte, ja, ditt tillvägagångssätt skulle vara korrekt. Du skulle spela in mötet i UTC med en kolumn av typen TIMESTAMP WITH TIME ZONE . Vid hämtning, anpassa sig till vilken tidszon som helst som användaren föredrar.

Databaser som Postgres använd vilken tidszonsinformation som helst som medföljer en ingång för att justera till UTC, och gör sedan bort den tidszonsinformationen. När du hämtar värdet från Postgres, kommer det alltid att representera ett datum med tid på dagen enligt UTC. Se upp, vissa verktyg eller mellanprogram kan ha anti-funktionen att tillämpa någon standardtidszon mellan hämtning från databasen och leverans till dig som programmerare. Men var tydlig:Postgres sparar och hämtar alltid värden av typen TIMESTAMP WITH TIME ZONE i UTC, alltid UTC och offset-from-UTC på noll timmar-minuter-sekunder.

Här är några exempel på Java-kod.

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 ) ;

För att se samma ögonblick i UTC, konvertera till en Instant . En Instant objekt representerar alltid ett ögonblick som sett i UTC.

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

Z i slutet av strängexemplet ovan finns standardnotation för UTC, och uttalas "zulu".

Tyvärr försummade JDBC 4.2-teamet att kräva support för antingen Instant eller ZonedDateTime typer. Så din JDBC-drivrutin kan eller kanske inte kan läsa/skriva sådana objekt till din databas. Om inte, konvertera helt enkelt till OffsetDateTime . Alla tre typerna representerar ett ögonblick, en specifik punkt på tidslinjen. Men OffsetDateTime har support som krävs av JDBC 4.2 av skäl som undgår mig.

OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;

Skriver till databas.

myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;

Hämtning från databasen.

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

Justera till den tidszon i New York som användaren önskar.

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

Du kan se alla ovanstående kodkörningar live på IdeOne.com .

Förresten, spårning av tidigare händelser behandlas också som ett ögonblick. När kom patienten faktiskt för att boka tid, när betalade kunden fakturan, när undertecknade en nyanställd sina dokument, när kraschade servern... allt detta spåras som ett ögonblick i UTC. Som diskuterats ovan skulle det vanligtvis vara ett Instant , även om ZonedDateTime &OffsetDateTime representerar också ett ögonblick. Använd TIMESTAMP WITH TIME ZONE för databasen (inte UTAN ).

Tid på dagen

Jag förväntar mig att de flesta affärsorienterade appar är fokuserade på möten av den andra typen, där vi siktar på ett datum med en tid på dagen snarare än ett specifikt ögonblick.

Om användaren bokar ett möte med sin vårdgivare för att granska resultaten av ett test, gör de det för en viss tid på dagen på det datumet. Om politikerna under tiden ändrar reglerna för sin tidszon, flyttar klockan framåt eller bakåt en timme eller en halvtimme eller någon annan tidsperiod, förblir datum och tid på dagen för det läkarbesöket detsamma. I själva verket kommer punkten för tidslinjen för det ursprungliga utnämningen att ändras efter att politikerna ändrat tidszonen, och flyttas till en tidigare/senare punkt på tidslinjen.

För sådana möten gör vi inte lagra datum och tid på dagen enligt UTC. Det gör vi inte använd databaskolumntypen TIMESTAMP WITH TIME ZONE .

För sådana möten lagrar vi datumet med tid på dagen utan hänsyn till tidszon. Vi använder en databaskolumn av typen TIMESTAMP WITHOUT TIME ZONE (notera UTAN istället för WITH ). Matchningstypen i Java är LocalDateTime .

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

Skriv det till databasen.

myPreparedStatement.setObject( … , medicalApptDateTime ) ;

Var tydlig med detta:en LocalDateTime objektet inte representerar ett ögonblick, är inte en specifik punkt på tidslinjen. En LocalDateTime objekt representerar ett intervall av möjliga ögonblick längs cirka 26-27 timmar av tidslinjen (intervallet av tidszoner runt om i världen). Att ge verklig mening åt en LocalDateTime , måste vi associera en avsedd tidszon.

För den avsedda tidszonen, använd en andra kolumn för att lagra zonidentifieraren. Till exempel strängarna Europa/Rom eller America/New_York . Se listan över zonnamn .

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

Skriv det till databasen som text.

myPreparedStatement.setString( … , zoneEuropeRome ) ;

Hämtning. Hämta zonnamnet som text och instansiera ett ZoneId objekt.

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

Sätt ihop dessa två delar för att bestämma ett ögonblick representerat som en ZonedDateTime objekt. Gör detta dynamiskt när du behöver schemalägga en kalender. Men gör inte lagra ögonblicket. Om politikerna omdefinierar tidszonen/tidszonerna i framtiden måste ett annat moment beräknas då.

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

Användaren reser till New York USA. De behöver veta när de ska ringa vårdgivaren i Milano Italien enligt klockorna på väggen i deras tillfälliga läge i New York. Så justera från en tidszon till en annan. Samma ögonblick, annan väggklocka.

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

tzdata

Tänk på att om reglerna för dina önskade tidszoner kan ändras måste du uppdatera kopian av tidszonsdefinitionerna på dina datorer.

Java innehåller en egen kopia av tzdata , liksom Postgres databasmotor. Och ditt värdoperativsystem också. Denna speciella kod som visas här kräver att endast Java är uppdaterad. Om du använder Postgres för att göra tidszonsjusteringar, är dess tzdata måste också vara uppdaterad. Och för loggning och sådant bör ditt värdoperativsystem hållas uppdaterat. För att användaren ska kunna titta på klockorna på rätt sätt måste klientdatorns operativsystem också vara uppdaterat.

Akta dig:Politiker runt om i världen har visat en förkärlek för att ändra sina tidszoner med överraskande frekvens, och ofta med liten förvarning.

Om java.time

java.time ramverket är inbyggt i Java 8 och senare. Dessa klasser ersätter det besvärliga gamla arvet datum-tid-klasser som java.util.Date , Kalender , &SimpleDateFormat .

Om du vill veta mer, se Oracle Tutorial . Och sök på Stack Overflow för många exempel och förklaringar. Specifikationen är JSR 310 .

Joda-Time projekt, nu i underhållsläge , rekommenderar migrering till java.time klasser.

Du kan byta ut java.time objekt direkt med din databas. Använd en JDBC-drivrutin kompatibel med JDBC 4.2 eller senare. Inget behov av strängar, inget behov av java.sql.* klasser. Hibernate 5 och JPA 2.2 stöder java.time .

Var får man tag i java.time-klasserna?



  1. Hur man återställer SQL Server-databas genom C#-kod

  2. Ladda data från MySQL-databasen till HTML-textrutor när du klickar på knappen

  3. Hur man skapar Facebook som vänner-system i php med mysql

  4. Hur man väljer rader där en kombination av 2 kolumner är densamma