Det afhænger af typen af aftale.
Der er to slags aftaler:
- Et øjeblik, et bestemt punkt på tidslinjen, ignorerer eventuelle ændringer i tidszonereglerne.
Eksempel:Raketaffyring. - En dato og et klokkeslæt, der bør justeres for ændringer i tidszonereglerne.
Eksempel:Læge-/tandlægebesøg.
Øjeblik
Hvis vi for eksempel bestiller affyring af en raket, er vi ligeglade med datoen og tidspunktet på dagen. Vi bekymrer os kun om det øjeblik, hvor (a) himlen er på linje, og (b) vi forventer gunstigt vejr.
Hvis politikerne i den mellemliggende tid ændrer reglerne for den tidszone, der er i brug på vores lanceringssted eller på vores kontorer, har det ingen betydning for vores lanceringsaftale. Hvis politikere, der styrer vores lanceringsside, vedtager sommertid (DST) , tidspunktet for vores lancering forbliver det samme. Hvis politikerne, der styrer vores kontorer, beslutter at skift uret en halv time tidligere på grund af diplomatiske forbindelser med et naboland forbliver tidspunktet for vores lancering det samme.
For sådan en udnævnelse, ja, din tilgang ville være korrekt. Du ville optage aftalen i UTC
ved hjælp af en kolonne af typen TIMESTAMP WITH TIME ZONE
. Efter hentning skal du justere til enhver tidszone, som brugeren foretrækker.
Databaser såsom Postgres
bruge enhver tidszoneinformation, der ledsager et input, til at justere til UTC, og derefter bortskaffe denne tidszoneinformation. Når du henter værdien fra Postgres, vil den altid repræsentere en dato med klokkeslæt som set i UTC. Pas på, nogle værktøjer eller middleware kan have den anti-funktion at anvende en standardtidszone mellem hentning fra databasen og levering til dig, programmøren. Men vær tydelig:Postgres gemmer og henter altid værdier af typen TIMESTAMP WITH TIME ZONE
i UTC, altid UTC og offset-from-UTC
på nul timer-minutter-sekunder.
Her er et eksempel på Java-kode.
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 ) ;
For at se det samme øjeblik i UTC skal du konvertere til et Instant
. Et Instant
objekt repræsenterer altid et øjeblik som set i UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
Z
i slutningen af ovenstående strengeksempel er standardnotation for UTC og udtales "Zulu".
Desværre forsømte JDBC 4.2-teamet at kræve support til enten Instant
eller ZonedDateTime
typer. Så din JDBC-driver
kan eller måske ikke være i stand til at læse/skrive sådanne objekter til din database. Hvis ikke, skal du blot konvertere til OffsetDateTime
. Alle tre typer repræsenterer et øjeblik, et bestemt punkt på tidslinjen. Men OffsetDateTime
har support påkrævet af JDBC
4.2
af årsager, der undslipper mig.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
Skriver til database.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Hentning fra database.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Juster til den New York-tidszone, som din bruger ønsker.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Du kan se alle ovenstående kode køres live på IdeOne.com .
Forresten behandles sporing af tidligere begivenheder også som et øjeblik. Hvornår ankom patienten faktisk til en aftale, hvornår betalte kunden fakturaen, hvornår underskrev en ny ansat deres dokumenter, hvornår gik serveren ned... alt dette spores som et øjeblik i UTC. Som diskuteret ovenfor vil det normalt være et Instant
, selvom ZonedDateTime
&OffsetDateTime
repræsenterer også et øjeblik. Brug TIMESTAMP WITH TIME ZONE
til databasen (ikke UDEN
).
Tid på dagen
Jeg forventer, at de fleste forretningsorienterede apps er fokuseret på aftaler af den anden type, hvor vi sigter mod en dato med et tidspunkt på dagen frem for et specifikt øjeblik.
Hvis brugeren laver en aftale med sin sundhedsplejerske for at gennemgå resultaterne af en test, gør de det på et bestemt tidspunkt på dagen på denne dato. Hvis politikerne i mellemtiden ændrer reglerne for deres tidszone, flytter uret frem eller bagud en time eller en halv time eller et hvilket som helst andet tidsrum, forbliver datoen og klokkeslættet for den lægesamtale den samme. I virkeligheden vil tidspunktet for den oprindelige udnævnelse blive ændret, efter at politikerne ændrer tidszonen, og skifter til et tidligere/senere tidspunkt på tidslinjen.
For sådanne aftaler gør vi ikke gemme dato og klokkeslæt som vist i UTC. Det gør vi ikke brug databasekolonnetypen TIMESTAMP WITH TIME ZONE
.
For sådanne aftaler gemmer vi datoen med klokkeslæt uden hensyn til tidszone. Vi bruger en databasekolonne af typen TIMESTAMP WITHOUT TIME ZONE
(bemærk UDEN
i stedet for WITH
). Den matchende type i Java er LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Skriv det til databasen.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Vær klar over dette:en LocalDateTime
objektet ikke repræsentere et øjeblik, er ikke et bestemt punkt på tidslinjen. En LocalDateTime
objekt repræsenterer en række mulige øjeblikke langs omkring 26-27 timer af tidslinjen (intervallet af tidszoner rundt om på kloden). At give reel mening til en LocalDateTime
, skal vi tilknytte en påtænkt tidszone.
For den påtænkte tidszone skal du bruge en anden kolonne til at gemme zone-id'et. For eksempel strengene Europa/Rom
eller America/New_York
. Se liste over zonenavne
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Skriv det til databasen som tekst.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Hentning. Hent zonenavnet som tekst, og instansier et ZoneId
objekt.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Sæt disse to stykker sammen for at bestemme et øjeblik repræsenteret som en ZonedDateTime
objekt. Gør dette dynamisk, når du skal planlægge en kalender. Men gør ikke gemme øjeblikket. Hvis politikerne omdefinerer tidszonen/tidszonerne i fremtiden, skal der så beregnes et andet tidspunkt.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
Brugeren rejser til New York USA. De skal vide, hvornår de skal ringe til sundhedsplejersken i Milano Italien ifølge urene på væggen i deres midlertidige placering i New York. Så juster fra en tidszone til en anden. Samme øjeblik, forskellig vægur.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
tzdata
Vær opmærksom på, at hvis reglerne for dine ønskede tidszoner kan ændre sig, skal du opdatere kopien af tidszonedefinitioner på dine computere.
Java indeholder sin egen kopi af tzdata , ligesom Postgres-databasemotoren. Og også dit værtsoperativsystem. Denne særlige kode, der vises her, kræver kun at Java er opdateret. Hvis du bruger Postgres til at foretage tidszonejusteringer, er dets tzdata skal også være opdateret. Og til logning og sådan skal dit værts-OS holdes opdateret. For korrekt ur-overvågning af brugeren skal deres klientmaskines OS også være opdateret.
Pas på:Politikere over hele verden har vist en forkærlighed for at ændre deres tidszoner med overraskende hyppighed og ofte med lidt forvarsel.
Om java.time
java.time
framework er indbygget i Java 8 og nyere. Disse klasser erstatter den besværlige gamle arv
dato-tidsklasser såsom java.util.Dato
, Kalender
, &>SimpleDateFormat
.
For at lære mere, se Oracle Tutorial . Og søg i Stack Overflow for mange eksempler og forklaringer. Specifikationen er JSR 310 .
Joda-Time projekt, nu i vedligeholdelsestilstand , anbefaler migrering til java.time klasser.
Du kan udveksle java.time objekter direkte med din database. Brug en JDBC-driver
i overensstemmelse med JDBC 4.2
eller senere. Intet behov for strenge, intet behov for java.sql.*
klasser. Hibernate 5 og JPA 2.2 understøtter java.time .
Hvor får man java.time klasserne?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
, og senere - En del af standard Java API med en bundtet implementering.
- Java 9 bragte nogle mindre funktioner og rettelser.
- Java SE 6
og Java SE 7
- Det meste af java.time funktionalitet er back-porteret til Java 6 &7 i ThreeTen-Backport .
- Android
- Senere versioner af Android (26+) bundter implementeringer af java.time klasser.
- For tidligere Android (<26), en proces kendt som API-afsugning
bringer en undersæt af java.time
funktionalitet, der ikke oprindeligt var indbygget i Android.
- Hvis afsugningen ikke tilbyder det, du har brug for, kan ThreeTenABP projektet tilpasser ThreeTen-Backport (nævnt ovenfor) til Android. Se Sådan bruger du ThreeTenABP... .