Pourquoi "2016-02-16" n'est-il pas égal à "2016-02-16 00:00"?

96

J'essaye de passer les deux chaînes de date à new Date(t).

Je m'attends à ce que les deux chaînes représentent la même heure, après tout, si j'omets l'heure, ne devrait-il pas être minuit ce jour-là?

Mais en même temps,

new Date("2016-02-16 00:00")

renvoie 16/02/2016, minuit, heure locale comme prévu,

new Date("2016-02-16")

renvoie 16/02/2016, minuit UTC, ce qui est faux, ou du moins pas ce à quoi je m'attendais étant donné ce que l'autre chaîne analyse.

Je comprendrais qu'ils aient tous les deux le même comportement, que ce soit pour renvoyer l'heure comme heure locale ou comme UTC, mais il semble très incohérent pourquoi ils renvoient des choses différentes comme celle-ci.

Comme solution de contournement, chaque fois que je rencontre une date qui n'a pas d'horodatage correspondant, je peux ajouter "00:00" pour obtenir un comportement cohérent, mais il semble que cela soit plutôt fragile.

J'obtiens cette valeur d'un élément INPUT, de type 'datetime-local', il semble donc particulièrement incohérent que je doive contourner une valeur retournée par un élément de page.

Est-ce que je fais quelque chose de mal ou devrais-je faire quelque chose différemment?

Michael
la source
2
2016-02-16 00:00- cela ne ressemble pas du tout à l'heure valide. ecma-international.org/ecma-262/6.0/... , mais même après l'avoir mis Tlà, il se comporte effectivement différemment
zerkms
Comment obtenez-vous actuellement l'objet Date à partir de l'élément d'entrée en fonction de sa valeur?
BoltClock
4
Conformément à la norme - "Si les champs HH, mm ou ss sont absents" 00 "est utilisé comme valeur et la valeur d'un champ sss absent est" 000 ". Si le décalage de fuseau horaire est absent, la date-heure est interprétée comme une heure locale. " --- il devrait se comporter de la même manière.
zerkms
@BoltClock Hmm, on dirait qu'il prend le champ de valeur de l'élément et (comme Zerkms l'a remarqué) en supprimant le T pour une raison quelconque (je pense que parce que la valeur est affichée à l'utilisateur dans un contexte où "T" serait déroutant)
Michael
1
@Michael: Fantastique. Les bizarreries de navigateur comme celle-ci sont mes préférées. (Ou pourrait-il être une bizarrerie de spécification DOM? Aucune idée, je n'ai pas vraiment regardé.)
BoltClock

Réponses:

100

C'est ce que dit la spécification ES5.1 :

La valeur d'un décalage de fuseau horaire absent est «Z».

Il dit aussi:

La fonction tente d'abord d'analyser le format de la chaîne selon les règles appelées dans le format de chaîne de date et d'heure (15.9.1.15). Si la chaîne n'est pas conforme à ce format, la fonction peut revenir à n'importe quelle heuristique spécifique à l'implémentation ou aux formats de date spécifiques à l'implémentation.

Étant donné que le format nécessite un Tséparateur entre la date et l'heure, les heures valides vont à UTC:

> new Date("2016-02-16T00:00:00")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)
> new Date("2016-02-16")
Tue Feb 16 2016 01:00:00 GMT+0100 (CET)

... tandis que dans node.js, une heure invalide (sans le séparateur T) semble aller à l'heure locale spécifique à l'implémentation:

> new Date("2016-02-16 00:00:00")
Tue Feb 16 2016 00:00:00 GMT+0100 (CET)

Notez que ES6 a changé cela, dans la même partie de la documentation qu'il change:

Si le décalage de fuseau horaire est absent, la date-heure est interprétée comme une heure locale.

La joie de rompre les changements .

Éditer

Selon TC39 , la spécification est censée être interprétée comme des chaînes de date et d'heure sans fuseau horaire (par exemple, "2016-02-16T00: 00: 00") sont traitées comme locales (selon ISO 8601), mais uniquement les chaînes de date (par exemple "2016-02-16") comme UTC (ce qui est incompatible avec l'ISO 8601).

Joachim Isaksson
la source
20
La joie de rompre les changements, en effet. Dans un nouveau projet de la norme ES7, une partie du changement de l'heure UTC à l'heure locale a été annulée La nouvelle norme dit que les dates doivent être interprétées comme UTC si aucun fuseau horaire n'est spécifié, tandis que les dates-heures doivent être interprétées comme une heure locale si aucun fuseau horaire est spécifié.
hichris123
3
Ils devraient vraiment tous les deux être l'heure locale, indépendamment des formes de date uniquement ou de date-heure. C'est ainsi que fonctionne ISO8601. Il est ridicule que la forme de date uniquement soit interprétée comme minuit UTC, car une date UTC intemporelle n'a même pas de sens conceptuellement. Il y a eu un débat animé à ce sujet à ECMA tc39. Je me suis battu pour l'heure locale et j'ai perdu. github.com/tc39/ecma262/issues/87
Matt Johnson-Pint
10

Selon les spécifications :

La fonction tente d'abord d'analyser le format de la chaîne selon les règles appelées dans le format de chaîne de date et d'heure (15.9.1.15). Si la chaîne n'est pas conforme à ce format, la fonction peut revenir à n'importe quelle heuristique spécifique à l'implémentation ou aux formats de date spécifiques à l'implémentation.

Et les formats de chaîne de date et d' heure acceptent 2016-02-16comme date valide

