Warum ist „2016-02-16“ nicht gleich „2016-02-16 00:00“?

96

Ich versuche, beide Datumszeichenfolgen an zu übergeben new Date(t).

Ich gehe davon aus, dass beide Zeichenfolgen dieselbe Zeit darstellen. Wenn ich die Zeit weglasse, sollte es dann nicht Mitternacht dieses Tages sein?

Aber während,

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

Rückgabe 2016-02-16, Mitternacht, Ortszeit wie erwartet,

new Date("2016-02-16")

gibt 2016-02-16, Mitternacht UTC zurück, was falsch ist oder zumindest nicht das, was ich erwartet habe, wenn man bedenkt, wie die anderen Zeichenfolgen analysiert werden.

Ich würde es verstehen, wenn beide das gleiche Verhalten hätten, ob es die Zeit als Ortszeit oder als UTC zurückgeben soll, aber es scheint sehr inkonsistent zu sein, warum sie verschiedene Dinge wie diese zurückgeben.

Um dieses Problem zu umgehen, kann ich immer dann, wenn ich auf ein Datum stoße, für das es keinen entsprechenden Zeitstempel gibt, "00:00" anhängen, um ein konsistentes Verhalten zu erzielen. Dies scheint jedoch ziemlich fragil zu sein.

Ich erhalte diesen Wert von einem INPUT-Element vom Typ 'datetime-local', daher scheint es besonders inkonsistent zu sein, dass ich einen von einem Seitenelement zurückgegebenen Wert umgehen muss.

Mache ich etwas falsch oder sollte ich etwas anders machen?

Michael
quelle
2
2016-02-16 00:00- Dies scheint überhaupt nicht die gültige Zeit zu sein. ecma-international.org/ecma-262/6.0/… , aber selbst nachdem Sie es Tdort platziert haben, verhält es sich tatsächlich anders
zerkms
Wie genau erhalten Sie aktuell das Date-Objekt vom Eingabeelement basierend auf seinem Wert?
BoltClock
4
Gemäß dem Standard - "Wenn die Felder HH, mm oder ss fehlen" wird "00" als Wert verwendet und der Wert eines fehlenden sss-Felds ist "000". Wenn der Zeitzonenversatz fehlt, wird das Datum und die Uhrzeit angezeigt wird als Ortszeit interpretiert. " --- es sollte sich genauso verhalten.
Zerkms
@BoltClock Hmm, es sieht so aus, als würde es das Wertefeld des Elements nehmen und (wie Zerkms bemerkt haben) das T aus irgendeinem Grund entfernen (ich denke, weil der Wert dem Benutzer in einem Kontext angezeigt wird, in dem "T" verwirrend wäre)
Michael
1
@ Michael: Fantastisch. Browser-Macken wie diese sind mein Favorit. (Oder könnte es eine DOM-Spezifikations-Eigenart sein? Keine Ahnung, ich habe nicht wirklich
nachgesehen

Antworten:

100

In der ES5.1-Spezifikation heißt es:

Der Wert eines fehlenden Zeitzonenversatzes ist "Z".

Es heißt auch:

Die Funktion versucht zunächst, das Format des Strings gemäß den im Date Time String Format (15.9.1.15) angegebenen Regeln zu analysieren. Wenn der String nicht diesem Format entspricht, kann die Funktion auf implementierungsspezifische Heuristiken oder implementierungsspezifische Datumsformate zurückgreifen.

Da das Format ein TTrennzeichen zwischen Datum und Uhrzeit erfordert , gehen die gültigen Zeiten an 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)

... während in node.js eine ungültige Zeit (ohne das T-Trennzeichen) zur implementierungsspezifischen Ortszeit zu gehen scheint:

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

Beachten Sie, dass ES6 dies im selben Teil der Dokumentation geändert hat, in den es geändert wurde:

Wenn der Zeitzonenversatz fehlt, wird die Datums- und Uhrzeit als Ortszeit interpretiert.

