new Date(Date.parse("Jul 8, 2005"));
Vendredi 08 juil 2005 00:00:00 GMT-0700 (PST)
new Date(Date.parse("2005-07-08"));
Jeu. 07 juillet 2005 17:00:00 GMT-0700 (PST)
Pourquoi la deuxième analyse est-elle incorrecte?
Jusqu'à la parution de la 5ème édition, la méthode Date.parse
était complètement dépendante de implementation (new Date(string)
est équivalent à Date.parse(string)
sauf que cette dernière retourne un nombre plutôt qu'une Date
). Dans la cinquième édition, la spécification a été ajoutée afin de prendre en charge un simplifié (et légèrement incorrect) ISO-8601 (voir aussi Quelles sont les chaînes de date et heure valides dans JavaScript? ). Mais à part cela, il n'y avait que no exigence sur ce que Date.parse
new Date(string)
devrait accepter, à part le fait qu'ils devaient accepter n'importe quelle sortie _/Date # toString (sans préciser de quoi il s'agissait).
Depuis ECMAScript 2017 (édition 8), les implémentations devaient analyser leur sortie pour Date # toString et Date # toUTCString, mais le format de ces chaînes n'était pas spécifié.
A partir d'ECMAScript 2019 (édition 9), le format de Date # toString et Date # toUTCString , a été spécifié comme suit:
fournir 2 formats supplémentaires que Date.parse doit analyser de manière fiable dans les nouvelles implémentations (notant que la prise en charge n'est pas omniprésente et que les implémentations non conformes resteront utilisées pendant un certain temps).
Je recommanderais que les chaînes de date soient analysées manuellement et que le constructeur Date soit utilisé avec les arguments year, month et day pour éviter toute ambiguïté:
// parse a date in yyyy-mm-dd format
function parseDate(input) {
var parts = input.split('-');
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2]); // Note: months are 0-based
}
Au cours de ma récente expérience en tant qu’interprète JS, j’ai beaucoup débattu du fonctionnement interne des dates ECMA/JS. Donc, je pense que je vais mettre mes 2 cents ici. Espérons que partager ces informations aidera les autres à poser des questions sur les différences entre les navigateurs dans la manière dont ils traitent les dates.
Toutes les implémentations stockent leurs valeurs de date en interne sous forme de nombres 64 bits représentant le nombre de millisecondes écoulées depuis le 1/1/1970 UTC (GMT correspond à l'UTC). Les dates postérieures à 1/1/1970 00:00:00
sont des nombres positifs et les dates antérieures sont négatives.
Par conséquent, le code suivant produit exactement le même résultat sur tous les navigateurs.
Date.parse('1/1/1970');
Dans mon fuseau horaire (EST), le résultat est 18000000 car c'est le nombre de ms en 5 heures (seulement 4 heures par heure d'été). La valeur sera différente dans différents fuseaux horaires. Tous les principaux navigateurs le font de la même manière.
Voici le problème cependant. Bien que les principaux navigateurs analysent les dates dans les formats de chaîne d'entrée, les formats de chaîne d'entrée varient quelque peu, mais ils les interprètent essentiellement de la même manière en ce qui concerne les fuseaux horaires et l'heure d'été. Le seul à retenir est le format ISO 8601. C'est le seul format décrit dans la spécification ECMA-262 v.5 spécifiquement. Pour tous les autres formats de chaîne, l'interprétation dépend de la mise en œuvre. Ironiquement, c'est le format où les navigateurs peuvent différer. Voici un résultat de comparaison entre Chrome et Firefox du 1/1/1970 sur ma machine, au format de chaîne ISO 8601.
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
toString
corresponde à ma valeur d'entrée, sauf si je spécifie un fuseau horaire différent, ce que je ne fais jamais. le absence d'un spécificateur doit présumer que l'heure locale est entrée.Mais c’est là que la situation s’aggrave: FF traite la forme abrégée du format ISO 8601 ("AAAA-MM-JJ") différemment de la forme longue ("AAAA-MM-JJTHH: mm: ss: sssZ") pour aucune raison logique que ce soit. Voici la sortie de FF avec les formats de date ISO long et court sans spécificateur de fuseau horaire.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Donc, pour répondre directement à la question du demandeur initial, "YYYY-MM-DD"
est la forme abrégée du format ISO 8601 "YYYY-MM-DDTHH:mm:ss:sssZ"
. Ainsi, il est interprété comme heure UTC, tandis que l'autre est interprété comme local. C'est pourquoi,
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08")).toString());
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
La ligne du bas est la suivante pour l'analyse des chaînes de date. La SEULE chaîne ISO 8601 que vous pouvez analyser en toute sécurité sur tous les navigateurs est la forme longue. Et, utilisez TOUJOURS le spécificateur "Z". Si vous le faites, vous pouvez sans risque vous déplacer entre l’heure locale et l’heure UTC.
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
Heureusement, la plupart des navigateurs actuels traitent les autres formats d'entrée de la même manière, y compris les formats les plus fréquemment utilisés, à savoir "1/1/1970" et "1/1/1970 00:00:00 AM". Tous les formats suivants (et autres) sont traités comme une entrée d’heure locale dans tous les navigateurs et convertis au format UTC avant stockage. Ainsi, les rendant compatibles entre navigateurs. La sortie de ce code est la même dans tous les navigateurs de mon fuseau horaire.
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"));
Du côté de la sortie, tous les navigateurs convertissent les fuseaux horaires de la même manière, mais ils traitent les formats de chaîne différemment. Voici les fonctions toString
et ce qu’elles produisent. Notez que les fonctions toUTCString
et toISOString
sont émises à 5h00 du matin sur ma machine.
Convertit l'heure UTC en heure locale avant l'impression
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Imprime directement l'heure UTC enregistrée
- toUTCString
- toISOString
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
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
Je n'utilise normalement pas le format ISO pour la saisie de chaînes. L’utilisation de ce format ne m’apporte des avantages que lorsque les dates doivent être triées sous forme de chaînes. Le format ISO est triable tel quel, alors 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.
Il y a une méthode à la folie. En règle générale, si un navigateur peut interpréter une date comme une ISO-8601, il le fera. "2005-07-08" tombe dans ce camp, et donc il est analysé comme UTC. "8 juillet 2005" ne peut pas, et donc il est analysé dans l'heure locale.
Voir JavaScript et dates, quel gâchis! pour plus.
Une autre solution consiste à créer un tableau associatif avec le format de date, puis à reformater les données.
Cette méthode est utile pour la date formatée de manière inhabituelle.
Un exemple:
mydate='01.02.12 10:20:43':
myformat='dd/mm/yy HH:MM:ss';
dtsplit=mydate.split(/[\/ .:]/);
dfsplit=myformat.split(/[\/ .:]/);
// creates assoc array for date
df = new Array();
for(dc=0;dc<6;dc++) {
df[dfsplit[dc]]=dtsplit[dc];
}
// uses assc array for standard mysql format
dstring[r] = '20'+df['yy']+'-'+df['mm']+'-'+df['dd'];
dstring[r] += ' '+df['HH']+':'+df['MM']+':'+df['ss'];
Selon http://blog.dygraphs.com/2012/03/javascript-and-dates-what-mess.html , le format "aaaa/mm/jj" résout les problèmes habituels. Il dit: "Si vous le pouvez, tenez-vous-en à" AAAA/MM/JJ "pour vos chaînes de date. Il est universellement pris en charge et sans ambiguïté. Avec ce format, toutes les heures sont en local." J'ai défini des tests: http://jsfiddle.net/jlanus/ND2Qg/432/ Ce format: + évite l'ambiguïté des ordres du jour et du mois en utilisant y m d ordre et une année à 4 chiffres + évite que le problème UTC par rapport au problème local ne soit pas conforme au format ISO en utilisant des barres obliques + danvk, le mec dygraphs , dit que ce format convient à tous les navigateurs.
Utilisez moment.js pour analyser les dates:
var caseOne = moment("Jul 8, 2005", "MMM D, YYYY", true).toDate();
var caseTwo = moment("2005-07-08", "YYYY-MM-DD", true).toDate();
Le troisième argument détermine l'analyse stricte (disponible à partir de la version 2.3.0). Sans cela, moment.js peut également donner des résultats incorrects.
Alors que CMS est correct que le passage de chaînes dans la méthode d'analyse est généralement dangereux, la nouvelle spécification ECMA-262 5e édition (également appelée ES5) de la section 15.9.4.2 suggère que Date.parse()
doit en réalité gérer les dates au format ISO . L'ancienne spécification ne contenait aucune revendication de ce type. Bien sûr, les anciens navigateurs et certains navigateurs actuels ne fournissent toujours pas cette fonctionnalité ES5.
Votre deuxième exemple n'est pas faux. Il s'agit de la date spécifiée en UTC, comme l'indique Date.prototype.toISOString()
, mais elle est représentée dans votre fuseau horaire local.
Cette bibliothèque d'analyse de date poids léger devrait résoudre tous les problèmes similaires. J'aime la bibliothèque car elle est assez facile à agrandir. Il est également possible de l’i18n (pas très simple, mais pas si difficile).
Exemple d'analyse:
var caseOne = Date.parseDate("Jul 8, 2005", "M d, Y");
var caseTwo = Date.parseDate("2005-07-08", "Y-m-d");
Et le formatage à la chaîne (vous remarquerez que les deux cas donnent exactement le même résultat):
console.log( caseOne.dateFormat("M d, Y") );
console.log( caseTwo.dateFormat("M d, Y") );
console.log( caseOne.dateFormat("Y-m-d") );
console.log( caseTwo.dateFormat("Y-m-d") );
Les deux sont corrects, mais ils sont interprétés comme des dates avec deux fuseaux horaires différents. Alors vous avez comparé des pommes et des oranges:
// local dates
new Date("Jul 8, 2005").toISOString() // "2005-07-08T07:00:00.000Z"
new Date("2005-07-08T00:00-07:00").toISOString() // "2005-07-08T07:00:00.000Z"
// UTC dates
new Date("Jul 8, 2005 UTC").toISOString() // "2005-07-08T00:00:00.000Z"
new Date("2005-07-08").toISOString() // "2005-07-08T00:00:00.000Z"
J'ai supprimé l'appel Date.parse()
puisqu'il est utilisé automatiquement sur un argument de chaîne. J'ai également comparé les dates en utilisant format ISO8601 afin que vous puissiez comparer visuellement les dates entre vos dates locales et les dates UTC. Les heures sont séparées de 7 heures, ce qui correspond à la différence de fuseau horaire et explique pourquoi vos tests ont montré deux dates différentes.
L’autre moyen de créer ces mêmes dates locales/UTC serait:
new Date(2005, 7-1, 8) // "2005-07-08T07:00:00.000Z"
new Date(Date.UTC(2005, 7-1, 8)) // "2005-07-08T00:00:00.000Z"
Mais je recommande toujours fortement Moment.js qui est comme simple mais puissant :
// parse string
moment("2005-07-08").format() // "2005-07-08T00:00:00+02:00"
moment.utc("2005-07-08").format() // "2005-07-08T00:00:00Z"
// year, month, day, etc.
moment([2005, 7-1, 8]).format() // "2005-07-08T00:00:00+02:00"
moment.utc([2005, 7-1, 8]).format() // "2005-07-08T00:00:00Z"
Voici un extrait court et flexible permettant de convertir une chaîne datetime de manière sécurisée pour tous les navigateurs, comme indiqué par @ drankin2112.
var inputTimestamp = "2014-04-29 13:00:15"; //example
var partsTimestamp = inputTimestamp.split(/[ \/:-]/g);
if(partsTimestamp.length < 6) {
partsTimestamp = partsTimestamp.concat(['00', '00', '00'].slice(0, 6 - partsTimestamp.length));
}
//if your string-format is something like '7/02/2014'...
//use: var tstring = partsTimestamp.slice(0, 3).reverse().join('-');
var tstring = partsTimestamp.slice(0, 3).join('-');
tstring += 'T' + partsTimestamp.slice(3).join(':') + 'Z'; //configure as needed
var timestamp = Date.parse(tstring);
Votre navigateur doit fournir le même résultat d’horodatage que Date.parse
avec:
(new Date(tstring)).getTime()
La réponse acceptée de CMS est correcte, je viens d'ajouter quelques fonctionnalités:
// parse a date time that can contains spaces, dashes, slashes, colons
function parseDate(input) {
// trimes and remove multiple spaces and split by expected characters
var parts = input.trim().replace(/ +(?= )/g,'').split(/[\s-\/:]/)
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2] || 1, parts[3] || 0, parts[4] || 0, parts[5] || 0); // Note: months are 0-based
}