Ce format inclut les formulaires à date uniquement:

AAAA
AAAA-MM
AAAA-MM-JJ

[...] Si les champs HH, mm ou ss sont absents, «00» est utilisé comme valeur et la valeur d'un champ sss absent est «000». La valeur d'un décalage de fuseau horaire absent est «Z».

Ainsi se 2016-02-16traduit par 2016-02-16T00:00:00.000Z.

L'autre date 2016-02-16 00:00n'est pas conforme au format et son analyse est donc spécifique à l'implémentation. Apparemment, ces dates sont traitées comme ayant un fuseau horaire local et votre exemple de date renverra des valeurs différentes selon le fuseau horaire:

/* tz = +05:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-15T19:00:00.000Z
/* tz = -08:00 */ new Date("2016-02-16 00:00").toISOString() // 2016-02-16T08:00:00.000Z

Résumé:

  • Pour les formats de date et d'heure conformes, le comportement est bien défini - en l'absence de décalage de fuseau horaire, la chaîne de date est traitée comme UTC (ES5) ou locale (ES6).
  • Pour les formats de date et d'heure non conformes, le comportement est spécifique à l'implémentation - en l'absence de décalage de fuseau horaire, le comportement habituel est de traiter la date comme locale.
  • En fait, l'implémentation pourrait choisir de revenir NaNau lieu d' essayer d'analyser les dates non conformes. Testez simplement votre code dans Internet Explorer 11;)
Salman A
la source
7

Vous rencontrez peut-être des différences entre les implémentations ES5, ES6 et le résultat attendu. Par date.parse chez MDN, "en particulier dans les différentes implémentations ECMAScript où des chaînes telles que" 2015-10-12 12:00:00 "peuvent être analysées en tant que NaN, UTC ou fuseau horaire local" est significative.

Des tests supplémentaires dans Firefox 44 et IE 11 ont révélé qu'ils renvoyaient tous deux un objet de date pour new Date("2016-02-16 00:00") , lequel objet renvoie NaN lors de la tentative d'obtention d'une valeur de composant de date, et dont la valeur toString est "Date invalide" (et non "NaN"). Par conséquent, l'ajout de "00:00 pour obtenir un comportement cohérent" peut facilement se briser dans différents navigateurs.

Comme indiqué dans d'autres réponses, new Date("2016-02-16")utilise un décalage de fuseau horaire de zéro par défaut, produisant minuit UTC au lieu de local.

traktor53
la source
OP semble obtenir des résultats différents sur la même implémentation.
Salman A
6

Par DateParser::Parse()code source V8 pour Chrome.

Dates ES5 ISO 8601:

[('-'|'+')yy]yyyy[-MM[-DD]][THH:mm[:ss[.sss]][Z|(+|-)hh:mm]]

Un nombre non signé suivi de «:» est une valeur de temps et est ajouté à TimeComposer.

le fuseau horaire est par défaut Z s'il est absent

> new Date("2016-02-16 00:00")
  Tue Feb 16 2016 00:00:00 GMT+0800 (China Standard Time)

Une chaîne qui correspond à deux formats (par exemple 1970-01-01) sera analysé comme une chaîne date-heure ES5 - ce qui signifie qu'il sera par défaut UTC time-zone. C'est inévitable si vous suivez la spécification ES5.

> new Date("2016-02-16")
Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)
zangw
la source
3

renvoie 16/02/2016, minuit UTC, ce qui est faux, ou du moins pas ce à quoi je m'attendais étant donné ce que l'autre chaîne analyse.

Il ajoute le décalage du fuseau horaire au 00:00

new Date("2016-02-16") les sorties Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

Mon fuseau horaire étant IST avec une valeur de décalage (en minutes) +330, il a donc ajouté 330 minutes à 00:00.

Selon ecma-262, section 20.3.3.2 Date.parse (chaîne)

Si ToString entraîne un achèvement brutal, l'enregistrement d'achèvement est immédiatement renvoyé. Sinon, parse interprète la chaîne résultante comme une date et une heure; il renvoie un nombre, la valeur de l'heure UTC correspondant à la date et à l'heure. La chaîne peut être interprétée comme une heure locale, une heure UTC ou une heure dans un autre fuseau horaire, selon le contenu de la chaîne.

Lorsque vous définissez explicitement les unités de temps, new Date("2016-02-16 00:00")il utilisera défini comme hourset minutes,

Sinon, comme indiqué ici en 2 0.3.1.16

Si le décalage de fuseau horaire est absent, la date-heure est interprétée comme une heure locale.

gurvinder372
la source
Oui mais pourquoi fait-il cela dans un cas mais pas dans l'autre?
Michael
@Michael oui, vérifiez ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Alors pourquoi ce sont des résultats différents? La norme prétend que ce doit être la même chose
zerkms
@zerkms Standard dit ce qu'il fera lorsque vous passerez les unités de temps, il ne dit pas ce qu'il fera lorsque vous ne le ferez pas. Il dit clairement The String may be interpreted as a local time, a UTC time, or a time in some other time zone, depending on the contents of the String.où prétend-il que ce doit être la même chose?
gurvinder372
@ gurvinder372 J'ai fourni la citation dans les commentaires de la question: "Si les champs HH, mm ou ss sont absents," 00 "est utilisé comme valeur et la valeur d'un champ sss absent est" 000 ". Si le décalage de fuseau horaire est absent, la date-heure est interprétée comme une heure locale. "
zerkms