¿Por qué "2016-02-16" no es igual a "2016-02-16 00:00"?

96

Estoy tratando de pasar ambas cadenas de fechas a new Date(t).

Espero que ambas cadenas representen la misma hora, después de todo, si omito la hora, ¿no debería ser la medianoche de ese día?

Pero mientras,

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

devuelve 2016-02-16, medianoche, hora local como se esperaba,

new Date("2016-02-16")

devuelve 2016-02-16, medianoche UTC, lo cual es incorrecto, o al menos no es lo que esperaba dado lo que analiza la otra cadena.

Lo entendería si ambos tuvieran el mismo comportamiento, ya sea para devolver la hora como hora local o como UTC, pero parece muy inconsistente por qué devuelven cosas diferentes como esta.

Como solución temporal, cada vez que encuentro una fecha que no tiene una marca de tiempo correspondiente, puedo agregar "00:00" para obtener un comportamiento consistente, pero parece que esto es bastante frágil.

Obtengo este valor de un elemento INPUT, de tipo 'datetime-local', por lo que parece especialmente incoherente que tenga que solucionar un valor devuelto por un elemento de página.

¿Estoy haciendo algo mal o debería hacer algo diferente?

Miguel
fuente
2
2016-02-16 00:00- esta no parece la hora válida en absoluto. ecma-international.org/ecma-262/6.0/… , pero incluso después de ponerlo Tallí, de hecho se comporta de manera diferente
zerkms
¿Cómo obtiene exactamente actualmente el objeto Fecha del elemento de entrada en función de su valor?
BoltClock
4
Según el estándar: "Si los campos HH, mm o ss están ausentes, se utiliza" 00 "como valor y el valor de un campo sss ausente es" 000 ". Si el desplazamiento de la zona horaria está ausente, la fecha y hora se interpreta como una hora local ". --- debería comportarse igual.
zerkms
@BoltClock Hmm, parece que está tomando el campo de valor del elemento y (como notó zerkms) eliminando la T por alguna razón (creo que porque el valor se muestra al usuario en un contexto donde "T" sería confuso)
Michael
1
@Michael: Fantástico. Las peculiaridades del navegador como esta son mis favoritas. (¿O podría ser una peculiaridad de la especificación DOM? No tengo idea, realmente no he mirado.)
BoltClock

Respuestas:

100

Es lo que dice la especificación ES5.1 :

El valor de un desplazamiento de zona horaria ausente es "Z".

También dice:

La función primero intenta analizar el formato de la Cadena de acuerdo con las reglas indicadas en Formato de cadena de fecha y hora (15.9.1.15). Si la cadena no se ajusta a ese formato, la función puede recurrir a cualquier heurística específica de implementación o formato de fecha específico de implementación.

Dado que el formato requiere un Tseparador entre fecha y hora, las horas válidas van a 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)

... mientras que en node.js, una hora no válida (sin el separador T) parece ir a la hora local específica de la implementación:

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

Tenga en cuenta que ES6 cambió esto, en la misma parte de la documentación a la que cambia:

Si el desplazamiento de la zona horaria está ausente, la fecha-hora se interpreta como una hora local.

La alegría de romper los cambios .

Editar

Según TC39 , la especificación debe interpretarse como cadenas de fecha y hora sin una zona horaria (p. Ej., "2016-02-16T00: 00: 00") se tratan como locales (según ISO 8601), pero solo cadenas de fecha (p. Ej. "2016-02-16") como UTC (que es incompatible con ISO 8601).

Joachim Isaksson
fuente
20
La alegría de romper los cambios, de hecho. En un nuevo borrador del estándar ES7, se revirtió parte del cambio de UTC a la hora local.El nuevo estándar dice que las fechas deben interpretarse como UTC si no se especifica una zona horaria, mientras que las fechas y horas deben interpretarse como una hora local si no hay una zona horaria está especificado.
hichris123
3
Realmente ambos deberían ser la hora local, independientemente de los formularios de solo fecha o de fecha y hora. Así es como funciona ISO8601. Es ridículo que la forma de solo fecha se interprete como la medianoche UTC, ya que una fecha UTC atemporal ni siquiera tiene sentido conceptualmente. Hubo un acalorado debate sobre esto en ECMA tc39. Luché por la hora local y perdí. github.com/tc39/ecma262/issues/87
Matt Johnson-Pint
10

Según las especificaciones :

La función primero intenta analizar el formato de la Cadena de acuerdo con las reglas indicadas en Formato de cadena de fecha y hora (15.9.1.15). Si la cadena no se ajusta a ese formato, la función puede recurrir a cualquier heurística específica de implementación o formato de fecha específico de implementación.

Y los formatos de cadena de fecha y hora aceptan 2016-02-16como una fecha válida

