
Récemment, je travaillais sur la tâche d'ajouter des fuseaux horaires à la bibliothèque de calendriers JS maintenue par mon équipe. J'étais bien conscient de la prise en charge inutile des fuseaux horaires dans JavaScript, mais j'espérais que l'abstrait des objets de données existants permettrait de résoudre facilement la plupart des difficultés.
Cependant, mes rêves se sont transformés en poussière. Quand je me suis plongé dans la tâche, j'ai réalisé qu'il est vraiment difficile de travailler avec des fuseaux horaires dans cette langue. Il était extrêmement difficile de mettre en œuvre quelque chose de plus compliqué que de simplement formater l'affichage de l'heure et de calculer la date à l'aide d'opérations complexes (fonctions de calendrier). J'ai acquis une expérience précieuse dans la résolution de ce problème, ce qui a entraîné de nouvelles difficultés.
Dans cet article, je veux discuter de ce que j'ai rencontré et comment cela a été résolu. Pendant que j'écrivais le texte, j'ai réalisé que la raison de toutes les difficultés était ma mauvaise compréhension du sujet des fuseaux horaires. À la lumière de cette prise de conscience, je propose d'abord de parler en détail de la définition et des normes, puis de passer ensuite à JavaScript.
Qu'est-ce qu'un fuseau horaire?
Un fuseau horaire est une région géographique qui utilise la même heure locale définie par le gouvernement. De nombreux pays sont entièrement liés à des fuseaux horaires spécifiques, et sur les territoires de grands États, comme la Russie et les États-Unis, plusieurs fuseaux horaires sont utilisés. Il est curieux que, bien que la Chine soit également suffisamment grande, elle n'accepte qu'un seul fuseau horaire. Parfois, cela conduit à des situations étranges lorsque le lever du soleil dans la partie ouest du pays commence vers 10 heures du matin.
GMT, UTC et Offset
GMT
L'heure locale sud-coréenne est désignée comme
GMT+09:00
. GMT signifie Greenwich Mean Time (GMT), c'est-à-dire cette fois sur l'horloge de l'Observatoire royal de Greenwich, au Royaume-Uni. Il est situé sur le méridien principal. Le signal radio du système GMT a commencé à émettre le 5 février 1924 et il est devenu lui-même une norme mondiale le 1er janvier 1972.
UTC
Beaucoup de gens pensent que GMT et UTC sont la même chose, les utilisant souvent comme systèmes interchangeables. Mais c'est une erreur. Le système UTC est apparu en 1972 comme un moyen de compenser l'effet de la rotation de la Terre. Le système est basé sur le temps atomique international, calculé par la fréquence des vibrations électromagnétiques des atomes de césium. En d'autres termes, l'UTC est un remplacement plus précis du GMT. Bien que la différence en temps réel entre les deux systèmes soit très faible, il est préférable pour les développeurs de logiciels de s'appuyer sur l'UTC.
Un fait intéressant: lorsque l'UTC était encore en cours de développement, il a été suggéré dans les pays anglophones de l'appeler CUT (temps universel coordonné), et dans les pays francophones - TUC (Temps Universal Coordonn). Cependant, aucun des camps n'a pu gagner, et ils ont accepté d'appeler le système UTC, épelant les deux options proposées (C, T et U).
Décalage
+09:00
en
UTC+09:00
signifie que l'heure locale a 9 heures d'avance sur l'heure UTC standard. Autrement dit, quand il est 21 heures en Corée du Sud, il est midi dans la région UTC. La différence entre l'heure UTC standard et l'heure locale est appelée «décalage», qui s'exprime en valeurs positives ou négatives:
+09:00
,
-03:00
, etc.
Dans de nombreux pays, il est habituel de donner à vos fuseaux horaires des noms uniques. Par exemple, le fuseau horaire de la Corée du Sud est appelé KST (Korea Standard Time), son décalage est exprimé comme
KST = UTC+09:00
. Cependant, le décalage
+09:00
utilisé non seulement par la Corée du Sud, mais aussi par le Japon, l'Indonésie et de nombreux autres pays, par conséquent, la relation entre les décalages et les courroies est exprimée non pas comme 1: 1, mais comme 1: N. Une liste des pays avec un décalage de
+09:00
est présentée
ici .
Certains décalages fonctionnent non seulement pendant des heures. Par exemple, en Corée du Nord, l'heure standard est
+08:30
, tandis qu'en Australie dans certaines régions,
+8:45
,
+09:30
et
+10:30
.
Une liste complète des décalages UTC est
ici .
Fuseau horaire! == décalage?
Comme je l'ai dit, nous utilisons des noms de fuseaux horaires (KST, JST) avec des décalages comme interchangeables, sans les distinguer. Mais il sera erroné de considérer à la fois le temps et le déplacement d'une région particulière. Il y a plusieurs raisons:
Heure d'été (DST)
Dans certains pays, ce terme est inconnu, mais dans de nombreux États, l'heure d'été est pratiquée, principalement en Europe. Pour cela, le terme international DST est adopté - Daylight Saving Time. Cela signifie déplacer l'horloge en été pendant une heure avant l'heure standard relative.
Par exemple, la Californie utilise PST (Pacific Standard Time) en hiver et PDT (Pacific Daylight Time,
UTC-07:00
) en hiver. Aux États-Unis et au Canada, le terme heure du Pacifique (PT, heure du Pacifique) est utilisé pour les régions qui utilisent deux fuseaux horaires.
Quand commence et se termine l'heure d'été? Tout dépend du pays. Par exemple, aux États-Unis et au Canada jusqu'en 2006, l'heure d'été était utilisée de 2 heures du matin le premier dimanche d'avril à 12 heures le dernier dimanche d'octobre. Et à partir de 2007, l'heure d'été a commencé à compter de 2 nuits le deuxième dimanche de mars à 2 nuits le premier dimanche de novembre. En Europe, différents pays pratiquent l'utilisation progressive de l'heure d'été en fonction de chaque fuseau horaire.
Les fuseaux horaires changent-ils?
Chaque pays détermine lui-même les fuseaux horaires à utiliser, de sorte que l'heure locale peut changer pour des raisons politiques et / ou économiques. Aux États-Unis, par exemple, les frontières de l'heure d'été ont été modifiées en 2007 parce que George W. Bush a lancé la politique énergétique en 2005. L'Égypte et la Russie passaient à l'heure d'été, mais l'ont abandonnée depuis 2011.
Dans certains cas, le gouvernement peut modifier non seulement l'heure d'été, mais également l'heure standard. Par exemple, les Samoa antérieures utilisaient la compensation
UTC-10:00
, mais elles sont ensuite passées à
UTC+14:00
pour réduire les pertes commerciales en raison du décalage horaire avec l'Australie et la Nouvelle-Zélande. Cette décision a entraîné la mort du pays toute une journée - le 30 décembre 2011, comme l'ont rapporté les
journaux du monde entier .
Aux Pays-Bas, un décalage de
+0:19:32.13
utilisé depuis 1909, depuis 1937 le pays est passé à
+00:20
et de 1940 à
+01:00
, depuis lors, l'heure principale n'a pas changé.
Fuseau horaire 1: décalage N
Ainsi, le fuseau horaire peut avoir un ou plusieurs décalages. Le délai accepté comme standard dépend des raisons politiques et / ou économiques actuelles dans un pays particulier.
Dans la vie de tous les jours, cela ne pose pas de problème tant que vous n'essayez pas de systématiser ces données sur la base de certaines règles. Imaginez que vous souhaitez définir l'heure standard sur votre smartphone en utilisant une sorte de biais. Si vous vivez dans une région où l'heure d'été est pratiquée, votre smartphone devrait savoir exactement quand aller et venir. Autrement dit, vous devez établir la relation entre l'heure standard et l'heure d'été dans un fuseau horaire (par exemple, heure du Pacifique).
Mais cela ne peut pas être fait avec quelques règles simples. Par exemple, aux États-Unis en 2007, le début et la fin de l'heure d'été ont été modifiés, le 31 mai 2006, il était censé utiliser l'heure d'été (
-07:00
) comme heure standard, et le 31 mars 2007 - PST (
-08:00
). Il s'avère que pour faire référence à un fuseau horaire spécifique, vous devez connaître l'historique complet du changement de fuseau horaire ou la date de changement des règles d'heure d'été.
Vous pouvez dire: "Le fuseau horaire de New York est PST (
-08:00
)." Cependant, il doit être clarifié: "Le fuseau horaire actuel à New York est PST." De plus, pour une implémentation précise du système, vous devez utiliser une expression encore plus précise. Oubliez le terme «fuseau horaire». Vous devez dire: "Maintenant à New York, l'heure d'été est utilisée comme heure standard."
Alors, que devons-nous utiliser au lieu du décalage pour déterminer le fuseau horaire d'une région particulière? Le nom de cette région. Plus précisément, vous devez regrouper dans un seul fuseau horaire les régions dans lesquelles elles passent également à l'heure d'été et à l'heure standard. Vous pouvez utiliser des noms comme PT (Pacific Time), mais ils combinent uniquement l'heure standard actuelle et l'heure d'été, et ne prennent pas nécessairement en compte tous les changements historiques. De plus, comme PT n'est en circulation qu'aux États-Unis et au Canada, vous devez vous fier aux normes établies d'organisations réputées pour garantir l'universalité de votre logiciel.
Base de données des fuseaux horaires IANA
Je dois admettre que les informations sur les fuseaux horaires sont plutôt une base de données, pas un ensemble de règles, car ces informations doivent contenir toutes les modifications historiques pertinentes. Il existe plusieurs bases de données standard conçues pour résoudre les tâches liées aux fuseaux horaires. Le plus souvent, la
base de données de fuseaux horaires IANA est utilisée , et elle est généralement appelée base de données tz (ou tzdata). La base de données contient des données historiques sur les changements d'heure standard et d'heure d'été dans le monde. De plus, il est organisé de sorte qu'il est possible de vérifier toutes les données historiques et de s'assurer que l'heure est exacte à partir de l'heure Unix (
1970.01/01 00:00:00
). Bien que vous puissiez trouver des informations dans la base de données avant 1970, leur exactitude n'est pas garantie.
La convention de dénomination utilise la règle région / lieu. Le nom du continent ou de l'océan (Asie, Amérique, océan Pacifique) est généralement utilisé comme région, et les noms des principales villes (Séoul, New York) comme lieu. La raison en est que les villes durent généralement plus longtemps que les pays. Par exemple, le fuseau horaire de la Corée du Sud est
Asia/Seoul
et
Asia/Tokyo
. Bien que les deux pays utilisent le même décalage
UTC+09:00
, leur heure locale a changé différemment, ils ont donc été divisés en différents fuseaux horaires.
La base IANA est gérée par de nombreuses communautés de développeurs et d'historiens. Des données historiques fraîchement trouvées y sont immédiatement saisies et les politiques actuelles sont mises à jour, de sorte que la base de données peut aujourd'hui être considérée comme la source la plus fiable. De plus, il est utilisé sous le capot par de nombreux systèmes Unix, y compris Linux et MacOS, ainsi que par un certain nombre de langages de programmation populaires, dont Java et PHP.
Veuillez noter que Windows utilise
la base de données Microsoft . Cependant, il est inexact en termes de données historiques et n'est pris en charge que par Microsoft lui-même. Par conséquent, la base est moins fiable que la base IANA.
JavaScript et IANA
La fonctionnalité liée au fuseau horaire est implémentée en JavaScript à l'improviste. Par défaut, la langue utilise la ceinture de région actuelle (plus précisément, la ceinture sélectionnée lors de l'installation du système d'exploitation), et il n'y a aucun moyen de la changer. De plus, même les spécifications de la norme de base de données en JavaScript sont vagues, et vous le comprendrez vous-même si vous décidez de traiter la spécification pour ES2015. À propos du fuseau horaire local et de la disponibilité de l'heure d'été, il n'y a que quelques déclarations vagues. Par exemple, l'heure d'été est définie comme suit:
ECMAScript 2015 - Réglage de l'heure d'été .
Il s'agit d'un algorithme dépendant de l'implémentation qui utilise les meilleures informations de fuseau horaire disponibles pour déterminer le paramètre d'heure d'été locale DaylightSavingTA (t), qui est calculé en millisecondes. Une implémentation ECMAScript devrait vous aider à mieux déterminer votre paramètre d'heure d'été local.
On dirait qu'ils disent juste: "Mec, essayez de faire en sorte que cela fonctionne." Entre autres, vous devez résoudre le problème de compatibilité avec différents navigateurs. Vous dites: "Quel gâchis!" Et puis lisez la ligne suivante:
Remarque: nous vous recommandons d'utiliser les informations de la base de données de fuseaux horaires IANA http://www.iana.org/time-zones/ dans vos implémentations.
Oui Les spécifications de l'ECMA vous donnent le ballon avec une recommandation sans prétention d'utiliser la base de données IANA, car JavaScript n'a pas de base de données standard spéciale. Par conséquent, différents navigateurs utilisent leurs propres opérations avec des fuseaux horaires, qui sont souvent incompatibles entre eux, pour calculer l'heure. Plus tard, dans ECMA, pour l'API internationale, l'option d'utiliser les données IANA au format ECMA-402
Intl.DateTimeFormat
a été
Intl.DateTimeFormat
. Mais cette option est beaucoup moins fiable que les analogues d'autres langages de programmation.
Fuseau horaire dans un environnement serveur-client
Prenons un scénario simple: nous devons déterminer le fuseau horaire. Supposons que nous créons un calendrier qui traitera les informations de temps. Lorsqu'un utilisateur dans l'environnement client entre une date et une heure dans la fenêtre d'enregistrement, la date est transférée au serveur et stockée dans la base de données. Le client reçoit alors du serveur la date enregistrée dans le planning pour affichage sur l'écran.
Ici, vous devez décider quelque chose. Que faire si certains des clients accédant au serveur sont dans des fuseaux horaires différents? L'événement dans le calendrier, qui a été enregistré à Séoul le 10 mars 2017 à 23h30, à New York devrait être affiché comme le 10 mars 2017 à 9h30. Pour que le serveur serve des clients de différents fuseaux horaires, la planification qui y est stockée doit contenir des valeurs absolues qui ne dépendent pas de la zone. Chaque serveur a sa propre façon de stocker ces valeurs, cette question dépasse le cadre de l'article, car tout dépend d'un serveur ou d'une base de données spécifique. En général, la date et l'heure transmises du client au serveur doivent être présentées soit sous forme de valeurs basées sur un seul décalage (généralement UTC), soit sous forme de valeurs contenant des informations sur le fuseau horaire de l'environnement client.
En règle générale, ces données sont transmises au format
Unix au format UTC ou selon la
norme ISO-8601 avec des informations de décalage. Si dans notre exemple nous convertissons Séoul 21.30 le 10 mars 2017 en heure Unix, alors nous obtenons la valeur entière
1489113000
. Et au format ISO-8601, la valeur de chaîne est
2017–03–10T11:30:00+09:00
.
Si vous utilisez JavaScript dans un environnement de navigateur, vous devez convertir la valeur entrée comme décrit ci-dessus, puis la reconvertir pour qu'elle corresponde au fuseau horaire de l'utilisateur. Il est nécessaire de résoudre ces deux problèmes. Du point de vue du langage de programmation, la première opération est appelée "analyse", et la seconde est "mise en forme". Voyons maintenant comment cela se fait en JavaScript.
Même lorsque vous travaillez avec JS dans un environnement de serveur utilisant Node.js, vous devrez peut-être analyser les données reçues du client. Mais comme les fuseaux horaires des serveurs et des bases de données sont généralement synchronisés et que le formatage est attribué aux clients, dans un environnement de navigateur, vous devez décider de plusieurs facteurs. Plus loin, je vais expliquer en relation avec l'environnement du navigateur.
Objet de date JavaScript
Les tâches impliquant un travail avec une heure ou une heure donnée sont résolues à l'aide de l'objet
Date
. Il s'agit d'un objet natif défini dans ECMAScript, comme
Array
ou
Function
. Autrement dit, il est, pour la plupart, implémenté en utilisant du code natif comme C ++. L'API est bien décrite dans la
documentation MDN . La classe
java.util.Date de Java a eu un grand impact sur l'objet, elle a donc hérité de certaines propriétés indésirables, telles que les caractéristiques des données mutables et un mois à partir de zéro.
Sous le capot, un objet
Date
JavaScript fonctionne avec le temps en utilisant des valeurs absolues au format Unix-time. Mais les constructeurs et les méthodes comme les fonctions
parse()
,
getHour()
,
setHour()
et d'autres sont affectés par le fuseau horaire du client (plus précisément, la ceinture spécifiée dans le système d'exploitation dans lequel le navigateur s'exécute). Donc, si vous créez un objet
Date
directement à l'aide de données saisies par l'utilisateur, le fuseau horaire local du client sera reflété dans ces données.
Comme je l'ai mentionné, JavaScript ne fournit aucun moyen de modifier arbitrairement le fuseau horaire. Par conséquent, nous supposons que nous pouvons utiliser directement la valeur de fuseau horaire spécifiée dans le navigateur.
Création d'un objet de date à l'aide d'une entrée utilisateur
Revenons au premier exemple. Supposons que l'utilisateur ait entré l'heure de Séoul sur son appareil à 11h30 le 11 mars 2017. Ces données sont enregistrées sous cinq chiffres: 2017, 2, 11, 11 et 30 - année, mois, jour, heures et minutes, respectivement (puisque le mois commence à 0, il la valeur doit être 3–1 = 2). En utilisant le constructeur, vous pouvez facilement créer un objet
Date
:
const d1 = new Date(2017, 2, 11, 11, 30); d1.toString();
Si vous regardez la valeur retournée par
d1.toString()
, vous verrez que la valeur absolue de l'objet créé est 11h00 le 11 mars 2017, elle a été calculée en utilisant le mélange
+09:00
(KST).
Vous pouvez utiliser des données de chaîne dans le constructeur. Si vous les appliquez à
Date
, l'objet appelle en interne
Date.parse()
et
Date.parse()
valeur correcte. Cette fonction prend en charge les
spécifications RFC2888 et
ISO-8601 . Mais la
documentation MDN pour Date.parse () indique que la valeur renvoyée par cette méthode dépend du navigateur et que le format du type de chaîne peut affecter la valeur finale exacte. Par conséquent, il est préférable de ne pas utiliser cette méthode. Par exemple, dans Safari et Internet Explorer, une valeur de chaîne comme
2015–10–12 12:00:00
renvoie
NaN
, tandis que dans Chrome et Firefox, elle renvoie le fuseau horaire local. Dans certaines situations, une valeur basée sur UTC est renvoyée.
Création d'un objet de date à l'aide des données du serveur
Supposons que vous souhaitiez recevoir des données d'un serveur. S'ils sont sous la forme d'une heure numérique Unix, vous pouvez simplement utiliser le constructeur pour créer un objet
Date
. Je n'ai pas encore mentionné que lorsque le constructeur
Date
reçoit une seule valeur en tant que paramètre unique, il calcule la valeur de temps Unix en millisecondes (remarque: JS traite le temps Unix en millisecondes. Cela signifie que la deuxième valeur doit être multipliée par 1000). Lors de l'exécution du code suivant, nous obtenons la même valeur que dans l'exemple précédent:
const d1 = new Date(1489199400000); d1.toString();
Et si, au lieu de l'heure Unix, utilisez le type de chaîne ISO-8601? Comme je l'ai expliqué ci-dessus, la méthode
Date.parse()
devient alors peu fiable et il vaut mieux ne pas l'utiliser. Cependant, à partir d'ECMAScript 5, vous pouvez utiliser des constructions de chaînes au format ISO-8601 dans le constructeur
Date
dans Internet Explorer 9.0 et supérieur.
Si vous n'utilisez pas le dernier navigateur, assurez-vous que le
Z
à la fin des valeurs. Sans cela, votre ancien navigateur peut interpréter la valeur en fonction de l'heure locale et non de l'UTC. Voici un exemple d'utilisation dans Internet Explorer 10:
const d1 = new Date('2017-03-11T11:30:00'); const d2 = new Date('2017-03-11T11:30:00Z'); d1.toString();
Selon la spécification, dans les deux cas, la même valeur doit être obtenue. Mais ils sont différents. Dans un navigateur ultérieur, les valeurs seront les mêmes. Pour éviter ce problème, ajoutez toujours
Z
à la fin de la ligne si les informations de fuseau horaire ne sont pas disponibles.
Création d'une date à transmettre au serveur
Vous pouvez maintenant utiliser la date précédemment créée, récupérer ou ajouter librement l'heure en fonction des fuseaux horaires locaux. Seulement à la fin du traitement, n'oubliez pas de convertir les données au format précédent avant de les renvoyer au serveur.
S'il s'agit de l'heure Unix, vous pouvez utiliser la méthode
getTime()
(n'oubliez pas les millisecondes).
const d1 = new Date(2017, 2, 11, 11, 30); d1.getTime();
Qu'en est-il d'une valeur de chaîne au format ISO-8601? Comme je l'ai dit, Internet Explorer 9.0 et supérieur prend en charge ECMAScript 5 et les versions ultérieures prennent en charge ISO-8601. Par conséquent, en utilisant les
toISOString()
ou
toJSON()
, vous pouvez créer une chaîne en ISO-8601 (
toJSON()
peut être utilisé pour les appels récursifs avec
JSON.stringify()
et autres). Les deux méthodes donnent le même résultat, sauf lors du traitement de données non valides:
const d1 = new Date(2017, 2, 11, 11, 30); d1.toISOString();
Vous pouvez utiliser les
toGMTString()
ou
toUTCString()
pour créer une chaîne UTC. Cela se traduira par une valeur conforme à la
RFC-1123 .
L'objet
Date
inclut
toString()
,
toLocaleString()
et leurs méthodes d'extension. Mais ils sont peu utiles, car ils sont principalement utilisés pour renvoyer des chaînes en fonction du fuseau horaire local, et les valeurs renvoyées dépendent du navigateur et du système d'exploitation.
Changer votre fuseau horaire local
Comme vous pouvez le voir, il existe une sorte de prise en charge des fuseaux horaires dans JS. ? ? , JS . , . . , .
. . 11.30 11 2017 - . Unix- , -
-05:00
. , .
getTimeZoneOffset()
. API JavaScript, . :
const seoul = new Date(1489199400000); seoul.getTimeZoneOffset();
-540
, 540 . , (
+09:00
). , , . -,
60 * 5 = 300
.
840
Date
.
getXX
. :
function formatDate(date) { return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate() + ' ' + date.getHours() + ':' + date.getMinutes(); } const seoul = new Date(1489199400000); const ny = new Date(1489199400000 - (840 * 60 * 1000)); formatDate(seoul);
formatDate()
-. , . , ? , . , — , . ( ).
, . , -, 11 15 .
setDate()
Date
, , .
ny.setDate(15); formatDate(ny);
, . , ? ,
getTime()
getISOString()
. , .
const time = ny.getTime() + (840 * 60 * 1000);
, , .
Date
? Non.
Date
11- 15-, 4 (
24 * 4 * 60 * 60 * 1000
). - 10- 15-, 5 (
24* 5 * 60 * 60 * 1000
). .
. , . - 12 , 15 2017
-04:00
,
-05:00
. , 780 , 60 , .
const time = ny.getTime() + (780 * 60 * 1000);
, - , .
, . , , , . , ,
IANA .
,
Date
, . , . , . . JS . .
Moment Timezone
Moment — JavaScript-, . API , .
Moment Timezone , . IANA API, .
. , .
.
, Moment Timezone:
const seoul = moment(1489199400000).tz('Asia/Seoul'); const ny = moment(1489199400000).tz('America/New_York'); seoul.format();
seoul
,
ny
-05:00
-04:00
.
format()
, ISO-8601, . .
Conclusion
API , JavaScript, . , API, Internet Explorer 9 . , . ,
getTimezoneOffset()
. , . Moment Timezone.
, , . : . , , , . , JavaScript . , .