Por que “2016-02-16” não é igual a “2016-02-16 00:00”?

96

Estou tentando passar as duas strings de data para new Date(t).

Espero que ambas as strings representem a mesma hora, afinal, se omitir a hora, não deveria ser meia-noite desse dia?

Mas enquanto,

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

retorna 2016-02-16, meia-noite, hora local conforme esperado,

new Date("2016-02-16")

retorna 2016-02-16, meia-noite UTC, o que está errado, ou pelo menos não o que eu esperava, dado como a outra string analisa.

Eu entenderia se ambos tivessem o mesmo comportamento, seja para retornar a hora como hora local ou como UTC, mas parece muito inconsistente porque eles retornam coisas diferentes como esta.

Como solução alternativa, sempre que encontro uma data que não tem um carimbo de data / hora correspondente, posso acrescentar "00:00" para obter um comportamento consistente, mas parece que isso é bastante frágil.

Estou obtendo esse valor de um elemento INPUT, do tipo 'datetime-local', portanto, parece especialmente inconsistente que tenho que solucionar um valor retornado por um elemento de página.

Estou fazendo algo errado ou deveria fazer algo diferente?

Michael
fonte
2
2016-02-16 00:00- este não parece ser o tempo válido. ecma-international.org/ecma-262/6.0/… , mas mesmo depois de colocá T-lo lá, ele realmente se comporta de maneira diferente
zerkms
Como exatamente você está obtendo atualmente o objeto Date do elemento de entrada com base em seu valor?
BoltClock
4
De acordo com o padrão - "Se os campos HH, mm ou ss estiverem ausentes" 00 "é usado como o valor e o valor de um campo sss ausente é" 000 ". Se o deslocamento de fuso horário estiver ausente, a data-hora é interpretado como a hora local. " --- deve se comportar da mesma forma.
zerkms de
@BoltClock Hmm, parece que ele está pegando o campo de valor do elemento e (como zerkms notou) removendo o T por algum motivo (eu acho que porque o valor está sendo exibido para o usuário em um contexto onde "T" seria confuso)
Michael
1
@Michael: Fantástico. Maneiras de navegador como essa são minhas favoritas. (Ou poderia ser uma peculiaridade da especificação DOM? Não faço ideia, eu realmente não olhei.)
BoltClock

Respostas:

100

É o que a especificação ES5.1 diz para fazer:

O valor de um deslocamento de fuso horário ausente é “Z”.

Também diz:

A função primeiro tenta analisar o formato da String de acordo com as regras apresentadas em Date Time String Format (15.9.1.15). Se a String não estiver em conformidade com esse formato, a função pode recorrer a qualquer heurística específica de implementação ou formatos de data específicos de implementação.

Como o formato requer um Tseparador entre a data e a hora, os horários válidos vão para 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)

... enquanto em node.js, uma hora inválida (sem o separador T) parece ir para a hora local específica da implementação:

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

Observe que o ES6 mudou isso, na mesma parte da documentação ele muda para:

Se a diferença de fuso horário estiver ausente, a data e hora será interpretada como a hora local.

A alegria de quebrar mudanças .

Editar

De acordo com TC39 , a especificação deve ser interpretada como strings de data e hora sem fuso horário (por exemplo, "2016-02-16T00: 00: 00") são tratadas como locais (de acordo com ISO 8601), mas apenas strings de data (por exemplo "2016-02-16") como UTC (que é inconsistente com ISO 8601).

Joachim Isaksson
fonte
20
A alegria de quebrar mudanças, de fato. Em um novo rascunho do padrão ES7, parte da mudança de UTC para a hora local foi revertida. A nova norma diz que as datas devem ser interpretadas como UTC se nenhum fuso horário for especificado, enquanto as datas e horas devem ser interpretadas como uma hora local se não houver fuso horário é especificado.
hichris123 de
3
Eles realmente deveriam estar no horário local, independentemente dos formatos de data e hora. É assim que funciona o ISO8601. É ridículo que a forma apenas de data seja interpretada como meia-noite UTC, já que uma data UTC atemporal nem mesmo faz sentido conceitualmente. Houve um acalorado debate sobre isso na ECMA tc39. Lutei pelo horário local e perdi. github.com/tc39/ecma262/issues/87
Matt Johnson-Pint
10

De acordo com as especificações :

A função primeiro tenta analisar o formato da String de acordo com as regras apresentadas em Date Time String Format (15.9.1.15). Se a String não estiver em conformidade com esse formato, a função pode recorrer a qualquer heurística específica de implementação ou formatos de data específicos de implementação.

E os formatos de string de data e hora aceitam 2016-02-16como uma data válida

