Почему «2016-02-16» не равно «2016-02-16 00:00»?

96

Я пытаюсь передать обе строки даты new Date(t).

Я ожидаю, что обе строки представляют одно и то же время, в конце концов, если я опущу время, разве это не будет полночь того дня?

Но пока,

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

возвращает 16 февраля 2016 г., полночь по местному времени, как и ожидалось,

new Date("2016-02-16")

возвращает 2016-02-16, полночь по Гринвичу, что неверно, или, по крайней мере, не то, что я ожидал, учитывая то, что анализирует другая строка.

Я бы понял это, если бы у них обоих было одинаковое поведение, будь то возвращение времени как местного времени или как UTC, но кажется очень непоследовательным, почему они возвращают разные вещи, подобные этому.

В качестве обходного пути всякий раз, когда я сталкиваюсь с датой, у которой нет соответствующей отметки времени, я могу добавить «00:00», чтобы добиться согласованного поведения, но это кажется довольно хрупким.

Я получаю это значение из элемента INPUT типа «datetime-local», поэтому мне кажется особенно непоследовательным, что мне приходится работать с значением, возвращаемым элементом страницы.

Я что-то делаю не так, или я должен делать что-то по-другому?

Майкл
источник
2
2016-02-16 00:00- это совсем не похоже на действительное время. ecma-international.org/ecma-262/6.0/… , но даже после того, как вы поместили Tтуда, он действительно ведет себя по-другому
zerkms
Как именно вы в настоящее время получаете объект Date из элемента ввода на основе его значения?
BoltClock
4
Согласно стандарту - «Если поля HH, mm или ss отсутствуют, в качестве значения используется« 00 », а значение отсутствующего поля sss равно« 000 ». Если смещение часового пояса отсутствует, дата-время интерпретируется как местное время ". --- он должен вести себя так же.
zerkms
@BoltClock Хм, похоже, он берет поле значения элемента и (как заметил zerkms) по какой-то причине удаляет T (я думаю, потому что значение отображается для пользователя в контексте, где "T" сбивает с толку)
Майкл
1
@ Майкл: Потрясающе. Подобные особенности браузера - мои любимые. (Или это может быть причуда спецификации DOM? Понятия не имею, я толком не смотрел.)
BoltClock

Ответы:

100

Вот что говорит спецификация ES5.1 :

Значение смещения отсутствующего часового пояса - «Z».

В нем также говорится:

Функция сначала пытается проанализировать формат строки в соответствии с правилами, установленными в формате строки даты и времени (15.9.1.15). Если String не соответствует этому формату, функция может вернуться к любым эвристикам, зависящим от реализации, или форматам даты, зависящим от реализации.

Поскольку формат требует Tразделителя между датой и временем, допустимое время указывается в формате 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)

... в то время как в node.js недопустимое время (без разделителя T), похоже, относится к конкретному местному времени реализации:

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

Обратите внимание, что ES6 изменил это, в той же части документации он меняется на:

Если смещение часового пояса отсутствует, дата-время интерпретируется как местное время.

Радость ломать перемены .

редактировать

Согласно TC39 , спецификация должна интерпретироваться как строки даты и времени без часового пояса (например, «2016-02-16T00: 00: 00») обрабатываются как локальные (в соответствии с ISO 8601), но строки только даты (например, «2016-02-16») как UTC (что несовместимо с ISO 8601).

Иоахим Исакссон
источник
20
Действительно, радость ломать изменения. В новом проекте стандарта ES7 часть изменения с UTC на местное время была возвращена. В новом стандарте говорится, что даты должны интерпретироваться как UTC, если часовой пояс не указан, а разовые даты должны интерпретироваться как местное время, если часовой пояс отсутствует. указан.
hichris123
3
Оба они действительно должны указывать местное время, независимо от формы только даты или даты и времени. Вот как работает ISO8601. Нелепо, что форма только для даты интерпретируется как полночь по всемирному координированному времени, поскольку вневременная дата в формате UTC не имеет смысла даже концептуально. На ECMA tc39 по этому поводу были жаркие споры. Я боролся за местное время и проиграл. github.com/tc39/ecma262/issues/87
Мэтт Джонсон-Пинт,
10

Согласно спецификации :

Функция сначала пытается проанализировать формат строки в соответствии с правилами, установленными в формате строки даты и времени (15.9.1.15). Если String не соответствует этому формату, функция может вернуться к любым эвристикам, зависящим от реализации, или форматам даты, зависящим от реализации.

И форматы строки даты и времени принимают 2016-02-16как действительную дату

Этот формат включает формы только для даты:

