Durante la experiencia reciente escribiendo un intérprete de JS luché mucho con el funcionamiento interno de las fechas de ECMA / JS. Entonces, supongo que arrojaré mis 2 centavos aquí. Espero que compartir estas cosas ayude a otros con cualquier pregunta sobre las diferencias entre los navegadores en cómo manejan las fechas.
El lado de entrada
Todas las implementaciones almacenan sus valores de fecha internamente como números de 64 bits que representan la cantidad de milisegundos (ms) desde 1970-01-01 UTC (GMT es lo mismo que UTC). Esta fecha es la época de ECMAScript que también utilizan otros lenguajes como los sistemas Java y POSIX como UNIX. Las fechas que ocurren después de la época son números positivos y las fechas anteriores son negativas.
El siguiente código se interpreta como la misma fecha en todos los navegadores actuales, pero con el desplazamiento de la zona horaria local:
Date.parse('1/1/1970'); // 1 January, 1970
En mi zona horaria (EST, que es -05: 00), el resultado es 18000000 porque esa es la cantidad de ms en 5 horas (son solo 4 horas durante los meses de verano). El valor será diferente en diferentes zonas horarias. Este comportamiento se especifica en ECMA-262, por lo que todos los navegadores lo hacen de la misma manera.
Si bien existe una cierta variación en los formatos de cadena de entrada que los principales navegadores analizarán como fechas, esencialmente los interpretan de la misma manera en lo que respecta a las zonas horarias y al horario de verano, aunque el análisis depende en gran medida de la implementación.
Sin embargo, el formato ISO 8601 es diferente. Es uno de los dos únicos formatos descritos en ECMAScript 2015 (ed. 6) específicamente que todas las implementaciones deben analizar de la misma manera (el otro es el formato especificado para Date.prototype.toString ).
Pero, incluso para las cadenas de formato ISO 8601, algunas implementaciones se equivocan. Aquí hay una salida de comparación de Chrome y Firefox cuando esta respuesta se escribió originalmente para el 1/1/1970 (la época) en mi máquina usando cadenas de formato ISO 8601 que deberían analizarse exactamente con el mismo valor en todas las implementaciones:
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
- En el primer caso, el especificador "Z" indica que la entrada está en tiempo UTC, por lo que no está desplazada de la época y el resultado es 0
- En el segundo caso, el especificador "-0500" indica que la entrada está en GMT-05: 00 y ambos navegadores interpretan que la entrada está en la zona horaria -05: 00. Eso significa que el valor UTC se compensa de la época, lo que significa agregar 18000000ms al valor de hora interna de la fecha.
- El tercer caso, donde no hay especificador, debe tratarse como local para el sistema host. FF trata correctamente la entrada como hora local, mientras que Chrome la trata como UTC, por lo que produce diferentes valores de hora. Para mí, esto crea una diferencia de 5 horas en el valor almacenado, lo cual es problemático. Otros sistemas con diferentes compensaciones obtendrán diferentes resultados.
Esta diferencia se ha solucionado a partir de 2020, pero existen otras peculiaridades entre los navegadores al analizar cadenas de formato ISO 8601.
Pero se pone peor. Una peculiaridad de ECMA-262 es que se requiere que el formato de fecha ISO 8601 (AAAA-MM-DD) se analice como UTC, mientras que ISO 8601 requiere que se analice como local. Aquí está la salida de FF con los formatos de fecha ISO largos y cortos sin especificador de zona horaria.
Date.parse('1970-01-01T00:00:00'); // 18000000
Date.parse('1970-01-01'); // 0
Entonces, el primero se analiza como local porque es la fecha y hora ISO 8601 sin zona horaria, y el segundo se analiza como UTC porque es solo la fecha ISO 8601.
Entonces, para responder la pregunta original directamente, "YYYY-MM-DD"
ECMA-262 requiere que se interprete como UTC, mientras que el otro se interpreta como local. Es por eso:
Esto no produce resultados equivalentes:
console.log(new Date(Date.parse("Jul 8, 2005")).toString()); // Local
console.log(new Date(Date.parse("2005-07-08")).toString()); // UTC
Esto hace:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
La conclusión es esto para analizar cadenas de fechas. La ÚNICA cadena ISO 8601 que puede analizar de forma segura en los navegadores es la forma larga con un desplazamiento (ya sea ± HH: mm o "Z"). Si lo hace, puede ir y venir con seguridad entre la hora local y la hora UTC.
Esto funciona en todos los navegadores (después de IE9):
console.log(new Date(Date.parse("2005-07-08T00:00:00Z")).toString());
La mayoría de los navegadores actuales tratan los otros formatos de entrada por igual, incluidos los '1/1/1970' (M / D / AAAA) y '1/1/1970 00:00:00 AM' (M / D / AAAA hh) utilizados con frecuencia. : mm: ss ap) formatos. Todos los siguientes formatos (excepto el último) se tratan como entrada de hora local en todos los navegadores. La salida de este código es la misma en todos los navegadores en mi zona horaria. El último se trata como -05: 00 independientemente de la zona horaria del host porque el desplazamiento se establece en la marca de tiempo:
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"));
Sin embargo, dado que el análisis de incluso los formatos especificados en ECMA-262 no es coherente, se recomienda no confiar nunca en el analizador incorporado y analizar siempre las cadenas manualmente, por ejemplo, utilizando una biblioteca y proporcionar el formato al analizador.
Por ejemplo, en moment.js podrías escribir:
let m = moment('1/1/1970', 'M/D/YYYY');
El lado de salida
En el lado de salida, todos los navegadores traducen las zonas horarias de la misma manera, pero manejan los formatos de cadena de manera diferente. Aquí están las toString
funciones y lo que generan. Observe la salida toUTCString
y las toISOString
funciones a las 5:00 AM en mi máquina. Además, el nombre de la zona horaria puede ser una abreviatura y puede ser diferente en diferentes implementaciones.
Convierte de UTC a hora local antes de imprimir
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Imprime la hora UTC almacenada directamente
- toUTCString
- toISOString
En cromo
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
En 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 no uso el formato ISO para la entrada de cadenas. El único momento en que usar ese formato es beneficioso para mí es cuando las fechas deben clasificarse como cadenas. El formato ISO se puede ordenar tal cual, mientras que los demás no. Si tiene que tener compatibilidad entre navegadores, especifique la zona horaria o use un formato de cadena compatible.
El código new Date('12/4/2013').toString()
pasa por la siguiente pseudo-transformación interna:
"12/4/2013" -> toUCT -> [storage] -> toLocal -> print "12/4/2013"
Espero que esta respuesta haya sido útil.