sql >> Database >  >> RDS >> Oracle

Omgaan met tijdzone in webapplicatie

Lees de Vraag Best practices voor zomertijd en tijdzones. De jouwe is in feite een duplicaat.

Servers in UTC

Ja, over het algemeen moeten servers hun besturingssysteem hebben ingesteld op UTC als tijdzone, of, indien niet opgegeven, GMT gebruiken of de tijdzone van Reykjavík IJsland. Uw Java-implementatie pakt deze instelling waarschijnlijk op als zijn eigen huidige standaardtijdzone.

Specificeer tijdzone

Maar vertrouw er niet op dat de tijdzone is ingesteld op UTC. Een systeembeheerder zou het kunnen veranderen. En elke Java-code in elke thread van elke app binnen uw JVM kan de huidige standaardtijdzone van de JVM veranderen at runtime door TimeZone.setDefault . te bellen . Maak er dus een gewoonte van om altijd de gewenste/verwachte tijdzone op te geven door het optionele argument in uw Java-code door te geven.

Ik beschouw het als een ontwerpfout dat elk datum-tijdraamwerk de tijdzone optioneel zou maken. Facultatief zijn zorgt voor eindeloze hoeveelheden verwarring omdat programmeurs, net als iedereen, onbewust denken in termen van hun eigen persoonlijke tijdzone, tenzij daarom wordt gevraagd. Dus al te vaak wordt er in date-time werk geen aandacht aan de kwestie besteed. Voeg het probleem toe dat de JVM-standaard varieert. Trouwens, idem voor Locale , dezelfde problemen, moet altijd expliciet worden gespecificeerd.

UTC

Uw bedrijfslogica, gegevensopslag en gegevensuitwisseling moeten bijna altijd in UTC worden gedaan. Bijna elke database heeft een functie om invoer in UTC aan te passen en op te slaan in UTC.

Wanneer u een datum-tijd aan een gebruiker presenteert, moet u zich aanpassen aan de verwachte tijdzone. Gebruik bij het serialiseren van een datum-tijd-waarde de ISO 8601-tekenreeksindelingen. Zie specifiek het antwoord van VickyArora voor Oracle (ik ben een Postgres-persoon). Zorg ervoor dat u het document aandachtig leest en oefen door te experimenteren om het gedrag van uw database volledig te begrijpen. De SQL-specificatie geeft in dit opzicht niet veel weer en het gedrag varieert sterk.

java.sql

Onthoud dat wanneer u Java en JDBC gebruikt, u de java.sql.Timestamp gebruikt en gerelateerde gegevenstypen. Ze zijn altijd automatisch in UTC. Verwacht in de toekomst dat JDBC-stuurprogramma's worden bijgewerkt om rechtstreeks gebruik te maken van de nieuwe gegevenstypen die zijn gedefinieerd in het java.time-framework dat is ingebouwd in Java 8 en hoger.

java.time

De oude klassen zijn achterhaald door java.time. Leer java.time gebruiken terwijl je de oude java.util.Date/.Calendar vermijdt en je programmeerleven veel aangenamer maakt.

Totdat uw JDBC-stuurprogramma is bijgewerkt, kunt u de in java.time ingebouwde conversiemethoden gebruiken. Zie hierna voorbeelden, waar Instant is een moment in UTC en ZonedDateTime is een Instant aangepast in een tijdzone.

Instant instant = myJavaSqlTimestamp.toInstant();
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Om de andere kant op te gaan.

java.sql.Timestamp myJavaSqlTimestamp = java.sql.Timestamp.from( zdt.toInstant() );

Als je de originele tijdzone nodig hebt, sla deze dan op

Als uw zakelijke vereisten de tijdzone van de oorspronkelijke invoergegevens belangrijk vinden om te onthouden, sla die dan expliciet op als een aparte kolom in uw databasetabel. U kunt een offset-van-UTC gebruiken, maar dat geeft geen volledige informatie. Een tijdzone is een offset plus een reeks regels voor het verleden, het heden en de toekomst van anomalieën zoals zomertijd. Een juiste tijdzonenaam is dus het meest geschikt, zoals America/Montreal .

