javascript - react - ¿Por qué Date.parse da resultados incorrectos?
moment react (11)
Caso uno:
new Date(Date.parse("Jul 8, 2005"));
Salida:
Viernes 08 de julio de 2005 00:00:00 GMT-0700 (PST)
Caso Dos:
new Date(Date.parse("2005-07-08"));
Salida:
Jue 07 de julio 2005 17:00:00 GMT-0700 (PST)
¿Por qué el segundo análisis es incorrecto?
Ambos son correctos, pero se interpretan como fechas con dos zonas horarias diferentes. Así que comparaste manzanas y naranjas:
// local dates
new Date("Jul 8, 2005").toISOString() // "2005-07-08T07:00:00.000Z"
new Date("2005-07-08T00:00-07:00").toISOString() // "2005-07-08T07:00:00.000Z"
// UTC dates
new Date("Jul 8, 2005 UTC").toISOString() // "2005-07-08T00:00:00.000Z"
new Date("2005-07-08").toISOString() // "2005-07-08T00:00:00.000Z"
Date.parse()
llamada Date.parse()
ya que se usa automáticamente en un argumento de cadena. También comparé las fechas utilizando el formato ISO8601 para que pueda comparar visualmente las fechas entre sus fechas locales y las fechas UTC. Los tiempos son de 7 horas de diferencia, que es la diferencia de zona horaria y por qué las pruebas mostraron dos fechas diferentes.
La otra forma de crear estas mismas fechas locales / UTC sería:
new Date(2005, 7-1, 8) // "2005-07-08T07:00:00.000Z"
new Date(Date.UTC(2005, 7-1, 8)) // "2005-07-08T00:00:00.000Z"
Pero todavía recomiendo encarecidamente Moment.js que es tan simple como poderoso :
// parse string
moment("2005-07-08").format() // "2005-07-08T00:00:00+02:00"
moment.utc("2005-07-08").format() // "2005-07-08T00:00:00Z"
// year, month, day, etc.
moment([2005, 7-1, 8]).format() // "2005-07-08T00:00:00+02:00"
moment.utc([2005, 7-1, 8]).format() // "2005-07-08T00:00:00Z"
Aquí hay un fragmento corto y flexible para convertir una cadena de fecha y hora de manera segura para todos los navegadores, como lo detalla nicel en @drankin2112.
var inputTimestamp = "2014-04-29 13:00:15"; //example
var partsTimestamp = inputTimestamp.split(/[ //:-]/g);
if(partsTimestamp.length < 6) {
partsTimestamp = partsTimestamp.concat([''00'', ''00'', ''00''].slice(0, 6 - partsTimestamp.length));
}
//if your string-format is something like ''7/02/2014''...
//use: var tstring = partsTimestamp.slice(0, 3).reverse().join(''-'');
var tstring = partsTimestamp.slice(0, 3).join(''-'');
tstring += ''T'' + partsTimestamp.slice(3).join('':'') + ''Z''; //configure as needed
var timestamp = Date.parse(tstring);
Su navegador debe proporcionar el mismo resultado de fecha y hora que Date.parse
con:
(new Date(tstring)).getTime()
Durante la experiencia reciente escribiendo un intérprete de JS, luché mucho con el funcionamiento interno de las fechas de ECMA / JS. Entonces, me imagino que voy a tirar mis 2 centavos aquí. Esperamos que compartir esto ayude a otros con cualquier pregunta sobre las diferencias entre los navegadores en la forma en que manejan las fechas.
El lado de entrada
Todas las implementaciones almacenan sus valores de fecha internamente como números de 64 bits que representan el número de milisegundos desde el 1/1/1970 UTC (GMT es lo mismo que UTC). Las fechas que tienen lugar después del 1/1/1970 00:00:00
son números positivos y las fechas anteriores son negativas.
Por lo tanto, el siguiente código produce exactamente el mismo resultado en todos los navegadores.
Date.parse(''1/1/1970'');
En mi zona horaria (EST), el resultado es 18000000 porque esa es la cantidad de ms en 5 horas (solo 4 horas durante los meses de ahorro de luz). El valor será diferente en diferentes zonas horarias. Todos los principales navegadores lo hacen de la misma manera.
Aquí está el problema. Si bien existe una cierta variación en los formatos de las cadenas 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 el horario de verano. El que se mantiene es el formato ISO 8601. Es el único formato descrito específicamente en la especificación ECMA-262 v.5. Para todos los demás formatos de cadena, la interpretación depende de la implementación. Irónicamente, este es el formato donde los navegadores pueden diferir. Aquí hay una salida de comparación de Chrome vs Firefox para 1/1/1970 en mi máquina usando el formato de cadena 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
- El especificador "Z" indica que la entrada ya está en tiempo UTC y no requiere compensación antes del almacenamiento.
- El especificador "-0500" indica que la entrada está en GMT-05: 00, por lo que ambos navegadores interpretan que la entrada está en mi zona horaria local. Eso significa que el valor se convierte a UTC antes de ser almacenado. En mi caso, significa agregar 18000000ms al valor interno de la fecha, por lo que se requiere un cambio de -18000000ms (-05: 00) para volver a ubicarme en la hora local.
- Sin embargo, cuando no hay ningún especificador, FF trata la entrada como hora local, mientras que Chrome la trata como hora UTC. Para mí esto crea una diferencia de 5 horas en el valor almacenado, lo cual es problemático. En mi implementación terminé poniéndome del lado de FF aquí porque me gusta que la salida de
toString
coincida con mi valor de entrada a menos que especifique una zona horaria alternativa, lo que nunca hago. La ausencia de un especificador debe suponer la entrada de la hora local.
Pero aquí es donde empeora, FF trata la forma abreviada del formato ISO 8601 ("YYYY-MM-DD") de manera diferente a la forma larga ("YYYY-MM-DDTHH: mm: ss: sssZ") para No hay razón lógica alguna. 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, para responder directamente a la pregunta del autor de la pregunta, "YYYY-MM-DD"
es la forma abreviada del formato ISO 8601 "YYYY-MM-DDTHH:mm:ss:sssZ"
. Por lo tanto, se interpreta como hora UTC mientras que el otro se interpreta como local. Es por eso,
Esto no encaja:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08")).toString());
Esto hace:
console.log(new Date(Date.parse("Jul 8, 2005")).toString());
console.log(new Date(Date.parse("2005-07-08T00:00:00")).toString());
El resultado final es esto para analizar las cadenas de fecha. La ÚNICA cadena ISO 8601 que puede analizar de forma segura en todos los navegadores es la forma larga. Y, SIEMPRE use el especificador "Z". Si lo hace, puede ir y volver 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());
Afortunadamente, la mayoría de los navegadores actuales tratan los otros formatos de entrada por igual, incluidos los formatos ''1/1/1970'' y ''1/1/1970 00:00:00 AM más utilizados''. Todos los formatos siguientes (y otros) se tratan como entrada de hora local en todos los navegadores y se convierten a UTC antes del almacenamiento. Por lo tanto, haciéndolos compatibles con varios navegadores. La salida de este código es la misma en todos los navegadores en mi zona horaria.
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"));
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 funciones de toString
y lo que producen. Observe que las funciones toUTCString
y toISOString
salida a las 5:00 AM en mi máquina.
Se convierte de UTC a hora local antes de imprimir
- toString
- toDateString
- toTimeString
- toLocaleString
- toLocaleDateString
- toLocaleTimeString
Imprime la hora UTC almacenada directamente
- toUTCString
- toISOString
In 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
In 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. La única vez que usar ese formato es beneficioso para mí es cuando las fechas deben ordenarse como cadenas. El formato ISO se puede ordenar como está, mientras que los otros no. Si tiene que tener compatibilidad con varios 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.
Esta biblioteca de análisis de fecha ligera debería resolver todos los problemas similares. Me gusta la biblioteca porque es bastante fácil de ampliar. También es posible hacerlo (no muy sencillo, pero no tan difícil).
Ejemplo de análisis:
var caseOne = Date.parseDate("Jul 8, 2005", "M d, Y");
var caseTwo = Date.parseDate("2005-07-08", "Y-m-d");
Y formatear de nuevo a cadena (notará que ambos casos dan exactamente el mismo resultado):
console.log( caseOne.dateFormat("M d, Y") );
console.log( caseTwo.dateFormat("M d, Y") );
console.log( caseOne.dateFormat("Y-m-d") );
console.log( caseTwo.dateFormat("Y-m-d") );
Hasta que salió la especificación de la quinta edición, el método Date.parse
era completamente dependiente de la implementación (la new Date(string)
es equivalente a la Date.parse excepto que esta última devuelve un número en lugar de una Date
). En la especificación de la quinta edición, se agregó el requisito para admitir un ISO-8601 simplificado (y ligeramente incorrecto) , pero aparte de eso, no hubo ningún requisito para lo que Date.parse
/ new Date(string)
debe aceptar, aparte del que tenía que acepte cualquier fecha de salida # toString (sin decir lo que fue).
A partir de ECMAScript 2017 (edición 8), se requirieron implementaciones para analizar su salida para Date # toString y Date # toUTCString , pero no se especificó el formato de esas cadenas.
A partir de ECMAScript 2019 (edición 9), el formato para Date#toString y Date#toUTCString , se ha especificado como (respectivamente):
- ddd MMM DD YYYY HH: mm: ss ZZ [(nombre de la zona horaria)]
por ejemplo, mar 10 de julio de 2018 18:39:58 GMT + 0530 (IST) - ddd, DD MMM YYYY HH: mm: ss Z
Ej. Mar 10 Jul 2018 13:09:58 GMT
proporcionando 2 formatos más que Date.parse debería analizar de forma confiable en nuevas implementaciones (teniendo en cuenta que el soporte no es ubicuo y las implementaciones que no cumplen con las normas seguirán en uso durante algún tiempo).
Recomendaría que las cadenas de fecha se analicen manualmente y que el constructor Fecha se use con los argumentos de año, mes y día para evitar la ambigüedad:
// parse a date in yyyy-mm-dd format
function parseDate(input) {
var parts = input.split(''-'');
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2]); // Note: months are 0-based
}
Hay algún método para la locura. Como regla general, si un navegador puede interpretar una fecha como un ISO-8601, lo hará. "2005-07-08" cae en este campo, por lo que se analiza como UTC. "8 de julio de 2005" no se puede, por lo que se analiza en la hora local.
Ver JavaScript y fechas, ¡qué desastre! para más.
La respuesta aceptada de CMS es correcta, acabo de agregar algunas características:
- recortar y limpiar espacios de entrada
- Analizar barras, guiones, dos puntos y espacios.
- tiene día y hora predeterminados
// parse a date time that can contains spaces, dashes, slashes, colons
function parseDate(input) {
// trimes and remove multiple spaces and split by expected characters
var parts = input.trim().replace(/ +(?= )/g,'''').split(/[/s-//:]/)
// new Date(year, month [, day [, hours[, minutes[, seconds[, ms]]]]])
return new Date(parts[0], parts[1]-1, parts[2] || 1, parts[3] || 0, parts[4] || 0, parts[5] || 0); // Note: months are 0-based
}
Otra solución es crear una matriz asociativa con formato de fecha y luego reformatear los datos.
Este método es útil para la fecha formateada de una manera poco usual.
Un ejemplo:
mydate=''01.02.12 10:20:43'':
myformat=''dd/mm/yy HH:MM:ss'';
dtsplit=mydate.split(/[// .:]/);
dfsplit=myformat.split(/[// .:]/);
// creates assoc array for date
df = new Array();
for(dc=0;dc<6;dc++) {
df[dfsplit[dc]]=dtsplit[dc];
}
// uses assc array for standard mysql format
dstring[r] = ''20''+df[''yy'']+''-''+df[''mm'']+''-''+df[''dd''];
dstring[r] += '' ''+df[''HH'']+'':''+df[''MM'']+'':''+df[''ss''];
Según http://blog.dygraphs.com/2012/03/javascript-and-dates-what-mess.html el formato "aaaa / mm / dd" resuelve los problemas habituales. Dice: "Mantente en" YYYY / MM / DD "para tus cadenas de fecha siempre que sea posible. Es universalmente compatible e inequívoco. Con este formato, todos los horarios son locales". He establecido pruebas: http://jsfiddle.net/jlanus/ND2Qg/432/ Este formato: + evita la ambigüedad en el orden de los días y los meses mediante el pedido de ymd y un año de 4 dígitos + evita el problema UTC vs. local no cumplir con el formato ISO mediante barras diagonales + danvk, el tío de dygraphs , dice que este formato es bueno en todos los navegadores.
Si bien share que pasar cadenas en el método de análisis por lo general no es seguro, la nueva especificación ECMA-262 5th Edition (también conocida como ES5) en la sección 15.9.4.2 sugiere que Date.parse()
realidad debería manejar fechas con formato ISO. La vieja especificación no hizo tal afirmación. Por supuesto, los navegadores antiguos y algunos navegadores actuales aún no proporcionan esta funcionalidad ES5.
Tu segundo ejemplo no está mal. Es la fecha especificada en UTC, como lo Date.prototype.toISOString()
, pero está representada en su zona horaria local.
Use moment.js para analizar las fechas:
var caseOne = moment("Jul 8, 2005", "MMM D, YYYY", true).toDate();
var caseTwo = moment("2005-07-08", "YYYY-MM-DD", true).toDate();
El tercer argumento determina el análisis estricto (disponible a partir de 2.3.0). Sin él, moment.js también puede dar resultados incorrectos.