ГГГГ
ГГГГ-ММ
ГГГГ-ММ-ДД

[...] Если поля HH, mm или ss отсутствуют, в качестве значения используется «00», а значение отсутствующего поля sss равно «000». Значение смещения отсутствующего часового пояса - «Z».

Таким образом 2016-02-16переводится на 2016-02-16T00:00:00.000Z.

Другая дата 2016-02-16 00:00не соответствует формату, и поэтому ее анализ зависит от реализации. По-видимому, такие даты рассматриваются как имеющие местный часовой пояс, и ваша примерная дата будет возвращать разные значения в зависимости от часового пояса:

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

Резюме:

  • Для соответствующих форматов даты и времени поведение четко определено - в отсутствие смещения часового пояса строка даты обрабатывается как UTC (ES5) или как локальная (ES6).
  • Для несоответствующих форматов даты и времени поведение зависит от реализации - при отсутствии смещения часового пояса обычным поведением является обработка даты как локальной.
  • Фактически, реализация могла бы выбрать возврат NaNвместо попытки синтаксического анализа несоответствующих дат. Просто проверьте свой код в Internet Explorer 11;)
Салман А
источник
7

Возможно, вы столкнулись с различиями между реализациями ES5 и ES6 и ожидаемым результатом. Per Date.parse в MDN, «особенно в различных реализациях ECMAScript, где строки вроде« 2015-10-12 12:00:00 »могут быть проанализированы как NaN, UTC или местный часовой пояс».

Дополнительное тестирование в Firefox 44 и IE 11 показало, что оба они возвращают объект даты, для new Date("2016-02-16 00:00")которого объект возвращает NaN при попытке получить значение компонента даты, а значение toString которого равно «Invalid Date» (не «NaN»). Следовательно, добавление «00:00 для согласованного поведения» может легко нарушить работу разных браузеров.

Как отмечалось в других ответах, new Date("2016-02-16")по умолчанию используется смещение часового пояса, равное нулю, что создает полночь по Гринвичу вместо местного.

трактор53
источник
OP, похоже, получает разные результаты в одной и той же реализации.
Salman A
6

Согласно DateParser::Parse()исходным кодам V8 для Chrome.

Даты ES5 ISO 8601:

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

Число без знака, за которым следует ':', является значением времени и добавляется в TimeComposer.

часовой пояс по умолчанию Z, если отсутствует

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

Строка, которая соответствует обоим форматам (например, 1970-01-01), будет проанализирована как строка даты и времени ES5 - это означает, что по умолчанию будет UTC time-zone. Это неизбежно, если следовать спецификации ES5.

> new Date("2016-02-16")
Tue Feb 16 2016 08:00:00 GMT+0800 (China Standard Time)
Zangw
источник
3

возвращает 2016-02-16, полночь по Гринвичу, что неверно, или, по крайней мере, не то, что я ожидал, учитывая то, что анализирует другая строка.

Он добавляет смещение часового пояса к 00:00

new Date("2016-02-16") выходы Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

Мой часовой пояс IST со значением смещения (в минутах) +330, поэтому он добавил 330 минут к 00:00.

Согласно ecma-262, раздел 20.3.3.2 Date.parse (строка)

Если ToString приводит к внезапному завершению, немедленно возвращается запись о завершении. В противном случае синтаксический анализ интерпретирует результирующую строку как дату и время; он возвращает число, значение времени UTC, соответствующее дате и времени. Строка может интерпретироваться как местное время, время в формате UTC или время в каком-либо другом часовом поясе, в зависимости от содержимого строки.

Когда вы явно устанавливаете единицы времени, new Date("2016-02-16 00:00")он будет использовать их как hoursи minutes,

В противном случае, как указано здесь в 2 0.3.1.16

Если смещение часового пояса отсутствует, дата-время интерпретируется как местное время.

гурвиндер372
источник
Да, но почему в одном случае это происходит, а в другом нет?
Майкл
@Michael: да, проверьте ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Так почему это разные результаты? Стандарт утверждает, что это должно быть то же самое
zerkms
@zerkms Standard говорит, что он будет делать, когда вы пройдете единицы времени, но не говорит, что он будет делать, когда вы этого не сделаете. В нем ясно сказано, 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.где оно утверждает, что оно должно быть таким же?
gurvinder372
@ gurvinder372 Я привел цитату в комментариях к вопросу: «Если поля HH, mm или ss отсутствуют, в качестве значения используется« 00 », а значение отсутствующего поля sss равно« 000 ». Если смещение часового пояса отсутствует, дата-время интерпретируется как местное время ".
zerkms