Comment gérer correctement TimeZone dans SQL Server?

34

Mon serveur de développement local est situé 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 est en Arabie Saoudite, je dois afficher l'heure en fonction du format Arabie Saoudite.

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

utilisateur960567
la source
En effet Conversion dates entre UTC et l' heure locale (. PST) dans SQL 2005 http://stackoverflow.com/questions/24797/effectively-converting-dates-between-utc-and-local-ie-pst-time-in- sql-2005
mehdi
S'agit-il d'une application Web ou d'une application de bureau ou ...? La façon dont cela est mis en œuvre varie considérablement en fonction du mécanisme de livraison du client, car ce dont vous avez besoin dépend du côté client.
Jon Seigel
Ce web ainsi que le mobile natif. Oui, cet effet diffère selon les clients.
user960567
1
On dirait que votre question ne concerne pas seulement la base de données elle-même, mais implique également le développement d'applications. Cette question Stack Overflow est un incontournable pour vous , alors: l'heure d' été et Timezone les meilleures pratiques
Gan

Réponses:

48

Malheureusement, il n'y a pas de solution miracle. L'internationalisation d'une application devrait faire partie des toutes premières discussions de conception, car elle se situe au cœur de nombreux domaines, y compris les comparaisons date / heure et le formatage de la sortie.

Quoi qu'il en soit, pour suivre la voie de Doing It Right, il est essentiel de stocker les informations de fuseau horaire avec l'heure . En d’autres termes, réaliser que la date / heure 20130407 14:50n’a pas de sens sans (a) y compris le décalage UTC actuel du fuseau horaire (note 1) , ou le plus probable 0). Sans l'une de ces choses, deux valeurs temporelles données sont incomparables et les données sont corrompues . (Cette dernière méthode consiste à jouer avec le feu (note 2) , d'ailleurs; ne le faites pas.)

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

Cela facilite la tâche des applications de type bureau, car ces plates-formes disposent généralement de mécanismes permettant de convertir automatiquement une date / heure + un fuseau horaire en heure locale, puis de les formater en 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 si les données principales sont correctement configurées, la tâche 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érences utilisateur (l’application convertit / formate les éléments avant la sortie) ou simplement en affichant des éléments avec le même format fixe et le même décalage de fuseau horaire pour tous (comme le fait actuellement la plate-forme Stack Exchange).

Vous pouvez voir comment, si les données back-end ne sont pas configurées correctement, très rapidement, cela va devenir compliqué et hacky. Je ne recommanderais pas de suivre l'une de ces voies, car vous vous retrouverez avec plus de problèmes par la suite.

Note 1:

Le décalage UTC d'un fuseau horaire n'est pas fixe: prenez en compte 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, utiliser datetimeoffset(ou un composé de local timeet UTC offset at that time) résulte en une récupération maximale de l'information.

Note 2:

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

Si ce n’était pas le cas, l’appelant doit s’appuyer sur la documentation (s’il la lit), ou le calcul est mal effectué, etc. ou même simplement Web / base de données sur des serveurs distincts, comme dans le cas présent).

Stocker le décalage tue quand même deux oiseaux avec une pierre; et même si cela n'est pas nécessaire maintenant , la possibilité est disponible ultérieurement si nécessaire. Certes, cela prend plus de mémoire, mais je pense que cela vaut la peine, car les données sont perdues si elles ne sont jamais enregistrées.

Jon Seigel
la source
3
Si j'ai bien compris, un décalage n'est pas la même chose qu'un fuseau horaire ... les heures stockées dans datetimeoffset perdent des informations telles que la reconnaissance de l'heure d'été ... Plus d'infos: stackoverflow.com/tags/timezone/info
Peter McEvoy
1
@PeterMcEvoy DST n'a aucune pertinence ici. datetimeoffsetsignifie "il s'agit de l'heure locale de l'utilisateur / de l'ordinateur lorsque l'événement s'est produit" - nous n'avons pas besoin de savoir si l'heure d'été était ou non effective, car le décalage UTC donné est toujours présent (intégré dans la datetimeoffsetvaleur).
Dai
12

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 l'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 elle utilise les fuseaux horaires IANA / Olson / TZDB plus courants à la place de ceux de Microsoft. Elle inclut des utilitaires permettant de conserver les données dans les nouvelles versions du TZDB sortir.

