sql >> Databasteknik >  >> RDS >> Oracle

Hantering av tidszon i webbapplikation

Läs de bästa metoderna för sommartid och tidszon i fråga. Din är i princip en dubblett.

Server i UTC

Ja, i allmänhet bör servrar ha sitt operativsystem inställt på UTC som tidszon, eller om det inte tillhandahålls använda GMT eller Reykjavik Islands tidszon. Din Java-implementering plockar förmodligen upp den här inställningen som sin egen nuvarande standardtidszon.

Ange tidszon

Men lita inte på att tidszonen är inställd på UTC. En systemadministratör kan ändra det. Och vilken Java-kod som helst i vilken tråd som helst i alla appar i din JVM kan ändra JVM:s nuvarande standardtidszon vid körning genom att anropa TimeZone.setDefault . Så ta istället för vana att alltid ange önskad/förväntad tidszon genom att skicka det valfria argumentet i din Java-kod.

Jag anser att det är ett designfel att varje datum-tid-ramverk skulle göra tidszonen valfri. Att vara valfri skapar oändliga mängder förvirring eftersom programmerare, som alla andra, omedvetet tänker i termer av sin egen personliga tidszon om de inte uppmanas. Så alltför ofta i datum-tid-arbete ägnas ingen uppmärksamhet åt frågan. Lägg till problemet att JVM-standarden varierar. Förresten, dito för Locale , samma problem, bör alltid anges explicit.

UTC

Din affärslogik, datalagring och datautbyte bör nästan alltid göras i UTC. Nästan varje databas har en funktion för att justera indata till UTC och lagra i UTC.

När du presenterar ett datum-tid för en användare, justera till den förväntade tidszonen. Använd ISO 8601-strängformaten när du serialiserar ett datum-tid-värde. Se svaret av VickyArora för Oracle specifikt (jag är en Postgres-person). Se till att läsa dokumentet noggrant och öva genom att experimentera för att helt förstå din databas beteende. SQL-specifikationen förklarar inte särskilt mycket i detta avseende, och beteendet varierar kraftigt.

java.sql

Kom ihåg att när du använder Java och JDBC kommer du att använda java.sql.Timestamp och relaterade datatyper. De är alltid i UTC, automatiskt. I framtiden förvänta dig att se JDBC-drivrutiner uppdateras för att direkt använda de nya datatyperna som definieras i java.time-ramverket inbyggt i Java 8 och senare.

java.time

De gamla klasserna är föråldrade av java.time. Lär dig använda java.time samtidigt som du undviker den gamla java.util.Date/.Calendar och gör ditt programmeringsliv mycket trevligare.

Tills din JDBC-drivrutin har uppdaterats kan du använda konverteringsmetoderna inbyggda i java.time. Se exempel härnäst, där Instant är ett ögonblick i UTC och ZonedDateTime är ett ögonblick som justeras till en tidszon.

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

Att gå åt andra hållet.

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

Om du behöver den ursprungliga tidszonen, lagra den

Om dina affärskrav anser att den ursprungliga indatans tidszon är viktig, att komma ihåg, lagra den explicit som en separat kolumn i din databastabell. Du kan använda en offset-från-UTC, men det ger inte fullständig information. En tidszon är en offset plus en uppsättning regler för tidigare, nuvarande och framtida hantering av anomalier som sommartid. Så ett korrekt tidszonnamn är mest lämpligt som America/Montreal .

Endast datum är tvetydig

Du sa att du samlar in många värden endast för datum, utan tid på dagen och utan tidszon. Klassen för det i java.time är LocalDate . Som med LocalTime och LocalDateTime , "Lokal..."-delen betyder ingen speciell ort, så därför har ingen tidszon, och alltså inte en punkt på tidslinjen -- ingen verklig betydelse.

Tänk på att ett värde endast för datum är tvetydigt per definition. Vid varje givet ögonblick varierar datumet runt om i världen. Till exempel, strax efter midnatt i Paris är Frankrike en ny dag, men i Montréal Québec är datumet fortfarande "igår".

Vanligtvis i affärer är någon tidszon implicit, till och med omedvetet intuitad. Omedveten intuition om datapunkter tenderar att inte fungera bra på lång sikt, särskilt i mjukvara. Bättre att tydliggöra vilken tidszon som var avsedd. Du kan lagra den avsedda zonen bredvid datumet, till exempel en annan kolumn i databastabellen, eller så kan du göra en kommentar i din programmeringskod. Jag tror att det skulle vara mycket bättre och säkrare att lagra ett datum-tid-värde. Så hur förvandlar vi ett endast datum till en datum-tid?

Ofta är en ny dag ögonblicket efter midnatt, dagens första ögonblick. Du kanske tror att det betyder tiden på dagen 00:00:00.0 men inte alltid. Sommartid (DST) och möjligen andra anomalier kan flytta det första ögonblicket till en annan väggklocka. Låt java.time bestämma rätt tid på dagen för första ögonblicket genom att gå igenom LocalDate klass och dess atStartOfDay metod.

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

I vissa affärssammanhang kan en ny dag definieras (eller antas) vara kontorstid. Säg till exempel att en förläggare i New York menar 09:00 lokal tid när de säger "bokutkastet ska vara klart senast den 2 januari". Låt oss få den tiden på dagen för det datumet i den tidszonen.

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

Vad betyder det för författaren som arbetar i Nya Zeeland? Anpassa sig till hennes specifika tidszon för presentation för henne genom att ringa withZoneSameInstant .

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

Databas

För databaslagring omvandlar vi till en Instant (ett ögonblick på tidslinjen i UTC) och skicka som en java.sql.Timestamp som sett tidigare ovan.

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

När den hämtas från databasen, omvandla tillbaka till en New York datum-tid. Konvertera från java.sql.Timestamp till ett Instant , använd sedan en tidszon ZoneId för att få en ZonedDateTime .

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

Om din databasdrivrutin överensstämmer med JDBC 4.2 eller senare, kanske du kan skicka/hämta java.time-typerna direkt istället för att konvertera till/från java.sql-typerna. Prova PreparedStatement::setObject och ResultSet::getObject metoder.



  1. Hur man skickar boolesk parameter till Oracle-proceduren C#

  2. CONVERT_TZ() Exempel – MySQL

  3. PostgreSQL:Skapa tabell om det inte finns AS

  4. Hur man grupperar en rapport i Access 2016