Die Freude , Veränderungen zu brechen .

Bearbeiten

Gemäß TC39 soll die Spezifikation so interpretiert werden, dass Datums- und Zeitzeichenfolgen ohne Zeitzone (z. B. "2016-02-16T00: 00: 00") als lokal (gemäß ISO 8601) behandelt werden, jedoch nur Datumszeichenfolgen (z "2016-02-16") als UTC (was nicht mit ISO 8601 vereinbar ist).

Joachim Isaksson
quelle
20
Die Freude, Veränderungen zu brechen, in der Tat. In einem neuen Entwurf des ES7-Standards wurde ein Teil der Änderung von UTC auf Ortszeit zurückgesetzt. Der neue Standard besagt, dass Datumsangaben als UTC interpretiert werden sollten, wenn keine Zeitzone angegeben ist, während Datums- und Uhrzeitangaben als Ortszeit interpretiert werden sollten, wenn keine Zeitzone vorhanden ist angegeben.
hichris123
3
Sie sollten wirklich beide Ortszeit sein, unabhängig von Nur-Datum- oder Datums- / Uhrzeitformen. So funktioniert ISO8601. Es ist lächerlich, dass das Nur-Datum-Formular als Mitternacht-UTC interpretiert wird, da ein zeitloses UTC-Datum konzeptionell nicht einmal Sinn macht. Auf der ECMA tc39 gab es eine hitzige Debatte darüber. Ich habe um die Ortszeit gekämpft und verloren. github.com/tc39/ecma262/issues/87
Matt Johnson-Pint
10

Gemäß den Spezifikationen :

Die Funktion versucht zunächst, das Format des Strings gemäß den im Date Time String Format (15.9.1.15) angegebenen Regeln zu analysieren. Wenn der String nicht diesem Format entspricht, kann die Funktion auf implementierungsspezifische Heuristiken oder implementierungsspezifische Datumsformate zurückgreifen.

Und Datum Zeit Zeichenfolgenformate akzeptieren 2016-02-16als gültiges Datum

Dieses Format enthält nur Datumsformulare:

JJJJ
JJJJ-MM
JJJJ-MM-TT

[...] Wenn die Felder HH, mm oder ss fehlen, wird "00" als Wert verwendet und der Wert eines fehlenden sss-Felds ist "000". Der Wert eines fehlenden Zeitzonenversatzes ist "Z".

So 2016-02-16übersetzt zu 2016-02-16T00:00:00.000Z.

Das andere Datum 2016-02-16 00:00entspricht nicht dem Format und daher ist die Analyse implementierungsspezifisch. Anscheinend werden solche Daten als lokale Zeitzonen behandelt, und Ihr Beispieldatum gibt je nach Zeitzone unterschiedliche Werte zurück:

