Во время недавнего опыта написания интерпретатора JS я много боролся с внутренней работой дат ECMA / JS. Итак, я полагаю, что я добавлю свои 2 цента здесь. Надеюсь, что обмен этими материалами поможет другим с любыми вопросами о различиях между браузерами в том, как они обрабатывают даты.
Сторона ввода
Все реализации хранят свои значения даты внутри как 64-битные числа, которые представляют количество миллисекунд с 01.01.1970 UTC (GMT - то же самое, что и UTC). Даты после 1/1/1970 00:00:00
являются положительными числами, а даты до являются отрицательными.
Следовательно, следующий код дает одинаковый результат во всех браузерах.
Date.parse('1/1/1970');
В моем часовом поясе (EST) результат равен 18000000, потому что это количество мс за 5 часов (это всего 4 часа в летнее время). Значение будет отличаться в разных часовых поясах. Все основные браузеры делают это одинаково.
Вот загвоздка, хотя. Хотя в форматах входных строк есть некоторые различия, которые основные браузеры будут анализировать как даты, они, по сути, интерпретируют их так же, как часовые пояса и переход на летнее время. Один из них - это формат ISO 8601. Это единственный формат, описанный в спецификации ECMA-262 v.5. Для всех других форматов строк интерпретация зависит от реализации. По иронии судьбы, это формат, в котором браузеры могут отличаться. Вот сравнительный вывод Chrome и Firefox за 01.01.1970 на моей машине с использованием строкового формата 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
- Спецификатор «Z» указывает, что вход уже находится во времени UTC и не требует смещения перед сохранением.
- Спецификатор "-0500" указывает, что ввод в GMT-05: 00, поэтому оба
браузеры интерпретируют ввод как находящийся в моем местном часовом поясе. Это означает, что
значение преобразуется в UTC перед сохранением. В моем случае это означает добавление 18000000 мс к внутреннему значению даты, поэтому требуется сдвиг -18000000 мс (-05: 00), чтобы вернуть меня по местному времени.
- Однако при отсутствии спецификатора FF обрабатывает ввод как местное время, тогда как Chrome
рассматривает это как время UTC. Для меня это создает 5-часовую разницу в сохраненном значении, что проблематично. В моей реализации я остановился на FF, потому что мне нравится, когда вывод
toString
совпадает с моим входным значением, если я не укажу альтернативный часовой пояс, чего я никогда не делаю. Отсутствие спецификатора должно предполагать ввод местного времени.
Но здесь ситуация ухудшается: FF обрабатывает краткую форму формата ISO 8601 («ГГГГ-ММ-ДД») иначе, чем длинную форму («ГГГГ-ММ-ДДГЧ: мм: сс: sssZ»). ") без всякой логической причины. Вот выходные данные FF с длинными и короткими форматами даты ISO без указания часового пояса.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Итак, чтобы непосредственно ответить на первоначальный вопрос Аскера, "YYYY-MM-DD"
- это краткая форма формата ISO 8601 "YYYY-MM-DDTHH:mm:ss:sssZ"
. Таким образом, это интерпретируется как время UTC, в то время как другое интерпретируется как местное. Вот почему
Это не джайв:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08")).toString());
Это делает:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
Суть в том, что для разбора строк даты. Единственная строка ISO 8601, которую вы можете безопасно анализировать в разных браузерах, - это длинная форма. И ВСЕГДА используйте спецификатор "Z". Если вы сделаете это, вы можете спокойно переходить между местным и UTC временем.
Это работает во всех браузерах (после IE9):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
К счастью, большинство современных браузеров обрабатывают другие форматы ввода одинаково, включая наиболее часто используемые форматы «01.01.1970» и «01.01.1970 00:00:00 AM». Все следующие форматы (и другие) обрабатываются как ввод местного времени во всех браузерах и перед сохранением преобразуются в UTC. Таким образом, делая их кросс-браузерными. Вывод этого кода одинаков во всех браузерах в моем часовом поясе.
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"));
Выходная сторона
Со стороны вывода все браузеры переводят часовые пояса одинаково, но по-разному обрабатывают строковые форматы. Вот функции toString
и что они выводят. Обратите внимание, что функции toUTCString
и toISOString
выводятся на мою машину в 5:00 утра.
Преобразует из UTC в Местное время перед печатью
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Печатает сохраненное время UTC напрямую
- toUTCString
- toISOString
<b>In Chrome</b>
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
<b>In Firefox</b>
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
Обычно я не использую формат ISO для ввода строк. Единственный раз, когда использование этого формата выгодно для меня, это когда даты должны быть отсортированы в виде строк. Формат ISO сортируется как есть, а остальные нет. Если вам нужна совместимость с разными браузерами, укажите часовой пояс или используйте совместимый формат строки.
Код new Date('12/4/2013').toString()
проходит через следующее внутреннее псевдопреобразование:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
Надеюсь, этот ответ был полезен.