PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Stockage des rendez-vous dans une base de données SQL telle que Postgres pour une utilisation avec le framework java.time

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 ?