web-dev-qa-db-fra.com

Comment gérer correctement TimeZone dans SQL Server?

Mon serveur de développement local est au Moyen-Orient, mais mon serveur de production est au Royaume-Uni.

Je dois montrer la date à l'utilisateur dans son fuseau horaire. Par exemple, si un utilisateur se trouve en Arabie saoudite, je dois afficher l'heure selon le format de l'Arabie saoudite.

Dois-je créer une nouvelle table de base de données appelée TimeZone et enregistrer l'heure en UTC?

39
user960567

Il n'y a malheureusement pas de solution miracle pour cela. L'internationalisation d'une application devrait faire partie des toutes premières discussions sur la conception, car elle va vraiment au cœur de nombreux domaines différents, y compris les comparaisons date/heure et le formatage des sorties.

Quoi qu'il en soit, pour se mettre sur la bonne voie, il est essentiel de stocker les informations de fuseau horaire avec l'heure. En d'autres termes, sachant que la date/heure 20130407 14:50 est sans signification sans que (a) inclue le décalage UTC actuel du fuseau horaire(note 1), ou (b) en veillant à ce que toute la logique insérant ces valeurs soit d'abord convertie en un certain décalage fixe (très probablement 0). Sans aucune de ces choses, deux valeurs de temps données sont incomparable, et les données sont corrompues. (Cette dernière méthode joue avec le feu(note 2), au fait; ne fais pas ça.)

Dans SQL Server 2008+, vous pouvez stocker le décalage avec l'heure directement en utilisant le type de données datetimeoffset . (Pour être complet, en 2005 et avant, j'ajouterais une deuxième colonne pour stocker la valeur de décalage UTC alors en cours (en minutes).)

Cela facilite la tâche d'une application de type bureau, car ces plates-formes disposent normalement de mécanismes pour convertir automatiquement une date/heure + fuseau horaire en heure locale, puis formater pour la sortie, le tout en fonction des paramètres régionaux de l'utilisateur.

Pour le Web, qui est une architecture intrinsèquement déconnectée, même avec les données dorsales correctement configurées, c'est plus complexe car vous avez besoin d'informations sur le client pour pouvoir effectuer la conversion et/ou le formatage. Cela se fait généralement via les paramètres de préférence de l'utilisateur (l'application convertit/formate les choses avant la sortie), ou simplement en affichant les choses avec le même format fixe et le même décalage de fuseau horaire pour tout le monde (ce que fait actuellement la plate-forme Stack Exchange).

Vous pouvez voir comment, si les données dorsales ne sont pas configurées correctement, très rapidement, cela deviendra compliqué et hacky. Je ne recommanderais pas d'emprunter l'un de ces chemins, car vous vous retrouverez avec plus de problèmes sur la ligne.

Remarque 1:

Le décalage UTC d'un fuseau horaire n'est pas fixe: pensez à l'heure d'été lorsque le décalage UTC d'une zone varie de plus ou moins une heure. De plus, les dates d'heure d'été des zones varient régulièrement. Donc, en utilisant datetimeoffset (ou un composite de local time et UTC offset at that time) permet une récupération maximale des informations.

Note 2:

Il s'agit de contrôler les entrées de données. Bien qu'il n'y ait aucun moyen infaillible de valider les valeurs entrantes, il est préférable d'appliquer une norme simple qui n'implique pas de calculs. Si une API publique attend un type de données qui inclut un décalage, cette exigence sera claire pour l'appelant.

Si ce n'était pas le cas, l'appelant doit se fier à la documentation (s'ils la lisent), ou le calcul est mal fait, etc. ou même simplement web/base de données sur des serveurs séparés comme le cas ici).

Le stockage de l'offset tue de toute façon deux oiseaux avec une pierre; et même si cela n'est pas requis maintenant, cela rend la possibilité disponible plus tard si nécessaire. Certes, cela prend plus de stockage, mais je pense que cela vaut le coup parce que les données sont perdues si elles ne sont jamais enregistrées en premier lieu.

51
Jon Seigel

J'ai développé une solution complète pour la conversion de fuseau horaire dans SQL Server. Voir Prise en charge du fuseau horaire SQL Server sur GitHub .

Il gère correctement les conversions entre les fuseaux horaires et vers et depuis UTC, y compris l'heure d'été.

Son concept est similaire à celui de la solution "T-SQL Toolbox" décrite dans la réponse d'adss, mais il utilise les fuseaux horaires IANA/Olson/TZDB les plus courants au lieu de Microsoft, et il inclut des utilitaires pour conserver les données en tant que nouvelles versions de la TZDB sort.

Exemple d'utilisation (pour répondre à l'OP):

SELECT Tzdb.UtcToLocal(sysutcdatetime(), 'Asia/Riyadh') as CurrentTimeInSaudiArabia

Voir le fichier Lisez-moi sur GitHub pour des API supplémentaires et des explications détaillées.

Notez également que comme il s'agit d'une solution basée sur UDF, elle n'est pas conçue avec les performances comme objectif principal. Il serait toujours préférable que cette fonctionnalité soit intégrée à SQL Server, de la même manière que le CONVERT_TZ la fonction existe dans Oracle et MySQL.

15
Matt Johnson-Pint

J'ai développé et publié le projet "T-SQL Toolbox" sur codeplex pour aider quiconque éprouve des difficultés avec la gestion du temps et du fuseau horaire dans SQL Server. Il est open source et totalement gratuit.

Il offre des UDF de conversion datetime faciles à utiliser en T-SQL simple avec des tableaux de configuration supplémentaires prêts à l'emploi.

Dans votre exemple, vous pouvez utiliser l'exemple suivant:

SELECT [DateTimeUtil].[UDF_ConvertLocalToLocalByTimezoneIdentifier] (
    'GMT Standard Time', -- the original timezone in which your datetime is stored
    'Middle East Standard Time', -- the target timezone for your user
    '2014-03-30 01:55:00' -- the original datetime you want to convert
)

Cela renverra la valeur datetime convertie pour votre utilisateur.

Une liste de tous les fuseaux horaires pris en charge se trouve dans le tableau "DateTimeUtil.Timezone" également fourni dans la base de données T-SQL Toolbox.

7
adss

SQL Server a apporté des modifications à partir de 2005, où le fuseau horaire interne est enregistré en UTC. Cela était dû en grande partie à la géoréplication et aux projecteurs HA impliquant l'envoi de journaux, et le fait que les heures d'expédition des journaux soient enregistrées dans différents fuseaux horaires empêchait l'ancienne méthode de les restaurer.

Ainsi, tout sauvegarder en interne en temps UTC a permis à SQL Server de bien fonctionner à l'échelle mondiale. C'est l'une des raisons pour lesquelles l'heure d'été est un peu difficile à gérer dans Windows, car d'autres produits MS tels que Outlook enregistrent également la date/l'heure en interne en UTC et créent un décalage qui doit être corrigé.

Je travaille dans une entreprise où nous avons des milliers de serveurs (pas des serveurs MS SQL, mais toutes sortes de serveurs) répartis dans le monde entier, et si nous ne forçions pas spécifiquement tout à passer par UTC, nous deviendrions tous fous très rapidement.

6
Ali Razeghi

Il me manque peut-être quelque chose d'évident, mais si tout ce que vous voulez faire est d'afficher la date en utilisant le fuseau horaire de l'utilisateur et le format de la langue, le moyen le plus simple est de toujours stocker les dates en UTC dans la base de données et de laisser l'application cliente convertir de l'UTC en local fuseau horaire et utilisez les paramètres régionaux de l'utilisateur pour formater la date en conséquence.

1
20c