Au cours de ma récente expérience dans l'écriture d'un interprète JS, j'ai beaucoup lutté avec le fonctionnement interne des dates ECMA / JS. Donc, je pense que je vais jeter mes 2 cents ici. Espérons que le partage de ces informations aidera les autres à répondre à toutes les questions sur les différences entre les navigateurs dans la façon dont ils traitent les dates.
Le côté entrée
Toutes les implémentations stockent leurs valeurs de date en interne sous forme de nombres 64 bits qui représentent le nombre de millisecondes (ms) depuis le 01/01/1970 UTC (GMT est la même chose que UTC). Cette date est l'époque ECMAScript qui est également utilisée par d'autres langages tels que Java et les systèmes POSIX comme UNIX. Les dates survenant après l'époque sont des nombres positifs et les dates antérieures sont négatives.
Le code suivant est interprété comme la même date dans tous les navigateurs actuels, mais avec le décalage de fuseau horaire local:
Date.parse('1/1/1970'); // 1 January, 1970
Dans mon fuseau horaire (EST, qui est -05: 00), le résultat est 18000000 car c'est le nombre de ms en 5 heures (c'est seulement 4 heures pendant les mois d'heure d'été). La valeur sera différente selon les fuseaux horaires. Ce comportement est spécifié dans ECMA-262 donc tous les navigateurs le font de la même manière.
Bien qu'il existe une certaine variation dans les formats de chaîne d'entrée que les principaux navigateurs analyseront comme des dates, ils les interprètent essentiellement de la même manière en ce qui concerne les fuseaux horaires et l'heure d'été, même si l'analyse est largement dépendante de l'implémentation.
Cependant, le format ISO 8601 est différent. C'est l'un des deux seuls formats décrits dans ECMAScript 2015 (ed 6) spécifiquement qui doivent être analysés de la même manière par toutes les implémentations (l'autre est le format spécifié pour Date.prototype.toString ).
Mais, même pour les chaînes au format ISO 8601, certaines implémentations se trompent. Voici une sortie de comparaison de Chrome et Firefox lorsque cette réponse a été initialement écrite pour 1/1/1970 (l'époque) sur ma machine en utilisant des chaînes de format ISO 8601 qui devraient être analysées exactement à la même valeur dans toutes les implémentations:
Date.parse('1970-01-01T00:00:00Z'); // Chrome: 0 FF: 0
Date.parse('1970-01-01T00:00:00-0500'); // Chrome: 18000000 FF: 18000000
Date.parse('1970-01-01T00:00:00'); // Chrome: 0 FF: 18000000
- Dans le premier cas, le spécificateur "Z" indique que l'entrée est en temps UTC donc n'est pas décalée de l'époque et le résultat est 0
- Dans le deuxième cas, le spécificateur "-0500" indique que l'entrée est en GMT-05: 00 et les deux navigateurs interprètent l'entrée comme étant dans le fuseau horaire -05: 00. Cela signifie que la valeur UTC est décalée de l'époque, ce qui signifie ajouter 18000000 ms à la valeur de l'heure interne de la date.
- Le troisième cas, où il n'y a pas de spécificateur, doit être traité comme local pour le système hôte. FF traite correctement l'entrée comme heure locale tandis que Chrome la traite comme UTC, produisant ainsi des valeurs d'heure différentes. Pour moi, cela crée une différence de 5 heures dans la valeur stockée, ce qui est problématique. D'autres systèmes avec différents décalages obtiendront des résultats différents.
Cette différence a été corrigée à partir de 2020, mais d'autres bizarreries existent entre les navigateurs lors de l'analyse des chaînes de format ISO 8601.
Mais ça empire. Une particularité d'ECMA-262 est que le format de date uniquement ISO 8601 (AAAA-MM-JJ) doit être analysé en UTC, tandis que ISO 8601 exige qu'il soit analysé en tant que local. Voici la sortie de FF avec les formats de date ISO longs et courts sans spécificateur de fuseau horaire.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Ainsi, le premier est analysé comme local car il s'agit de la date et de l'heure ISO 8601 sans fuseau horaire, et le second est analysé en UTC car il s'agit uniquement de la date ISO 8601.
Ainsi, pour répondre directement à la question d'origine, "YYYY-MM-DD"
ECMA-262 doit être interprété comme UTC, tandis que l'autre est interprété comme local. Voilà pourquoi:
Cela ne produit pas de résultats équivalents:
console.log(new Date(Date.parse("Jul 8, 2005")).toString()); // Local
console.log(new Date(Date.parse("2005-07-08")).toString()); // UTC
Cela fait:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
La ligne de fond est la suivante pour analyser les chaînes de date. La SEULE chaîne ISO 8601 que vous pouvez analyser en toute sécurité entre les navigateurs est la forme longue avec un décalage (± HH: mm ou "Z"). Si vous le faites, vous pouvez aller et venir en toute sécurité entre l'heure locale et l'heure UTC.
Cela fonctionne sur tous les navigateurs (après IE9):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
La plupart des navigateurs actuels traitent les autres formats d'entrée de la même manière, y compris le '1/1/1970' (M / D / YYYY) fréquemment utilisé et le '' 1/1/1970 00:00:00 AM '' (M / D / YYYY hh : mm: ss ap) formats. Tous les formats suivants (sauf le dernier) sont traités comme des entrées d'heure locale dans tous les navigateurs. La sortie de ce code est la même dans tous les navigateurs de mon fuseau horaire. Le dernier est traité comme -05: 00 quel que soit le fuseau horaire de l'hôte car le décalage est défini dans l'horodatage:
console.log(Date.parse("1/1/1970"));
console.log(Date.parse("1/1/1970 12:00:00 AM"));
console.log(Date.parse("Thu Jan 01 1970"));
console.log(Date.parse("Thu Jan 01 1970 00:00:00"));
console.log(Date.parse("Thu Jan 01 1970 00:00:00 GMT-0500"));
Cependant, étant donné que l'analyse même des formats spécifiés dans ECMA-262 n'est pas cohérente, il est recommandé de ne jamais compter sur l'analyseur intégré et de toujours analyser manuellement les chaînes, par exemple en utilisant une bibliothèque et en fournissant le format à l'analyseur.
Par exemple, dans moment.js, vous pourriez écrire:
let m = moment('1/1/1970', 'M/D/YYYY');
Côté sortie
Côté sortie, tous les navigateurs traduisent les fuseaux horaires de la même manière mais ils gèrent les formats de chaîne différemment. Voici les toString
fonctions et ce qu'elles produisent. Notez le toUTCString
et la toISOString
sortie des fonctions 5h00 sur ma machine. En outre, le nom du fuseau horaire peut être une abréviation et peut être différent dans différentes implémentations.
Conversion de l'heure UTC en heure locale avant l'impression
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Imprime l'heure UTC stockée directement
- toUTCString
- toISOString
Dans Chrome
toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time)
toDateString Thu Jan 01 1970
toTimeString 00:00:00 GMT-05:00 (Eastern Standard Time)
toLocaleString 1/1/1970 12:00:00 AM
toLocaleDateString 1/1/1970
toLocaleTimeString 00:00:00 AM
toUTCString Thu, 01 Jan 1970 05:00:00 GMT
toISOString 1970-01-01T05:00:00.000Z
Dans Firefox
toString Thu Jan 01 1970 00:00:00 GMT-05:00 (Eastern Standard Time)
toDateString Thu Jan 01 1970
toTimeString 00:00:00 GMT-0500 (Eastern Standard Time)
toLocaleString Thursday, January 01, 1970 12:00:00 AM
toLocaleDateString Thursday, January 01, 1970
toLocaleTimeString 12:00:00 AM
toUTCString Thu, 01 Jan 1970 05:00:00 GMT
toISOString 1970-01-01T05:00:00.000Z
Normalement, je n'utilise pas le format ISO pour l'entrée de chaîne. La seule fois où l'utilisation de ce format est bénéfique pour moi, c'est lorsque les dates doivent être triées sous forme de chaînes. Le format ISO est triable tel quel tandis que les autres ne le sont pas. Si vous devez avoir une compatibilité entre navigateurs, spécifiez le fuseau horaire ou utilisez un format de chaîne compatible.
Le code new Date('12/4/2013').toString()
passe par la pseudo-transformation interne suivante:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
J'espère que cette réponse a été utile.