Durante uma experiência recente ao escrever um intérprete de JS, lutei bastante com o funcionamento interno das datas da ECMA / JS. Então, acho que vou jogar meus 2 centavos aqui. Esperamos que o compartilhamento dessas coisas ajude outras pessoas com qualquer dúvida sobre as diferenças entre os navegadores na maneira como lidam com as datas.
O lado da entrada
Todas as implementações armazenam seus valores de data internamente como números de 64 bits que representam o número de milissegundos (ms) desde 01-01-2009 UTC (GMT é a mesma coisa que UTC). Essa data é a época do ECMAScript que também é usada por outros idiomas, como sistemas Java e POSIX, como o UNIX. As datas que ocorrem após a época são números positivos e as datas anteriores são negativas.
O código a seguir é interpretado como a mesma data em todos os navegadores atuais, mas com o deslocamento do fuso horário local:
Date.parse('1/1/1970'); // 1 January, 1970
No meu fuso horário (EST, que é -05: 00), o resultado é 18000000 porque é quantos ms há em 5 horas (são apenas 4 horas durante os meses de verão). O valor será diferente em diferentes fusos horários. Esse comportamento é especificado no ECMA-262 para que todos os navegadores façam o mesmo.
Embora exista alguma variação nos formatos de sequência de entrada que os principais navegadores analisarão como datas, eles essencialmente os interpretam da mesma forma no que diz respeito a fusos horários e horário de verão, embora a análise seja amplamente dependente da implementação.
No entanto, o formato ISO 8601 é diferente. É um dos dois únicos formatos descritos no ECMAScript 2015 (ed 6) especificamente que deve ser analisado da mesma maneira por todas as implementações (o outro é o formato especificado para Date.prototype.toString ).
Mas, mesmo para as cadeias de formato ISO 8601, algumas implementações estão erradas. Aqui está uma saída de comparação do Chrome e Firefox quando esta resposta foi originalmente escrita para 1/1/1970 (a época) na minha máquina usando cadeias de formato ISO 8601 que devem ser analisadas exatamente para o mesmo valor em todas as implementações:
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
- No primeiro caso, o especificador "Z" indica que a entrada está no horário UTC, portanto não é deslocada da época e o resultado é 0
- No segundo caso, o especificador "-0500" indica que a entrada está no GMT-05: 00 e os dois navegadores interpretam a entrada como estando no fuso horário -05: 00. Isso significa que o valor UTC é deslocado da época, o que significa adicionar 18000000ms ao valor da hora interna da data.
- O terceiro caso, onde não há especificador, deve ser tratado como local para o sistema host. O FF trata corretamente a entrada como hora local, enquanto o Chrome a trata como UTC, produzindo valores de hora diferentes. Para mim, isso cria uma diferença de 5 horas no valor armazenado, o que é problemático. Outros sistemas com diferentes compensações obterão resultados diferentes.
Essa diferença foi corrigida a partir de 2020, mas existem outras peculiaridades entre os navegadores ao analisar as cadeias de formato ISO 8601.
Mas piora. Uma peculiaridade do ECMA-262 é que o formato somente data ISO 8601 (AAAA-MM-DD) deve ser analisado como UTC, enquanto o ISO 8601 exige que ele seja analisado como local. Aqui está a saída do FF com os formatos de data ISO longo e curto sem especificador de fuso horário.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Portanto, o primeiro é analisado como local porque é a data e hora da ISO 8601 sem fuso horário e o segundo é analisado como UTC porque é apenas a data da ISO 8601.
Portanto, para responder diretamente à pergunta original, o "YYYY-MM-DD"
ECMA-262 exige que seja interpretado como UTC, enquanto o outro é interpretado como local. É por isso:
Isso não produz resultados equivalentes:
console.log(new Date(Date.parse("Jul 8, 2005")).toString()); // Local
console.log(new Date(Date.parse("2005-07-08")).toString()); // UTC
Isto faz:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
A linha inferior é esta para analisar seqüências de datas. A ÚNICA string ISO 8601 que você pode analisar com segurança nos navegadores é o formato longo com um deslocamento (± HH: mm ou "Z"). Se você fizer isso, poderá ir e voltar com segurança entre o horário local e o UTC.
Isso funciona nos navegadores (após o IE9):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
A maioria dos navegadores atuais trata os outros formatos de entrada igualmente, incluindo os usados com frequência '1/1/1970' (M / D / AAAA) e '1/1/1970 00:00:00 AM' (M / D / AAAA hh : mm: ss ap) formatos. Todos os seguintes formatos (exceto o último) são tratados como entrada da hora local em todos os navegadores. A saída desse código é a mesma em todos os navegadores no meu fuso horário. O último é tratado como -05: 00, independentemente do fuso horário do host, porque o deslocamento é definido no registro de data e hora:
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"));
No entanto, como a análise até dos formatos especificados no ECMA-262 não é consistente, é recomendável nunca confiar no analisador interno e sempre analisar manualmente as seqüências de caracteres, digamos, usando uma biblioteca e fornecendo o formato ao analisador.
Por exemplo, em moment.js você pode escrever:
let m = moment('1/1/1970', 'M/D/YYYY');
O lado da saída
No lado da saída, todos os navegadores convertem os fusos horários da mesma maneira, mas manipulam os formatos de sequência de maneira diferente. Aqui estão as toString
funções e o que elas produzem. Observe que as funções toUTCString
e toISOString
saem às 5:00 da manhã na minha máquina. Além disso, o nome do fuso horário pode ser uma abreviação e pode ser diferente em diferentes implementações.
Converte do UTC para o horário local antes de imprimir
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Imprime diretamente a hora UTC armazenada
- toUTCString
- toISOString
No 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
No 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
Normalmente não uso o formato ISO para entrada de string. A única vez que usar esse formato é benéfico para mim é quando as datas precisam ser classificadas como cadeias. O formato ISO pode ser classificado como está, enquanto os outros não. Se você precisar ter compatibilidade entre navegadores, especifique o fuso horário ou use um formato de sequência compatível.
O código new Date('12/4/2013').toString()
passa pela seguinte pseudo-transformação interna:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
Eu espero que a resposta tenha ajudado.