Este formato inclui formulários apenas para datas:

AAAA
AAAA-MM
AAAA-MM-DD

[...] Se os campos HH, mm ou ss estiverem ausentes, “00” é usado como o valor e o valor de um campo sss ausente é “000”. O valor de um deslocamento de fuso horário ausente é “Z”.

Assim se 2016-02-16traduz em 2016-02-16T00:00:00.000Z.

A outra data 2016-02-16 00:00não está em conformidade com o formato e, portanto, sua análise é específica da implementação. Aparentemente, essas datas são tratadas como tendo fuso horário local e sua data de exemplo retornará valores diferentes dependendo do fuso horário:

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

Resumo:

  • Para formatos de data e hora em conformidade, o comportamento é bem definido - na ausência de deslocamento de fuso horário, a string de data é tratada como UTC (ES5) ou local (ES6).
  • Para formatos de data e hora não conformes, o comportamento é específico da implementação - na ausência de deslocamento de fuso horário, o comportamento usual é tratar a data como local.
  • Na verdade, a implementação poderia escolher retornar em NaNvez de tentar analisar datas não conformes. Basta testar seu código no Internet Explorer 11;)
Salman A
fonte
7

Talvez você esteja encontrando diferenças entre as implementações ES5, ES6 e o ​​resultado esperado. Por Date.parse at MDN, "especialmente em diferentes implementações ECMAScript onde strings como" 2015-10-12 12:00:00 "podem ser analisadas como NaN, UTC ou fuso horário local" é significativo.

Testes adicionais no Firefox 44 e no IE 11 revelaram que ambos retornam um objeto de data para new Date("2016-02-16 00:00"), cujo objeto retorna NaN ao tentar obter um valor de componente de data e cujo valor toString é "Data inválida" (não "NaN"). Portanto, acrescentar "00:00 para obter um comportamento consistente" pode facilmente quebrar em diferentes navegadores.

Conforme observado em outras respostas, new Date("2016-02-16")usa um deslocamento de fuso horário de zero por padrão, produzindo UTC da meia-noite em vez de local.

traktor53
fonte
OP parece obter resultados diferentes na mesma implementação.
Salman A
6

De acordo DateParser::Parse()com os códigos-fonte V8 do Chrome.

Datas ES5 ISO 8601:

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

Um número sem sinal seguido por ':' é um valor de tempo e é adicionado ao TimeComposer.

o fuso horário padrão é Z se ausente

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

Uma string que corresponda a ambos os formatos (por exemplo 1970-01-01) será analisada como uma string de data e hora ES5 - o que significa que o padrão será UTC time-zone. Isso é inevitável se seguir a especificação ES5.

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

retorna 2016-02-16, meia-noite UTC, o que está errado, ou pelo menos não o que eu esperava, dado como a outra string analisa.

Ele adiciona a diferença de fuso horário ao 00:00

new Date("2016-02-16") saídas Tue Feb 16 2016 05:30:00 GMT+0530 (India Standard Time)

Meu fuso horário sendo IST com um valor de deslocamento (em minutos) +330, então ele adicionou 330 minutos a 00:00.

Conforme ecma-262, seção 20.3.3.2 Date.parse (string)

Se ToString resultar em uma conclusão abrupta, o Registro de Conclusão será retornado imediatamente. Caso contrário, parse interpreta a String resultante como uma data e hora; ele retorna um número, o valor de hora UTC correspondente à data e hora. A String pode ser interpretada como uma hora local, uma hora UTC ou uma hora em algum outro fuso horário, dependendo do conteúdo da String.

Quando você definir explicitamente as unidades de tempo, new Date("2016-02-16 00:00")ele usará definir isso como hourse minutes,

Caso contrário, conforme indicado aqui em 2 0.3.1.16

Se a diferença de fuso horário estiver ausente, a data e hora será interpretada como a hora local.

gurvinder372
fonte
Sim, mas por que isso ocorre em um caso, mas não no outro?
Michael em
@Michael sim, verifique ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Então, por que esses resultados são diferentes? O padrão afirma que deve ser o mesmo
zerkms de
@zerkms O padrão diz o que fará quando você passar as unidades de tempo, não diz o que fará quando você não o fizer. Diz claramente 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.Onde está afirmando que deve ser o mesmo?
gurvinder372
@ gurvinder372 Eu forneci a citação nos comentários da pergunta: "Se os campos HH, mm ou ss estiverem ausentes," 00 "será usado como o valor e o valor de um campo sss ausente será" 000 ". Se o fuso horário for deslocado está ausente, a data-hora é interpretada como a hora local. "
zerkms de