Podczas ostatnich doświadczeń, pisząc tłumacza JS, dużo zmagałem się z wewnętrznym funkcjonowaniem dat ECMA / JS. Więc myślę, że wrzucę tutaj moje 2 centy. Mamy nadzieję, że udostępnienie tych informacji pomoże innym w razie jakichkolwiek pytań dotyczących różnic między przeglądarkami w zakresie sposobu obchodzenia się z datami.
Strona wejściowa
Wszystkie implementacje przechowują swoje wartości daty wewnętrznie jako liczby 64-bitowe, które reprezentują liczbę milisekund (ms) od 1970-01-01 UTC (GMT to to samo, co UTC). Ta data to epoka ECMAScript, która jest również używana przez inne języki, takie jak systemy Java i POSIX, takie jak UNIX. Daty występujące po epoce są liczbami dodatnimi, a wcześniejsze daty są ujemne.
Poniższy kod jest interpretowany jako ta sama data we wszystkich bieżących przeglądarkach, ale z przesunięciem lokalnej strefy czasowej:
Date.parse('1/1/1970'); // 1 January, 1970
W mojej strefie czasowej (EST, która wynosi -05: 00), wynik to 18000000, ponieważ tyle ms jest w ciągu 5 godzin (to tylko 4 godziny w miesiącach letnich). Wartość będzie inna w różnych strefach czasowych. To zachowanie jest określone w ECMA-262, więc wszystkie przeglądarki robią to w ten sam sposób.
Chociaż istnieją pewne różnice w formatach ciągów wejściowych, które główne przeglądarki będą analizować jako daty, zasadniczo interpretują je tak samo, jeśli chodzi o strefy czasowe i czas letni, nawet jeśli parsowanie jest w dużej mierze zależne od implementacji.
Jednak format ISO 8601 jest inny. Jest to jeden z dwóch formatów opisanych w ECMAScript 2015 (ed. 6), który musi zostać przeanalizowany w ten sam sposób przez wszystkie implementacje (drugi to format określony dla Date.prototype.toString ).
Ale nawet w przypadku ciągów formatu ISO 8601 niektóre implementacje źle to rozumieją. Oto wynik porównania przeglądarki Chrome i Firefox, gdy ta odpowiedź została pierwotnie napisana dla okresu 1/1/1970 (epoki) na moim komputerze przy użyciu ciągów formatu ISO 8601, które powinny zostać sparsowane do dokładnie tej samej wartości we wszystkich implementacjach:
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
- W pierwszym przypadku specyfikator „Z” wskazuje, że dane wejściowe są w czasie UTC, więc nie są przesunięte w stosunku do epoki, a wynik wynosi 0
- W drugim przypadku specyfikator „-0500” wskazuje, że dane wejściowe są w GMT-05: 00 i obie przeglądarki interpretują dane wejściowe w strefie czasowej -05: 00. Oznacza to, że wartość UTC jest przesunięta w stosunku do epoki, co oznacza dodanie 18000000 ms do wewnętrznej wartości czasu daty.
- Trzeci przypadek, w którym nie ma specyfikatora, powinien być traktowany jako lokalny dla systemu hosta. FF poprawnie traktuje dane wejściowe jako czas lokalny, podczas gdy Chrome traktuje je jako czas UTC, więc generuje różne wartości czasu. Dla mnie tworzy to 5-godzinną różnicę w przechowywanej wartości, co jest problematyczne. Inne systemy z różnymi przesunięciami uzyskają różne wyniki.
Ta różnica została naprawiona od 2020 r., Ale istnieją inne dziwactwa między przeglądarkami podczas analizowania ciągów formatu ISO 8601.
Ale robi się coraz gorzej. Dziwactwo ECMA-262 polega na tym, że format wyłącznie daty ISO (RRRR-MM-DD) musi być analizowany jako UTC, podczas gdy ISO 8601 wymaga, aby był analizowany jako lokalny. Oto dane wyjściowe z FF z długim i krótkim formatem daty ISO bez specyfikatora strefy czasowej.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Tak więc pierwszy jest analizowany jako lokalny, ponieważ jest to data i godzina ISO 8601 bez strefy czasowej, a drugi jest analizowany jako UTC, ponieważ jest to tylko data ISO 8601.
Tak więc, aby bezpośrednio odpowiedzieć na pierwotne pytanie, "YYYY-MM-DD"
ECMA-262 musi być interpretowana jako UTC, podczas gdy drugie jest interpretowane jako lokalne. Dlatego:
To nie daje równoważnych wyników:
console.log(new Date(Date.parse("Jul 8, 2005")).toString()); // Local
console.log(new Date(Date.parse("2005-07-08")).toString()); // UTC
To robi:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
Najważniejsze jest to, aby parsować ciągi dat. TYLKO ciąg ISO 8601, który można bezpiecznie analizować w różnych przeglądarkach, to długi formularz z przesunięciem (± GG: mm lub „Z”). Jeśli to zrobisz, możesz bezpiecznie przechodzić między godzinami lokalnymi i UTC.
Działa to w różnych przeglądarkach (po IE9):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
Większość obecnych przeglądarek równo traktuje inne formaty wejściowe, w tym często używane „1/1/1970” (M / D / RRRR) i „1/1/1970 00:00:00 AM” (M / D / RRRR hh : mm: ss ap) formaty. Wszystkie poniższe formaty (oprócz ostatniego) są traktowane jako dane czasu lokalnego we wszystkich przeglądarkach. Wyjście tego kodu jest takie samo we wszystkich przeglądarkach w mojej strefie czasowej. Ostatni jest traktowany jako -05: 00 niezależnie od strefy czasowej hosta, ponieważ przesunięcie jest ustawione na znaczniku czasu:
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"));
Ponieważ jednak parsowanie nawet formatów określonych w ECMA-262 nie jest spójne, zaleca się, aby nigdy nie polegać na wbudowanym analizatorze składni i zawsze ręcznie analizować ciągi, na przykład za pomocą biblioteki i przekazać format do analizatora składni.
Np. W momencie.js możesz napisać:
let m = moment('1/1/1970', 'M/D/YYYY');
Strona wyjściowa
Po stronie wyjściowej wszystkie przeglądarki tłumaczą strefy czasowe w ten sam sposób, ale inaczej obsługują formaty ciągów. Oto toString
funkcje i dane wyjściowe. Zwróć uwagę na wyjście funkcji toUTCString
i toISOString
5:00 AM na moim komputerze. Ponadto nazwa strefy czasowej może być skrótem i może być różna w różnych implementacjach.
Konwertuje czas UTC na czas lokalny przed wydrukowaniem
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Drukuje bezpośrednio zapisany czas UTC
- toUTCString
- toISOString
W 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
W przeglądarce 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
Zwykle nie używam formatu ISO do wprowadzania tekstu. Używanie tego formatu jest dla mnie jedyne, kiedy daty muszą być sortowane jako ciągi. Format ISO można sortować w obecnym stanie, podczas gdy inne nie. Jeśli musisz mieć zgodność z różnymi przeglądarkami, określ strefę czasową lub użyj zgodnego formatu ciągu.
Kod new Date('12/4/2013').toString()
przechodzi następującą wewnętrzną pseudo-transformację:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
Mam nadzieję, że ta odpowiedź była pomocna.