Este formato incluye formularios de solo fecha:

AAAA
AAAA-MM
AAAA-MM-DD

[...] Si los campos HH, mm o ss están ausentes, se utiliza "00" como valor y el valor de un campo sss ausente es "000". El valor de un desplazamiento de zona horaria ausente es "Z".

Así se 2016-02-16traduce en 2016-02-16T00:00:00.000Z.

La otra fecha 2016-02-16 00:00no se ajusta al formato y, por lo tanto, su análisis es específico de la implementación. Aparentemente, estas fechas se tratan como si tuvieran una zona horaria local y la fecha de su ejemplo devolverá diferentes valores según la zona horaria:

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

Resumen:

  • Para los formatos de fecha y hora conformes, el comportamiento está bien definido: en ausencia de un desplazamiento de zona horaria, la cadena de fecha se trata como UTC (ES5) o local (ES6).
  • Para los formatos de fecha y hora no conformes, el comportamiento es específico de la implementación; en ausencia de una compensación de zona horaria, el comportamiento habitual es tratar la fecha como local.
  • De hecho, la implementación podría optar por regresar en NaNlugar de intentar analizar las fechas no conformes. Simplemente pruebe su código en Internet Explorer 11;)
Salman A
fuente
7

Quizás se encuentre con diferencias entre las implementaciones de ES5, ES6 y el resultado esperado. Por Date.parse en MDN, "especialmente en diferentes implementaciones de ECMAScript donde cadenas como" 2015-10-12 12:00:00 "se pueden analizar como NaN, UTC o zona horaria local" es significativo.

Pruebas adicionales en Firefox 44 e IE 11 revelaron que ambos devuelven un objeto de fecha new Date("2016-02-16 00:00"), cuyo objeto devuelve NaN cuando se intenta obtener un valor de componente de fecha, y cuyo valor toString es "Fecha no válida" (no "NaN"). Por lo tanto, agregar "00:00 para obtener un comportamiento consistente" puede romper fácilmente en diferentes navegadores.

Como se señaló en otras respuestas, new Date("2016-02-16")utiliza un desplazamiento de zona horaria de cero de forma predeterminada, lo que produce la medianoche UTC en lugar de local.

traktor53
fuente
OP parece obtener resultados diferentes en la misma implementación.
Salman A
6

Por DateParser::Parse()código fuente V8 para Chrome.

Fechas ES5 ISO 8601:

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

Un número sin signo seguido de ':' es un valor de tiempo y se agrega al TimeComposer.

la zona horaria se establece de forma predeterminada en Z si falta

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

Una cadena que coincida con ambos formatos (p 1970-01-01. Ej. ) Se analizará como una cadena de fecha y hora de ES5, lo que significa que estará predeterminada en UTC time-zone. Eso es inevitable si se sigue la especificación ES5.

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

devuelve 2016-02-16, medianoche UTC, lo cual es incorrecto, o al menos no es lo que esperaba dado lo que analiza la otra cadena.

Agrega el desplazamiento de la zona horaria al 00:00

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

Mi zona horaria es IST con un valor de compensación (en minutos) +330, por lo que agregó 330 minutos a las 00:00.

Según ecma-262, sección 20.3.3.2 Date.parse (cadena)

Si ToString da como resultado una finalización abrupta, el Registro de finalización se devuelve inmediatamente. De lo contrario, parse interpreta la cadena resultante como una fecha y hora; devuelve un Número, el valor de hora UTC correspondiente a la fecha y la hora. La Cadena puede interpretarse como una hora local, una hora UTC o una hora en alguna otra zona horaria, según el contenido de la Cadena.

Cuando establezca explícitamente las unidades de tiempo new Date("2016-02-16 00:00"), usará establecer eso como hoursy minutes,

De lo contrario, como se indica aquí en 2 0.3.1.16

Si el desplazamiento de la zona horaria está ausente, la fecha-hora se interpreta como una hora local.

gurvinder372
fuente
Sí, pero ¿por qué hace esto en un caso pero no en el otro?
Michael
@Michael sí, consulte ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf section 20.3.3.2
gurvinder372
Entonces, ¿por qué esos son resultados diferentes? El estándar afirma que debe ser el mismo
zerkms
@zerkms Standard dice lo que hará cuando pasas las unidades de tiempo, no dice qué hará cuando no lo hagas. Dice 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.¿Dónde está afirmando que debe ser el mismo?
gurvinder372
@ gurvinder372 He proporcionado la cita en los comentarios de la pregunta: "Si los campos HH, mm o ss están ausentes, se usa" 00 "como valor y el valor de un campo sss ausente es" 000 ". Si la zona horaria se desplaza está ausente, la fecha y hora se interpreta como una hora local ".
zerkms