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?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
, och senare - En del av standard Java API med en medföljande implementering.
- Java 9 medförde några mindre funktioner och korrigeringar.
- Java SE 6
och Java SE 7
- Det mesta av java.time Funktionaliteten är backporterad till Java 6 &7 i ThreeTen-Backport .
- Android
- Senare versioner av Android (26+) paketimplementationer av java.time klasser.
- För tidigare Android (<26), en process som kallas API-avsockring
ger en underuppsättning av java.time
funktionalitet som inte ursprungligen var inbyggd i Android.
- Om avsockringen inte erbjuder det du behöver, ThreeTenABP projektet anpassar ThreeTen-Backport (som nämns ovan) till Android. Se Hur du använder ThreeTenABP... .