Dipende dal tipo di appuntamento.
Esistono due tipi di appuntamenti:
- Un momento, un punto specifico sulla sequenza temporale, ignorando eventuali modifiche alle regole del fuso orario.
Esempio:lancio di un razzo. - Una data e un'ora del giorno che dovrebbero adattarsi alle modifiche alle regole del fuso orario.
Esempio:visita medica/dentistica.
Momento
Se stiamo prenotando il lancio di un razzo, ad esempio, non ci interessa la data e l'ora. Ci interessa solo il momento in cui (a) i cieli si allineano e (b) ci aspettiamo un clima favorevole.
Se nel frattempo i politici cambiano le regole del fuso orario in uso presso il nostro sito di lancio o presso i nostri uffici, ciò non ha effetto sul nostro appuntamento di lancio. Se i politici che governano il nostro sito di lancio adottano Ora legale (DST) , il momento del nostro lancio rimane lo stesso. Se i politici che governano i nostri uffici decidono di cambia l'orologio mezz'ora prima a causa delle relazioni diplomatiche con un paese vicino, il momento del nostro lancio rimane lo stesso.
Per un tale appuntamento, sì, il tuo approccio sarebbe corretto. Registrare l'appuntamento in UTC
utilizzando una colonna di tipo TIMESTAMP WITH TIME ZONE
. Al momento del recupero, regola il fuso orario preferito dall'utente.
Database come Postgres
usa qualsiasi informazione sul fuso orario che accompagna un input per adattarla all'UTC, quindi elimina tali informazioni sul fuso orario. Quando recuperi il valore da Postgres, rappresenterà sempre una data con l'ora del giorno come si vede in UTC. Attenzione, alcuni strumenti o middleware potrebbero avere l'anti-funzione di applicare un fuso orario predefinito tra il recupero dal database e la consegna al programmatore. Ma sia chiaro:Postgres salva e recupera sempre valori di tipo TIMESTAMP WITH TIME ZONE
in UTC, sempre in UTC e offset-from-UTC
di zero ore-minuti-secondi.
Ecco alcuni esempi di codice Java.
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 ) ;
Per vedere lo stesso momento in UTC, converti in un Instant
. Un Instant
l'oggetto rappresenta sempre un momento visto in UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
La Z
alla fine dell'esempio di stringa sopra c'è la notazione standard per UTC e si pronuncia "Zulu".
Sfortunatamente il team JDBC 4.2 ha trascurato di richiedere il supporto per Instant
o ZonedDateTime
tipi. Quindi il tuo driver JDBC
potrebbe o meno essere in grado di leggere/scrivere tali oggetti nel database. In caso contrario, converti semplicemente in OffsetDateTime
. Tutti e tre i tipi rappresentano un momento, un punto specifico sulla timeline. Ma OffsetDateTime
ha il supporto richiesto da JDBC
4.2
per ragioni che mi sfuggono.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
Scrittura nel database.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Recupero dal database.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Regola il fuso orario di New York desiderato dal tuo utente.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Puoi vedere tutto il codice sopra eseguito dal vivo su IdeOne.com .
A proposito, anche il monitoraggio degli eventi passati viene considerato come un momento. Quando il paziente è effettivamente arrivato all'appuntamento, quando il cliente ha pagato la fattura, quando un nuovo assunto ha firmato i propri documenti, quando si è verificato un crash del server ... tutto questo viene registrato come un momento in UTC. Come discusso in precedenza, di solito sarebbe un Instant
, sebbene ZonedDateTime
&OffsetDateTime
rappresentano anche un momento. Per il database utilizzare TIMESTAMP WITH TIME ZONE
(non WITHOUT
).
Ora del giorno
Mi aspetto che la maggior parte delle app orientate al business si concentrino su appuntamenti dell'altro tipo, in cui miriamo a una data con un'ora del giorno piuttosto che a un momento specifico.
Se l'utente prende un appuntamento con il proprio medico per rivedere i risultati di un test, lo fa per una particolare ora del giorno in quella data. Se nel frattempo i politici cambiano le regole del loro fuso orario, spostando l'orologio avanti o indietro di un'ora o mezz'ora o di qualsiasi altro intervallo di tempo, la data e l'ora dell'appuntamento medico rimangono le stesse. In realtà, il punto della sequenza temporale dell'appuntamento originale verrà modificato dopo che i politici avranno cambiato il fuso orario, spostandosi a un punto precedente/successivo sulla sequenza temporale.
Per tali appuntamenti, non memorizzare la data e l'ora come si vede in UTC. Noi non utilizzare il tipo di colonna del database TIMESTAMP WITH TIME ZONE
.
Per tali appuntamenti, memorizziamo la data con l'ora del giorno senza alcun riguardo al fuso orario. Utilizziamo una colonna del database di tipo TIMESTAMP WITHOUT TIME ZONE
(notare WITHOUT
anziché WITH
). Il tipo corrispondente in Java è LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Scrivilo nel database.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Sii chiaro su questo:un LocalDateTime
oggetto non rappresentano un momento, non un punto specifico della timeline. Un LocalDateTime
l'oggetto rappresenta un intervallo di possibile momenti lungo circa 26-27 ore della linea temporale (l'intervallo di fusi orari in tutto il mondo). Per dare un significato reale a un LocalDateTime
, dobbiamo associare un fuso orario previsto.
Per quel fuso orario previsto, utilizzare una seconda colonna per memorizzare l'identificatore di zona. Ad esempio, le stringhe Europe/Rome
o America/New_York
. Vedi elenco dei nomi delle zone
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Scrivilo nel database come testo.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Recupero. Recupera il nome della zona come testo e crea un'istanza di un ZoneId
oggetto.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Metti insieme questi due pezzi per determinare un momento rappresentato come ZonedDateTime
oggetto. Fallo in modo dinamico quando devi pianificare un calendario. Ma non memorizza il momento. Se i politici ridefiniranno i fusi orari in futuro, allora sarà necessario calcolare un momento diverso.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
L'utente si sta recando a New York negli Stati Uniti. Hanno bisogno di sapere quando chiamare il medico in Milano Italia secondo gli orologi sul muro nella loro posizione temporanea di New York. Quindi regolati da un fuso orario all'altro. Stesso momento, diverso orario dell'orologio da parete.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
tzdata
Tieni presente che se le regole dei fusi orari desiderati potrebbero cambiare, devi aggiornare la copia delle definizioni dei fusi orari sui tuoi computer.
Java contiene una propria copia di tzdata , così come il motore di database Postgres. E anche il tuo sistema operativo host. Questo codice particolare mostrato qui richiede che Java sia aggiornato. Se usi Postgres per apportare modifiche al fuso orario, sono tzdata deve essere anche aggiornato. E per la registrazione e simili, il tuo sistema operativo host dovrebbe essere aggiornato. Per un corretto monitoraggio dell'orologio da parte dell'utente, anche il sistema operativo del computer client deve essere aggiornato.
Attenzione:i politici di tutto il mondo hanno mostrato un debole per cambiare i loro fusi orari con frequenza sorprendente e spesso con scarso preavviso.
Informazioni su java.time
Il java.time
framework è integrato in Java 8 e versioni successive. Queste classi soppiantano la vecchia e problematica eredità
classi data-ora come java.util.Date
, Calendar
, &SimpleDateFormat
.
Per ulteriori informazioni, consulta il Tutorial Oracle . E cerca Stack Overflow per molti esempi e spiegazioni. La specifica è JSR 310 .
Il Joda-Time progetto, ora in modalità di manutenzione , consiglia la migrazione a java.time classi.
Puoi scambiare java.time oggetti direttamente con il tuo database. Utilizzare un driver JDBC
conforme a JDBC 4.2
o più tardi. Non c'è bisogno di stringhe, non c'è bisogno di java.sql.*
classi. Hibernate 5 e JPA 2.2 supportano java.time .
Dove ottenere le classi java.time?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
e versioni successive:parte dell'API Java standard con un'implementazione in bundle.
- Java 9 ha apportato alcune funzionalità e correzioni minori.
- Java SE 6
e Java SE 7
- La maggior parte di java.time la funzionalità è stata trasferita a Java 6 e 7 in ThreeTen-Backport .
- Android
- Versioni successive di Android (26+) implementazioni in bundle di java.time classi.
- Per Android precedente (<26), un processo noto come Dezuccheraggio API
porta un sottoinsieme di java.time
funzionalità non originariamente integrata in Android.
- Se il desugaring non offre ciò di cui hai bisogno, ThreeTenABP il progetto adatta ThreeTen-Backport (menzionato sopra) ad Android. Vedi Come usare ThreeTenABP... .