Alleen datum is dubbelzinnig

U zei dat u veel alleen-datumwaarden verzamelt, zonder tijd van de dag en zonder tijdzone. De klasse daarvoor in java.time is LocalDate . Net als bij LocalTime en LocalDateTime , het gedeelte "Lokaal..." betekent geen specifieke plaats, dus geen tijdzone en dus geen punt op de tijdlijn -- heeft geen echte betekenis.

Houd er rekening mee dat een waarde met alleen datum per definitie dubbelzinnig is. Op elk willekeurig moment varieert de datum over de hele wereld. Bijvoorbeeld, net na middernacht in Parijs is Frankrijk een nieuwe dag, maar in Montréal Québec is de datum nog steeds "gisteren".

Meestal is in het bedrijfsleven een bepaalde tijdzone impliciet, zelfs onbewust intuïtief. Onbewuste intuïtie over datapunten werkt meestal niet goed op de lange termijn, vooral in software. Het is beter om expliciet te maken welke tijdzone bedoeld was. U kunt de bedoelde zone naast de datum opslaan, zoals een andere kolom in de databasetabel, of u kunt een opmerking maken in uw programmeercode. Ik geloof dat het veel beter en veiliger zou zijn om een ​​datum-tijdwaarde op te slaan. Dus hoe zetten we een date-only om in een date-time?

Vaak is een nieuwe dag het moment na middernacht, het eerste moment van de dag. Je zou denken dat dit de tijd van de dag 00:00:00.0 . betekent maar niet altijd. Zomertijd (DST) en mogelijk andere afwijkingen kunnen het eerste moment naar een andere wandkloktijd duwen. Laat java.time de juiste tijd van de dag bepalen voor het eerste moment dat door de LocalDate gaat class en zijn atStartOfDay methode.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
LocalDate today = LocalDate.now( zoneId );
ZonedDateTime todayStart = today.atStartOfDay( zoneId );

In sommige zakelijke contexten kan een nieuwe dag worden gedefinieerd (of aangenomen) als kantooruren. Stel bijvoorbeeld dat een uitgever in New York 9 uur 's ochtends in hun lokale tijd bedoelt als ze zeggen "de conceptversie van het boek moet uiterlijk op 2 januari worden ingediend". Laten we die tijd van de dag voor die datum in die tijdzone nemen.

ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.of( 2016 , 1 , 2 , 9 , 0 , 0 , 0 , zoneId );

Wat betekent dat voor de auteur die in Nieuw-Zeeland werkt? Pas je aan in haar specifieke tijdzone voor presentatie aan haar door withZoneSameInstant te bellen .

ZoneId zoneId_Pacific_Auckland = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdt_Pacific_Auckland = zdt.withZoneSameInstant( zoneId_Pacific_Auckland );

Database

Voor databaseopslag transformeren we in een Instant (een moment op de tijdlijn in UTC) en geef door als een java.sql.Timestamp zoals eerder hierboven gezien.

java.sql.Timestamp ts = java.sql.Timestamp.from( zdt.toInstant() );

Wanneer opgehaald uit de database, transformeert u terug naar een New Yorkse datum-tijd. Converteren van java.sql.Timestamp naar een Instant en pas vervolgens een tijdzone toe ZoneId om een ​​ZonedDateTime . te krijgen .

Instant instant = ts.toInstant();
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

Als uw databasestuurprogramma voldoet aan JDBC 4.2 of hoger, kunt u de java.time-typen mogelijk rechtstreeks doorgeven/ophalen in plaats van converteren naar/van java.sql-typen. Probeer de PreparedStatement::setObject en ResultSet::getObject methoden.



  1. Hoe een Oracle-functie aanroepen met een Ref Cursor als Out-parameter van C #?

  2. Een studentendatabase maken met Microsoft Access

  3. COUNT(*) selecteren met DISTINCT

  4. Index gebruiken, tijdelijk gebruiken, filesort gebruiken - hoe dit op te lossen?