También disponible en: English · Português · Français · العربية
Analizador de fechas HTTP
Los tres formatos que permite HTTP, qué significa cada uno y las seis formas en que Date.parse se equivoca con ellos.
¿Qué es una fecha HTTP?
Todas las marcas de tiempo de una cabecera HTTP —Date, Expires, Last-Modified, If-Modified-Since y la forma con fecha de Retry-After— se escriben en un formato que el RFC 9110 llama HTTP-date. Hay tres, porque antes de 1995 los servidores usaban tres, y la especificación define los tres por compatibilidad.
La regla que los gobierna es asimétrica, y esa asimetría es lo que conviene recordar. Quien recibe una marca de tiempo «debe aceptar los tres formatos»; quien la genera «debe generarla en el formato IMF-fixdate». O sea: solo hay una escritura que puedes producir —Sun, 06 Nov 1994 08:49:37 GMT— y tres que tienes que saber leer.
Los tres representan un instante en UTC. Los dos primeros lo dicen con las letras GMT; el tercero, heredado de la función asctime de C, no lleva zona ninguna, y la especificación se limita a decir que sus valores se suponen en UTC. Esa frase es donde empiezan casi todos los problemas.
Cómo usarlo
- Pega el valor de la cabecera. Solo el valor, sin el nombre del campo. Los tres botones de ejemplo cubren un formato cada uno, y el cuarto escribe este momento tal y como debe hacerlo un emisor.
- Lee la línea del formato y las notas de debajo. Verás el instante en UTC y en tu zona horaria, cuál de los tres formatos es, si un emisor tiene permitido enviarlo y todas las formas en que se aparta de la gramática.
- Compara la última fila con la de arriba. Esa fila es lo que el Date.parse de tu propio navegador hace con la misma cadena. Donde no coincidan, verás la diferencia en segundos.
Seis cosas que Date.parse hace mal
La más dañina es asctime. Ese formato no lleva zona horaria y el RFC 9110 dice que sus valores se suponen en UTC, pero Date.parse lo lee como hora local: la respuesta queda desviada el desfase horario de quien la ejecuta, y solo es correcta en Greenwich. Es el peor tipo de fallo: silencioso, verosímil y distinto en cada máquina. La fila de comparación de esta página se ejecuta en tu navegador precisamente para que veas qué hace el tuyo.
Luego están los años de dos dígitos. El formato obsoleto del RFC 850 escribe el año con dos cifras, y la regla de la especificación es relativa al presente: quien recibe un valor que quede a más de cincuenta años vista debe leerlo como el año pasado más reciente terminado en esas cifras. Los motores de JavaScript usan en cambio un pivote fijo en 50. Hoy las dos reglas discrepan en 27 de los 100 años posibles —todos los valores del 50 al 76— y como el límite de la especificación avanza cada enero, ese conjunto crece en uno cada año. Un 68 significa 2068 según la regla y 1968 según el pivote: un siglo entero de diferencia.
Con un año de cuatro cifras menor que 100 pasa lo mismo. La gramática dice que el año son cuatro dígitos, así que 0094 es una forma legítima de escribir el año 94, y Date.parse lo convierte igualmente en 1994.
El nombre del día sobra, y por eso sirve dos veces. Si contradice a la fecha, algo montó mal el valor: Mon, 06 Nov 1994 fue domingo, y Date.parse lo acepta sin decir nada. Además desempata el siglo: el ejemplo de arriba, Tuesday, 06-Nov-68, solo es coherente si el año es 2068, porque el 6 de noviembre de 1968 fue miércoles. El campo redundante resuelve el ambiguo, que es una casualidad afortunada en un formato que nunca se diseñó para eso.
La gramática permite el segundo 60 para un segundo intercalar. Date.parse convierte 23:59:60 en 23:59:00: tira los segundos y pierde un minuto en lugar de avanzar un segundo.
Y por último las fechas imposibles. El 31 de noviembre no existe, y Date.parse devuelve el 1 de diciembre en vez de NaN: un valor que parecerá perfectamente razonable en un registro.
Límites honestos
Esta página comprueba el valor, no el mensaje del que salió. Si la cabecera Date debía enviarse siquiera, si una caché puede usarla y si el reloj del emisor iba bien son cosas que una cadena de texto no puede decirte; de hecho el RFC 9110 permite expresamente que un receptor con reloj sustituya un valor de Date inválido por la hora a la que llegó la respuesta.
También aplica la regla de los cincuenta años tal como queda hoy. Eso es lo correcto y conviene decirlo en voz alta: la misma marca de tiempo del RFC 850 significa de verdad años distintos según cuándo se lea, así que un valor guardado hoy en un registro y releído dentro de décadas puede cambiar de significado. No hay forma de arreglarlo en un analizador: al formato le faltan dos dígitos y ya está.
El veredicto de conformidad va sobre la gramática, no sobre si el valor funcionará. Un gmt en minúsculas, un día de una cifra y un espacio de más son cosas que la especificación prohíbe producir y que además todos los analizadores reales aceptan. La página te dice de qué lado de esa raya estás en vez de fingir que es un aprobado o un suspenso.
La casilla de Retry-After distingue sus dos formas —un número entero de segundos o una marca de tiempo— porque ese campo es el único sitio donde HTTP admite las dos, y es una fuente habitual de confusión. Un número negativo y uno con decimales no son ninguna de las dos.
¿Por qué es gratis?
Todo se ejecuta en tu navegador. El valor de la cabecera no se envía a ninguna parte, algo que importa en una página como esta: un Last-Modified o un If-Modified-Since de un sistema real dice algo sobre ese sistema.
No hay coste de servidor que recuperar, así que no hay cuenta, ni límite, ni marca de agua.