/* 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

Zusammenfassung:

  • Für konforme Datums- und Uhrzeitformate ist das Verhalten gut definiert. Wenn kein Zeitzonenversatz vorhanden ist, wird die Datumszeichenfolge als UTC (ES5) oder lokal (ES6) behandelt.
  • Bei nicht konformen Datums- und Uhrzeitformaten ist das Verhalten implementierungsspezifisch. Wenn kein Zeitzonenversatz vorliegt, wird das Datum normalerweise als lokal behandelt.
  • Tatsächlich könnte die Implementierung entscheiden, zurückzukehren, NaNanstatt zu versuchen , nicht konforme Daten zu analysieren. Testen Sie einfach Ihren Code in Internet Explorer 11;)
Salman A.
quelle
7

Möglicherweise stoßen Sie auf Unterschiede zwischen ES5- und ES6-Implementierungen und Ihrem erwarteten Ergebnis. Per Date.parse bei MDN ist "insbesondere bei verschiedenen ECMAScript-Implementierungen, bei denen Zeichenfolgen wie" 2015-10-12 12:00:00 "als NaN, UTC oder lokale Zeitzone analysiert werden können" von Bedeutung.

Zusätzliche Tests in Firefox 44 und IE 11 ergaben, dass beide ein new Date("2016-02-16 00:00")Datumsobjekt für zurückgeben , für das NaN beim Versuch, einen Datumskomponentenwert abzurufen, NaN zurückgibt und dessen toString-Wert "Ungültiges Datum" (nicht "NaN") ist. Daher kann das Anhängen von "00:00, um ein konsistentes Verhalten zu erzielen" in verschiedenen Browsern leicht zu Problemen führen.

Wie in anderen Antworten erwähnt, wird new Date("2016-02-16")standardmäßig ein Zeitzonenversatz von Null verwendet, der Mitternacht UTC anstelle von lokal erzeugt.

traktor53
quelle
OP scheint bei derselben Implementierung unterschiedliche Ergebnisse zu erzielen.
Salman A
6

Pro DateParser::Parse()V8-Quellcode für Chrome.

ES5 ISO 8601 Daten:

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

Eine vorzeichenlose Zahl gefolgt von ':' ist ein Zeitwert und wird dem TimeComposer hinzugefügt.

Die Zeitzone ist standardmäßig Z, wenn sie fehlt

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

Eine Zeichenfolge, die beiden Formaten entspricht (z. B. 1970-01-01), wird als ES5-Datums- / Uhrzeitzeichenfolge analysiert. Dies bedeutet, dass standardmäßig UTC time-zone verwendet wird. Dies ist unvermeidlich, wenn Sie die ES5-Spezifikation befolgen.

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

gibt 2016-02-16, Mitternacht UTC zurück, was falsch ist oder zumindest nicht das, was ich erwartet habe, wenn man bedenkt, wie die anderen Zeichenfolgen analysiert werden.

Es fügt den Zeitzonenversatz zum hinzu 00:00

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

Meine Zeitzone ist IST mit einem Versatzwert (in Minuten) +330, daher wurden 330 Minuten zu 00:00 hinzugefügt.

Wie pro ECMA-262, Abschnitt 20.3.3.2 Date.parse (string)

Wenn ToString zu einem abrupten Abschluss führt, wird der Abschlussdatensatz sofort zurückgegeben. Andernfalls interpretiert parse den resultierenden String als Datum und Uhrzeit. Es gibt eine Zahl zurück, deren UTC-Zeitwert dem Datum und der Uhrzeit entspricht. Der String kann abhängig vom Inhalt des Strings als Ortszeit, UTC-Zeit oder Zeit in einer anderen Zeitzone interpretiert werden.

Wenn Sie die Zeiteinheiten explizitnew Date("2016-02-16 00:00") festlegen, wird set that as hoursand minutes,

Ansonsten wie hier in 2 0.3.1.16 angegeben

Wenn der Zeitzonenversatz fehlt, wird die Datums- und Uhrzeit als Ortszeit interpretiert.

gurvinder372
quelle
Ja, aber warum macht es das in einem Fall, aber nicht im anderen?
Michael
@ Michael ja, überprüfen Sie ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Warum sind das unterschiedliche Ergebnisse? Der Standard behauptet, dass es das gleiche sein muss
zerkms
@zerkms Standard sagt, was es tun wird, wenn Sie die Zeiteinheiten überschreiten. Es sagt nicht, was es tun wird, wenn Sie es nicht tun. Es heißt klar: 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.Wo behauptet es, dass es dasselbe sein muss?
Gurvinder372
@ gurvinder372 Ich habe das Zitat in den Fragenkommentaren angegeben: "Wenn die Felder HH, mm oder ss fehlen, wird" 00 "als Wert verwendet und der Wert eines fehlenden sss-Felds ist" 000 ". Wenn der Zeitzonenversatz fehlt, wird das Datum und die Uhrzeit als Ortszeit interpretiert. "
Zerkms