Dlaczego „2016-02-16” nie jest równe „2016-02-16 00:00”?

96

Próbuję przekazać oba ciągi dat do new Date(t).

Spodziewam się, że oba ciągi reprezentują ten sam czas, w końcu, jeśli pominę godzinę, czy nie powinna to być północ tego dnia?

Ale podczas gdy

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

zwraca 16.02.2016, o północy czasu lokalnego zgodnie z oczekiwaniami,

new Date("2016-02-16")

zwraca 2016-02-16, północ UTC, co jest błędne, a przynajmniej nie to, czego się spodziewałem, biorąc pod uwagę to, co inny ciąg analizuje jako.

Zrozumiałbym to, gdyby obaj zachowywali się tak samo, niezależnie od tego, czy zwracają czas jako czas lokalny, czy jako UTC, ale wydaje się bardzo niekonsekwentne, dlaczego zwracają różne takie rzeczy.

Aby obejść ten problem, za każdym razem, gdy napotkam datę, która nie ma odpowiedniego znacznika czasu, mogę dodać „00:00”, aby uzyskać spójne zachowanie, ale wydaje się, że jest to raczej delikatne.

Otrzymuję tę wartość z elementu INPUT typu „datetime-local”, więc wydaje się szczególnie niespójne, że muszę obejść wartość zwracaną przez element strony.

Czy robię coś źle, czy powinienem robić coś inaczej?

Michael
źródło
2
2016-02-16 00:00- to wcale nie wygląda na aktualny czas. ecma-international.org/ecma-262/6.0/… , ale nawet po umieszczeniu Ttam rzeczywiście zachowuje się inaczej
zerkms
Jak dokładnie uzyskujesz obiekt Date z elementu wejściowego na podstawie jego wartości?
BoltClock
4
Zgodnie ze standardem - „Jeśli nie ma pól HH, mm lub ss”, jako wartość przyjmuje się „00”, a wartość pola nieobecnego sss to „000”. W przypadku braku przesunięcia strefy czasowej, data-godzina jest interpretowany jako czas lokalny. " --- powinien zachowywać się tak samo.
zerkms
@BoltClock Hmm, wygląda na to, że przyjmuje pole wartości elementu i (jak zauważyli zerkms) usuwa T z jakiegoś powodu (myślę, że wartość jest wyświetlana użytkownikowi w kontekście, w którym „T” byłoby mylące)
Michael
1
@Michael: Fantastycznie. Takie dziwactwa przeglądarek są moimi ulubionymi. (A może to dziwactwo specyfikacji DOM? Nie mam pojęcia, tak naprawdę nie patrzyłem.)
BoltClock

Odpowiedzi:

100

Oto, co mówi specyfikacja ES5.1 :

Wartość przesunięcia brakującej strefy czasowej to „Z”.

Mówi również:

Funkcja najpierw próbuje przeanalizować format ciągu zgodnie z regułami określonymi w formacie ciągu daty i godziny (15.9.1.15). Jeśli String nie jest zgodny z tym formatem, funkcja może powrócić do dowolnej heurystyki specyficznej dla implementacji lub formatów daty specyficznych dla implementacji.

Ponieważ format wymaga Tseparatora między datą i godziną, prawidłowe czasy przechodzą do 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)

... podczas gdy w node.js nieprawidłowy czas (bez separatora T) wydaje się iść do czasu lokalnego specyficznego dla implementacji:

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

Zwróć uwagę, że ES6 zmieniło to, w tej samej części dokumentacji, w której zmienia się:

Jeśli nie ma przesunięcia strefy czasowej, data-godzina jest interpretowana jako czas lokalny.

Radość z przełamywania zmian .

Edytować

Zgodnie z TC39 specyfikację należy interpretować jako ciągi daty i godziny bez strefy czasowej (np. „2016-02-16T00: 00: 00”) są traktowane jako lokalne (zgodnie z ISO 8601), ale ciągi zawierające tylko datę (np. „2016-02-16”) jako UTC (co jest niezgodne z ISO 8601).

Joachim Isaksson
źródło
20
Rzeczywiście radość z przełamywania zmian. W nowym projekcie standardu ES7 część zmiany z czasu UTC na czas lokalny została cofnięta Nowa norma mówi, że daty powinny być interpretowane jako UTC, jeśli nie określono strefy czasowej, natomiast daty i godziny należy interpretować jako czas lokalny, jeśli nie ma strefy czasowej jest specyficzne.
hichris123
3
Tak naprawdę oba powinny być czasem lokalnym, niezależnie od formularzy zawierających tylko datę lub datę i godzinę. Tak działa ISO8601. To śmieszne, że forma zawierająca tylko datę jest interpretowana jako północ UTC, ponieważ ponadczasowa data UTC nie ma nawet sensu koncepcyjnego. Na ECMA tc39 odbyła się gorąca debata na ten temat. Walczyłem o czas lokalny i przegrałem. github.com/tc39/ecma262/issues/87
Matt Johnson-Pint
10

Zgodnie ze specyfikacją :

Funkcja najpierw próbuje przeanalizować format ciągu zgodnie z regułami określonymi w formacie ciągu daty i godziny (15.9.1.15). Jeśli String nie jest zgodny z tym formatem, funkcja może powrócić do dowolnej heurystyki specyficznej dla implementacji lub formatów daty specyficznych dla implementacji.

