yyyy - objet date javascript




Pourquoi Date.parse donne des résultats incorrects? (7)

Cas un:

new Date(Date.parse("Jul 8, 2005"));

Sortie:

Ven Jul 08 2005 00:00:00 GMT-0700 (PST)

Deuxième cas:

new Date(Date.parse("2005-07-08"));

Sortie:

Jeu. 07 juillet 2005 17:00:00 GMT-0700 (PST)

Pourquoi la deuxième analyse est-elle incorrecte?


Au cours de l'expérience récente d'un interprète JS j'ai lutté beaucoup avec le fonctionnement interne des dates ECMA / JS. Donc, je pense que je vais jeter dans mes 2 cents ici. Espérons que le partage de ces choses aidera les autres avec des questions sur les différences entre les navigateurs dans la façon dont ils traitent les dates.

Le côté d'entrée

Toutes les implémentations stockent leurs valeurs de date en interne en tant que nombres de 64 bits représentant le nombre de millisecondes depuis le 1/1/1970 UTC (GMT est la même chose que UTC). Les dates survenues après le 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 de 18000000 parce que c'est le nombre de ms en 5 heures (c'est seulement 4 heures pendant l'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 frotter cependant. Bien qu'il y ait une certaine variance dans les formats de chaînes d'entrée que les principaux navigateurs interprètent comme des dates, ils les interprètent essentiellement de la même manière en ce qui concerne les fuseaux horaires et les heures d'été. Celui qui tient le coup 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 l'implémentation. Ironiquement, c'est le format où les navigateurs peuvent différer. Voici une sortie de comparaison de Chrome vs Firefox pour 1/1/1970 sur ma machine en utilisant le 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
  • Le spécificateur "Z" indique que l'entrée est déjà en heure UTC et ne nécessite aucun décalage avant le stockage.
  • Le spécificateur "-0500" indique que l'entrée est dans GMT-05: 00 donc les deux navigateurs interprètent l'entrée comme étant dans mon fuseau horaire local. Cela signifie que la valeur est convertie en UTC avant d'être stockée. Dans mon cas, cela signifie ajouter 18000000ms à la valeur interne de la date, ce qui nécessite un décalage de -18000000ms (-05: 00) pour me remettre à l'heure locale.
  • Lorsqu'il n'y a pas de spécificateur, FF traite l'entrée comme heure locale, tandis que Chrome la traite comme heure UTC. Pour moi cela crée une différence de 5 heures dans la valeur stockée, ce qui est problématique. Dans ma mise en œuvre, j'ai fini par prendre le parti de FF ici parce que j'aime la sortie de toString pour correspondre à ma valeur d'entrée sauf si je spécifie un autre fuseau horaire, ce que je ne fais jamais. L' absence d'un spécificateur devrait supposer une entrée de l'heure locale.

Mais là où il y a pire, 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. 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

Donc, pour répondre directement à la question du demandeur, "YYYY-MM-DD" est la forme abrégée du format ISO 8601 "YYYY-MM-DDTHH:mm:ss:sssZ" . Donc, il est interprété comme temps UTC tandis que l'autre est interprété comme local. C'est pourquoi,

Cela ne jive pas:

console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08")).toString());

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 du bas est-ce pour l'analyse des chaînes de date. La seule chaîne ISO 8601 que vous pouvez analyser en toute sécurité parmi les navigateurs est la forme longue. Et, utilisez TOUJOURS le spécificateur "Z". Si vous faites cela, vous pouvez sans risque aller et venir entre l'heure locale et l'heure UTC.

Cela fonctionne à travers les navigateurs (après IE9):

console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());

Heureusement, la plupart des navigateurs actuels traitent également les autres formats d'entrée, y compris les formats '1/1/1970' et '1/1/1970 00:00:00 AM' les plus fréquemment utilisés. Tous les formats suivants (et autres) sont traités en tant que saisie de l'heure locale dans tous les navigateurs et convertis en UTC avant le stockage. Ainsi, les rendant compatibles avec tous les 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"));

Le côté de la sortie

Du côté de la sortie, tous les navigateurs traduisent les fuseaux horaires de la même manière, mais ils traitent différemment les formats de chaîne. Voici les fonctions toString et ce qu'elles produisent. Notez les fonctions toUTCString et toISOString 5:00 AM sur ma machine.

Convertit l'heure UTC en heure locale avant l'impression

 - toString
 - toDateString
 - toTimeString
 - toLocaleString
 - toLocaleDateString
 - toLocaleTimeString

Imprime l'heure UTC stockée directement

 - toUTCString
 - toISOString 
In 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
In 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

Je n'utilise normalement pas le format ISO pour la saisie de chaîne. La seule fois que l'utilisation de ce format est bénéfique pour moi, c'est quand les dates doivent être triées en tant que 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.


Bien que share que le passage des chaînes dans la méthode d'analyse est généralement dangereux, la nouvelle spécification ECMA-262 5th Edition (alias ES5) de la section 15.9.4.2 suggère que Date.parse() devrait gérer les dates au format ISO. L'ancienne spécification ne faisait aucune revendication de ce genre. 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. C'est la date spécifiée dans UTC, comme implicite par Date.prototype.toISOString() , mais est représentée dans votre fuseau horaire local.


Il y a une méthode à la folie. En règle générale, si un navigateur peut interpréter une date comme 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 il est donc analysé à l'heure locale.

Voir JavaScript et les dates, quel gâchis! pour plus.


Jusqu'à la sortie de la 5e édition, la méthode Date.parse entièrement de l' implémentation (la new Date(string) est équivalente à Date.parse sauf que la dernière renvoie un nombre plutôt qu'une Date ). Dans la spécification de la 5ème édition, l'exigence a été ajoutée pour prendre en charge une ISO-8601 simplifiée (et légèrement incorrecte) , mais à part cela, il n'y avait aucune exigence pour ce que Date.parse / new Date(string) devrait accepter autre que Date#toString sortie de Date#toString (sans dire ce que c'était).

Je vous recommande d'analyser votre chaîne de date manuellement et d'utiliser le constructeur Date 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
}

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: "Tenez-vous-en à" YYYY / MM / DD "pour vos cordes de date autant que possible, c'est universellement supporté et sans ambiguïté, avec ce format, toutes les heures sont locales." J'ai mis des tests: http://jsfiddle.net/jlanus/ND2Qg/432/ Ce format: + évite l'ambiguïté de l'ordre du jour et du mois en utilisant la commande ymd et une année à 4 chiffres + évite le problème UTC vs local pas se conformer au format ISO en utilisant des barres obliques + danvk, le gars des dygraphes , dit que ce format est bon dans tous les navigateurs.


Une autre solution consiste à créer un tableau associatif avec un format de date, puis à reformater les données.

Cette méthode est utile pour la date formatée de manière non-aléatoire.

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'];

Voici un extrait de code court et flexible pour convertir une chaîne de date-heure dans un mode de sécurité multi-navigateur comme nicel détaillé 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()




date