Exemple d'utilisation (pour répondre au PO):

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

Consultez le fichier readme sur GitHub pour des API supplémentaires et des explications détaillées.

Notez également qu’il s’agit d’une solution basée sur UDF, elle n’est pas conçue avec la performance 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 la CONVERT_TZfonction existe dans Oracle et MySQL.

Matt Johnson-Pint
la source
6

SQL Server a apporté des modifications à partir de 2005 dans lesquelles le fuseau horaire interne est enregistré au format UTC. Cela était dû en grande partie à la géo-réplication et aux projecteurs haute disponibilité impliquant l'envoi de journaux, et le fait que les temps d'envoi des journaux soient enregistrés dans des fuseaux horaires différents rendait impossible la restauration par l'ancienne méthode.

Ainsi, tout économiser en interne dans le temps UTC a permis à SQL Server de fonctionner correctement à l’échelle mondiale. C’est l’une des raisons pour lesquelles il est difficile de gérer l’heure avancée sous Windows, car d’autres produits MS, tels que Outlook, enregistrent également la date et l’heure en interne au format UTC et créent un décalage devant être corrigé.

Je travaille dans une entreprise où nous avons des milliers de serveurs (pas des serveurs MS SQL, mais de nombreux types de serveurs) répartis dans le monde entier, et si nous n'imposions pas tout à UTC, nous deviendrions complètement fous. très rapidement.

Ali Razeghi
la source
5

J'ai développé et publié le projet «T-SQL Toolbox» sur codeplex afin d'aider quiconque se débat avec la gestion de la date / heure et du fuseau horaire dans SQL Server. C'est open source et totalement gratuit à utiliser.

Il offre des fonctions UDF de conversion date / heure simples utilisant T-SQL brut, ainsi que des tables de configuration supplémentaires prêtes à 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 date / heure convertie pour votre utilisateur.

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

adss
la source
Une telle boîte à outils semble être une aide précieuse pour un développeur. Cependant, une fonction scalaire est une boîte noire pour l'optimiseur de requêtes. Cela signifie que lorsque j'applique votre fonction à une colonne, les tables de configuration que vous mentionnez seront consultées autant de fois qu'il y a de lignes dans le jeu de résultats (en supposant que j'utilise la fonction dans la clause SELECT uniquement). Cela ne semble pas être une bonne perspective en termes de performance. Cependant, je n'ai pas vraiment testé votre trousse à outils. Je ne peux donc pas dire avec certitude qu'il ne s'agit que d'une préoccupation générale fondée sur ce que je sais.
Andriy M
Andriy, bien sûr, vous avez raison du point de vue des performances. Les tables de requête UDF ne sont pas conçues pour des opérations de données en masse, mais pour une utilisation facile. Néanmoins, ils fonctionnent assez bien jusqu'à environ 100 000 enregistrements sur ma machine.
Annonce
2
S'il est nécessaire de traiter plus d'enregistrements dans son cas d'utilisation et que les performances importent, vous devriez mieux penser aux données pré-calculées persistantes. Mais même dans ce cas, ces fonctions utilisateur pourraient aider à conserver les valeurs datatime dans les timzones requis.
annonce
De mon point de vue, ce problème ne concerne pas les performances, mais plutôt l’obtention des fonctionnalités de base. Être en mesure de renvoyer des informations précises à partir d'une requête est la première chose à faire. Travailler sur une table temporaire pour mettre en cache des éléments, puis faire référence à celle-ci dans la requête ou à la façon dont vous la gérez pour améliorer les performances devient secondaire.
Action Dan
1

Il se peut que quelque chose d’évident me manque, mais si vous souhaitez uniquement afficher la date en utilisant le fuseau horaire de l’utilisateur et le format de langue, le moyen le plus simple est de toujours stocker les dates au format UTC dans la base de données et de permettre à l’application cliente de convertir l’UTC en local. fuseau horaire et utilisez les paramètres régionaux de l'utilisateur pour formater la date en conséquence.

20c
la source