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

Gestion du fuseau horaire dans l'application Web

Lisez la question sur les meilleures pratiques en matière d'heure d'été et de fuseau horaire. Le vôtre est essentiellement un doublon.

Serveurs en UTC

Oui, généralement, les serveurs doivent avoir leur système d'exploitation défini sur UTC comme fuseau horaire, ou s'il n'est pas fourni, utilisez GMT ou le fuseau horaire Reykjavík Islande. Votre implémentation Java sélectionne probablement ce paramètre comme son propre fuseau horaire par défaut actuel.

Spécifiez le fuseau horaire

Mais ne dépendez pas du fuseau horaire défini sur UTC. Un administrateur système pourrait le changer. Et tout code Java dans n'importe quel thread de n'importe quelle application de votre JVM peut modifier le fuseau horaire par défaut actuel de la JVM au moment de l'exécution en appelant TimeZone.setDefault . Au lieu de cela, prenez l'habitude de toujours spécifier le fuseau horaire souhaité/attendu en passant l'argument facultatif dans votre code Java.

Je considère que c'est un défaut de conception que tout cadre date-heure rende le fuseau horaire facultatif. Être facultatif crée une quantité infinie de confusion parce que les programmeurs, comme tout le monde, pensent inconsciemment en termes de leur propre fuseau horaire personnel à moins d'y être invités. Trop souvent, dans le travail sur la date et l'heure, aucune attention n'est accordée à la question. Ajoutez le problème que la valeur par défaut de la JVM varie. Au fait, idem pour Locale , mêmes problèmes, doivent toujours être spécifiés explicitement.

UTC

Votre logique métier, votre stockage de données et vos échanges de données doivent presque toujours être effectués en UTC. Presque toutes les bases de données ont une fonctionnalité pour ajuster toute entrée en UTC et stocker en UTC.

Lorsque vous présentez une date-heure à un utilisateur, adaptez-vous au fuseau horaire attendu. Lors de la sérialisation d'une valeur date-heure, utilisez les formats de chaîne ISO 8601. Voir la réponse de VickyArora pour Oracle spécifiquement (je suis une personne Postgres). Assurez-vous de lire attentivement la documentation et de vous entraîner en expérimentant pour bien comprendre le comportement de votre base de données. La spécification SQL ne précise pas grand-chose à cet égard et le comportement varie considérablement.

java.sql

N'oubliez pas que lorsque vous utilisez Java et JDBC, vous utiliserez le java.sql.Timestamp et les types de données associés. Ils sont toujours en UTC, automatiquement. À l'avenir, attendez-vous à voir les pilotes JDBC mis à jour pour utiliser directement les nouveaux types de données définis dans le framework java.time intégré à Java 8 et versions ultérieures.

java.time

Les anciennes classes sont dépassées par java.time. Apprenez à utiliser java.time tout en évitant l'ancien java.util.Date/.Calendar et rendre votre vie de programmation beaucoup plus agréable.

Jusqu'à ce que votre pilote JDBC soit mis à jour, vous pouvez utiliser les méthodes pratiques de conversion intégrées à java.time. Voir les exemples suivants, où Instant est un moment en UTC et ZonedDateTime est un Instant ajusté dans un fuseau horaire.

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

Pour aller dans l'autre sens.

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

Si vous avez besoin du fuseau horaire d'origine, stockez-le

Si les besoins de votre entreprise considèrent que le fuseau horaire des données d'entrée d'origine est important, à retenir, stockez-le explicitement dans une colonne distincte de votre table de base de données. Vous pouvez utiliser un décalage depuis UTC, mais cela ne fournit pas d'informations complètes. Un fuseau horaire est un décalage plus un ensemble de règles pour le traitement passé, présent et futur des anomalies telles que l'heure d'été. Ainsi, un nom de fuseau horaire approprié est le plus approprié, tel que America/Montreal .

La date uniquement est ambiguë

Vous avez dit que vous collectiez de nombreuses valeurs de date uniquement, sans heure du jour et sans fuseau horaire. La classe pour cela dans java.time est LocalDate . Comme avec LocalTime et LocalDateTime , la partie "Local…" signifie aucune localité particulière, donc pas de fuseau horaire, et donc pas un point sur la chronologie -- n'a pas de sens réel.

Gardez à l'esprit qu'une valeur de date uniquement est ambiguë par définition. À tout moment, la date varie dans le monde entier. Par exemple, juste après minuit à Paris France est un nouveau jour mais à Montréal Québec la date est toujours « hier ».

Habituellement, dans les affaires, un fuseau horaire est implicite, voire inconsciemment intuitif. L'intuition inconsciente sur les points de données a tendance à ne pas bien fonctionner à long terme, en particulier dans les logiciels. Mieux vaut préciser le fuseau horaire prévu. Vous pouvez stocker la zone prévue à côté de la date, comme une autre colonne dans la table de la base de données, ou vous pouvez faire un commentaire dans votre code de programmation. Je pense qu'il serait beaucoup mieux et plus sûr de stocker une valeur date-heure. Alors, comment transformer une date uniquement en date-heure ?

Souvent, un nouveau jour est le moment après minuit, le premier moment de la journée. Vous pourriez penser que cela signifie l'heure du jour 00:00:00.0 mais pas toujours. L'heure d'été (DST) et éventuellement d'autres anomalies peuvent pousser le premier moment à une heure différente. Laissez java.time déterminer l'heure correcte du jour pour le premier moment en passant par le LocalDate classe et son atStartOfDay méthode.

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

Dans certains contextes commerciaux, un nouveau jour peut être défini (ou supposé) comme étant des heures ouvrables. Par exemple, supposons qu'un éditeur à New York signifie 9 heures du matin dans son heure locale lorsqu'il dit "le brouillon du livre est dû pour le 2 janvier". Obtenons cette heure du jour pour cette date dans ce fuseau horaire.

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

Qu'est-ce que cela signifie pour l'auteur travaillant en Nouvelle-Zélande ? Adaptez-vous à son fuseau horaire particulier pour lui présenter en appelant withZoneSameInstant .

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

Base de données

Pour le stockage de la base de données, nous nous transformons en un Instant (un moment sur la chronologie en UTC) et passer comme java.sql.Timestamp comme vu précédemment ci-dessus.

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

Une fois extrait de la base de données, retransformez-le en une date-heure de New York. Convertir depuis java.sql.Timestamp à un Instant , puis appliquez un fuseau horaire ZoneId pour obtenir un ZonedDateTime .

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

Si votre pilote de base de données est conforme à JDBC 4.2 ou version ultérieure, vous pourrez peut-être transmettre/récupérer les types java.time directement plutôt que de convertir vers/depuis les types java.sql. Essayez le PreparedStatement::setObject et ResultSet::getObject méthodes.