Formaty ciągów daty i godziny są akceptowane 2016-02-16jako poprawna data

Ten format obejmuje formularze zawierające tylko datę:

RRRR
RRRR-MM
RRRR-MM-DD

[...] Jeśli nie ma pól HH, mm lub ss, jako wartość przyjmuje się „00”, a wartość pola nieobecnego sss to „000”. Wartość przesunięcia brakującej strefy czasowej to „Z”.

A zatem 2016-02-16 przekłada się na 2016-02-16T00:00:00.000Z.

Druga data 2016-02-16 00:00nie jest zgodna z formatem i dlatego jej analiza jest zależna od implementacji. Najwyraźniej takie daty są traktowane jako mające lokalną strefę czasową, a Twoja przykładowa data zwróci różne wartości w zależności od strefy czasowej:

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

Podsumowanie:

  • W przypadku zgodnych formatów daty i godziny zachowanie jest dobrze zdefiniowane - w przypadku braku przesunięcia strefy czasowej ciąg daty jest traktowany jako UTC (ES5) lub lokalny (ES6).
  • W przypadku niezgodnych formatów daty i godziny zachowanie jest specyficzne dla implementacji - w przypadku braku przesunięcia strefy czasowej zwykle zachowuje się traktowanie daty jako lokalnej.
  • W rzeczywistości implementacja mogłaby wybrać powrót NaNzamiast próbować analizować niezgodne daty. Po prostu przetestuj swój kod w przeglądarce Internet Explorer 11;)
Salman A
źródło
7

Być może napotykasz różnice między implementacjami ES5, ES6 a oczekiwanym wynikiem. Per Date.parse at MDN, „szczególnie w różnych implementacjach ECMAScript, w których ciągi takie jak„ 2015-10-12 12:00:00 ”mogą być analizowane jako NaN, UTC lub lokalna strefa czasowa” ma znaczenie.

Dodatkowe testy w Firefoksie 44 i IE 11 ujawniły, że oba zwracają obiekt daty dla new Date("2016-02-16 00:00") którego obiekt zwraca NaN podczas próby uzyskania wartości składnika daty i którego wartością toString jest „Invalid Date” (nie „NaN”). Dlatego dołączanie „00:00, aby uzyskać spójne zachowanie” może łatwo zepsuć się w różnych przeglądarkach.

Jak zauważono w innych odpowiedziach new Date("2016-02-16"), domyślnie stosowane jest przesunięcie strefy czasowej o zero, co daje północ UTC zamiast lokalnej.

traktor53
źródło
Wydaje się, że OP uzyskuje różne wyniki przy tej samej realizacji.
Salman A,
6

Według DateParser::Parse()kodów źródłowych V8 dla Chrome.

Daty ES5 ISO 8601:

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

Liczba bez znaku, po której następuje „:”, jest wartością czasu i jest dodawana do TimeComposer.

Strefa czasowa jest domyślnie ustawiona na Z, jeśli jej nie ma

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

Łańcuch pasujący do obu formatów (np. 1970-01-01) Zostanie przeanalizowany jako ciąg daty i godziny ES5 - co oznacza, że ​​domyślnie będzie UTC time-zone. Jest to nieuniknione, jeśli postępujesz zgodnie ze specyfikacją ES5.

> new Date("2016-02-16")
Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)
zangw
źródło
3

zwraca 2016-02-16, północ UTC, co jest błędne, a przynajmniej nie to, czego się spodziewałem, biorąc pod uwagę to, co inny ciąg analizuje jako.

Dodaje przesunięcie strefy czasowej do pliku 00:00

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

Moja strefa czasowa to IST z wartością przesunięcia (w minutach) +330, więc dodała 330 minut do 00:00.

Zgodnie z ecma-262, sekcja 20.3.3.2 Date.parse (string)

Jeśli ToString spowoduje nagłe zakończenie, rekord ukończenia jest natychmiast zwracany. W przeciwnym razie parse interpretuje wynikowy ciąg znaków jako datę i godzinę; zwraca liczbę, wartość czasu UTC odpowiadającą dacie i godzinie. Ciąg może być interpretowany jako czas lokalny, czas UTC lub czas w innej strefie czasowej, w zależności od zawartości ciągu.

Gdy jawnie ustawisz jednostki czasu, new Date("2016-02-16 00:00")których użyje, ustaw to jako hoursi minutes,

W innym przypadku, jak podano w 2 0.3.1.16

Jeśli nie ma przesunięcia strefy czasowej, data-godzina jest interpretowana jako czas lokalny.

gurvinder372
źródło
Tak, ale dlaczego robi to w jednym przypadku, a w drugim nie?
Michael
@Michael tak, sprawdź ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Dlaczego więc są to różne wyniki? Standard twierdzi, że musi być tak samo
zerkms
@zerkms Standard mówi, co zrobi, gdy miniesz jednostki czasu, nie mówi, co zrobi, gdy tego nie zrobisz. Wyraźnie mówi, 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.gdzie twierdzi, że musi być taki sam?
gurvinder372
@ gurvinder372 Podałem cytat w komentarzach do pytania: „Jeśli pola HH, mm lub ss są nieobecne, jako wartość używana jest wartość„ 00 ”, a wartość pola nieobecnego sss to„ 000 ”. Jeśli przesunięcie strefy czasowej jest nieobecny, data-czas jest interpretowana jako czas lokalny. "
zerkms