Cela dépend du type de rendez-vous.
Il existe deux types de rendez-vous :
- Un moment, un point précis sur la chronologie, en ignorant tout changement des règles de fuseau horaire.
Exemple :lancement d'une fusée. - Une date et une heure de la journée qui doivent s'adapter aux modifications des règles de fuseau horaire.
Exemple :visite médicale/dentaire.
Instant
Si nous réservons le lancement d'une fusée, par exemple, nous ne nous soucions pas de la date et de l'heure de la journée. Nous ne nous soucions que du moment où (a) les cieux s'alignent et (b) nous nous attendons à un temps favorable.
Si dans l'intervalle les politiciens changent les règles du fuseau horaire en vigueur sur notre site de lancement ou dans nos bureaux, cela n'a aucun effet sur notre rendez-vous de lancement. Si les politiciens qui régissent notre site de lancement adoptent l'heure d'été (DST) , le moment de notre lancement reste le même. Si les politiciens qui gouvernent nos bureaux décident de changer l'heure une demi-heure plus tôt à cause des relations diplomatiques avec un pays voisin, le moment de notre lancement reste le même.
Pour un tel rendez-vous, oui, votre approche serait correcte. Vous enregistreriez le rendez-vous en UTC
en utilisant une colonne de type TIMESTAMP WITH TIME ZONE
. Lors de la récupération, ajustez-vous dans n'importe quel fuseau horaire que l'utilisateur préfère.
Bases de données telles que Postgres
utiliser n'importe quelle information de fuseau horaire accompagnant une entrée pour s'adapter à l'UTC, puis disposer de cette information de fuseau horaire. Lorsque vous récupérez la valeur de Postgres, elle représentera toujours une date avec l'heure du jour telle qu'elle est vue en UTC. Attention, certains outils ou intergiciels peuvent avoir l'anti-fonctionnalité d'appliquer un fuseau horaire par défaut entre la récupération de la base de données et la livraison au programmeur. Mais soyez clair :Postgres enregistre et récupère toujours les valeurs de type TIMESTAMP WITH TIME ZONE
en UTC, toujours UTC, et offset-from-UTC
de zéro heures-minutes-secondes.
Voici un exemple de code 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 ) ;
Pour voir le même moment en UTC, convertissez en un Instant
. Un Instant
l'objet représente toujours un moment vu en UTC.
Instant launchInstant = launchMomentAsSeenInRome.toInstant() ; // Adjust from Rome time zone to UTC.
Le Z
à la fin de l'exemple de chaîne ci-dessus est la notation standard pour UTC, et se prononce "Zulu".
Malheureusement, l'équipe JDBC 4.2 a négligé d'exiger la prise en charge de Instant
ou ZonedDateTime
les types. Donc, votre pilote JDBC
peut ou non être capable de lire/écrire de tels objets dans votre base de données. Sinon, convertissez simplement en OffsetDateTime
. Les trois types représentent un moment, un point spécifique sur la chronologie. Mais OffsetDateTime
a le support requis par JDBC
4.2
pour des raisons qui m'échappent.
OffsetDateTime odtLaunchAsSeenInRome = launchMomentAsSeenInRome.toOffsetDateTime() ;
Ecriture dans la base de données.
myPreparedStatement.setObject( … , odtLaunchAsSeenInRome ) ;
Récupération depuis la base de données.
OffsetDateTime launchMoment = myResultSet.getObject( … , OffsetDateTime.class ) ;
Ajustez-vous au fuseau horaire de New York souhaité par votre utilisateur.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime launchAsSeenInNewYork = launchMoment.atZoneSameInstant( zoneAmericaNewYork ) ;
Vous pouvez voir tous les code ci-dessus exécutés en direct sur IdeOne.com .
Soit dit en passant, le suivi des événements passés est également traité comme un moment. Quand le patient est-il réellement arrivé au rendez-vous, quand le client a-t-il payé la facture, quand un nouvel employé a-t-il signé ses documents, quand le serveur a-t-il planté… tout cela est suivi comme un moment en UTC. Comme indiqué ci-dessus, il s'agirait généralement d'un Instant
, bien que ZonedDateTime
&OffsetDateTime
représentent aussi un moment. Pour la base de données, utilisez TIMESTAMP WITH TIME ZONE
(pas WITHOUT
).
Heure de la journée
Je suppose que la plupart des applications orientées entreprise se concentrent sur les rendez-vous de l'autre type, où nous visons une date avec une heure de la journée plutôt qu'un moment spécifique.
Si l'utilisateur prend rendez-vous avec son fournisseur de soins de santé pour examiner les résultats d'un test, il le fait pour une heure particulière de la journée à cette date. Si entre-temps les politiciens modifient les règles de leur fuseau horaire, avançant ou retardant l'horloge d'une heure ou d'une demi-heure ou de toute autre durée, la date et l'heure de ce rendez-vous médical restent les mêmes. En réalité, le point de la chronologie du rendez-vous d'origine sera modifié après que les politiciens auront changé le fuseau horaire, passant à un point antérieur/ultérieur sur la chronologie.
Pour de tels rendez-vous, nous ne le faisons pas stocker la date et l'heure du jour telles qu'elles sont vues en UTC. Nous ne le faisons pas utiliser le type de colonne de base de données TIMESTAMP WITH TIME ZONE
.
Pour de tels rendez-vous, nous stockons la date avec l'heure du jour sans tenir compte du fuseau horaire. Nous utilisons une colonne de base de données de type TIMESTAMP WITHOUT TIME ZONE
(remarquez WITHOUT
plutôt que WITH
). Le type correspondant en Java est LocalDateTime
.
LocalDate medicalApptDate = LocalDate.of( 2021 , 1 , 23 ) ;
LocalTime medicalApptTime = LocalTime.of( 21 , 0 ) ;
LocalDateTime medicalApptDateTime = LocalDateTime.of( medicalApptDate , medicalApptTime ) ;
Écrivez cela dans la base de données.
myPreparedStatement.setObject( … , medicalApptDateTime ) ;
Soyez clair à ce sujet :un LocalDateTime
l'objet ne le fait pas représenter un moment, n'est pas un point précis de la chronologie. Un LocalDateTime
l'objet représente une plage de possibles moments le long d'environ 26-27 heures de la chronologie (la gamme de fuseaux horaires autour du globe). Pour donner un vrai sens à un LocalDateTime
, nous devons associer un fuseau horaire prévu.
Pour ce fuseau horaire prévu, utilisez une deuxième colonne pour stocker l'identificateur de zone. Par exemple, les chaînes Europe/Rome
ou America/New_York
. Voir liste des noms de zone
.
ZoneId zoneEuropeRome = ZoneId.of( "Europe/Rome" ) ;
Écrivez cela dans la base de données sous forme de texte.
myPreparedStatement.setString( … , zoneEuropeRome ) ;
Récupération. Récupérez le nom de la zone sous forme de texte et instanciez un ZoneId
objet.
LocalDateTime medicalApptDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
ZoneId medicalApptZone = ZoneId.of( myResultSet.getString( … ) ) ;
Assemblez ces deux éléments pour déterminer un moment représenté par un ZonedDateTime
objet. Faites-le dynamiquement lorsque vous devez planifier un calendrier. Mais ne faites pas stocker l'instant. Si les politiciens redéfinissent le ou les fuseaux horaires à l'avenir, un moment différent doit alors être calculé.
ZonedDateTime medicalApptAsSeenInCareProviderZone = ZonedDateTime.of( medicalApptDateTime , medicalApptZone ) ;
L'utilisateur se rend à New York aux États-Unis. Ils ont besoin de savoir quand appeler le fournisseur de soins de santé à Milan en Italie selon les horloges murales de leur emplacement temporaire à New York. Ajustez donc d'un fuseau horaire à l'autre. Même moment, heure différente de l'horloge murale.
ZoneId zoneAmericaNewYork = ZoneId.of( "America/New_York" ) ;
ZonedDateTime medicalApptAsSeenInNewYork = medicalApptAsSeenInCareProviderZone.withZoneSameInstant( zoneAmericaNewYork ) ;
tzdata
Sachez que si les règles de vos fuseaux horaires souhaités peuvent changer, vous devez mettre à jour la copie des définitions de fuseaux horaires sur vos ordinateurs.
Java contient sa propre copie de tzdata , tout comme le moteur de base de données Postgres. Et votre système d'exploitation hôte également. Ce code particulier présenté ici ne nécessite que Java pour être à jour. Si vous utilisez Postgres pour effectuer des ajustements de fuseau horaire, ses tzdata doit également être à jour. Et pour la journalisation et autres, votre système d'exploitation hôte doit être tenu à jour. Pour une bonne surveillance de l'horloge par l'utilisateur, le système d'exploitation de sa machine cliente doit également être à jour.
Attention :les politiciens du monde entier ont tendance à changer de fuseau horaire à une fréquence surprenante, et souvent sans préavis.
À propos de java.time
Le java.time
framework est intégré à Java 8 et versions ultérieures. Ces classes supplantent l'ancien héritage gênant
les classes date-heure telles que java.util.Date
, Calendar
, &SimpleDateFormat
.
Pour en savoir plus, consultez le tutoriel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .
Le Joda-Time projet, maintenant en mode maintenance , conseille la migration vers java.time cours.
Vous pouvez échanger java.time objets directement avec votre base de données. Utilisez un pilote JDBC
conforme à JDBC 4.2
ou plus tard. Pas besoin de chaînes, pas besoin de java.sql.*
Des classes. Hibernate 5 et JPA 2.2 prennent en charge java.time .
Où obtenir les classes java.time ?
- Java SE 8
, Java SE 9
, Java SE 10
, Java SE 11
, et versions ultérieures - Partie de l'API Java standard avec une implémentation groupée.
- Java 9 apporté quelques fonctionnalités et correctifs mineurs.
- Java SE 6
et Java SE 7
- La plupart du temps java.time la fonctionnalité est rétroportée vers Java 6 et 7 dans ThreeTen-Backport .
- Android
- Versions ultérieures des implémentations groupées Android (26+) de java.time cours.
- Pour les versions antérieures d'Android (<26), un processus appelé Désucrage API
apporte un sous-ensemble de java.time
fonctionnalité non intégrée à l'origine dans Android.
- Si le désucrage n'offre pas ce dont vous avez besoin, le ThreeTenABP le projet adapte ThreeTen-Backport (mentionné ci-dessus) à Android. Voir Comment utiliser ThreeTenABP… .