Fechas y horas en JavaScript: Date e Intl

JavaScript maneja las fechas con el objeto Date, que tiene fama de traicionero, y las muestra con Intl.DateTimeFormat, que es excelente. Esta página separa lo que funciona bien de lo que conviene evitar.

Todos los ejemplos se ejecutaron con la zona horaria de la Ciudad de México (TZ=America/Mexico_City), así que las horas «locales» son las de allí.

Crear una fecha

const ahora = new Date();
console.log(ahora.toISOString());

const fecha = new Date(2026, 9, 19, 18, 30);
console.log(fecha.getFullYear(), fecha.getMonth(), fecha.getDate(), fecha.getDay());
console.log(fecha.toString());
salida2026-10-19T16:02:33.213Z
2026 9 19 1
Mon Oct 19 2026 18:30:00 GMT-0600 (Central Standard Time)

La primera línea es la fecha actual en formato ISO, siempre en UTC (la Z final). La segunda tiene truco: new Date(2026, 9, 19) es el 19 de octubre, porque los meses van de 0 a 11. Por eso getMonth() devuelve 9. Los días del mes, en cambio, empiezan en 1, y getDay() da el día de la semana: 0 es domingo y 1, lunes.

Mostrarla en español

const fecha = new Date("2026-10-19T18:30:00-06:00");

const larga = new Intl.DateTimeFormat("es-MX", {
  dateStyle: "full",
  timeStyle: "short",
  timeZone: "America/Mexico_City",
});
console.log(larga.format(fecha));

const corta = new Intl.DateTimeFormat("es-ES", { dateStyle: "short", timeZone: "Europe/Madrid" });
console.log(corta.format(fecha));

const aMedida = new Intl.DateTimeFormat("es-MX", {
  weekday: "short", day: "numeric", month: "long", hour: "2-digit", minute: "2-digit",
  timeZone: "America/Mexico_City",
});
console.log(aMedida.format(fecha));

for (const zona of ["America/Mexico_City", "America/Bogota", "Europe/Madrid", "Asia/Tokyo"]) {
  const hora = fecha.toLocaleTimeString("es", { timeZone: zona, hour: "2-digit", minute: "2-digit" });
  console.log(zona.padEnd(20), hora);
}
salidalunes, 19 de octubre de 2026, 6:30 p.m.
20/10/26
lun, 19 de octubre, 06:30 p.m.
America/Mexico_City  18:30
America/Bogota       19:30
Europe/Madrid        02:30
Asia/Tokyo           09:30

Intl.DateTimeFormat recibe el idioma y unas opciones, y se encarga de los nombres de los meses, el orden día-mes-año y el formato de la hora de cada país. Fíjate en la segunda línea: en Madrid ya es el día 20, porque son las 2:30 de la madrugada.

Un mismo instante se muestra con horas y fechas distintas según la zona horariaUna fecha es un instante; la zona horaria se elige al mostrarla1792456200000milisegundos desde el 1 de enero de 1970, en UTCCiudad de México19 oct, 18:30Bogotá19 oct, 19:30Madrid20 oct, 02:30Tokio20 oct, 09:30Intl.DateTimeFormat con la opción timeZone hace la conversión al mostrar.

Un objeto Date no guarda ninguna zona horaria: es un número. La zona entra en juego al mostrarlo. Esquema: decodigo.com.

Esta es la idea que evita casi todos los errores: un Date es un instante, un número de milisegundos. No «está» en ninguna zona. Cuando lo muestras eliges la zona, y si no la eliges se usa la del equipo donde corre el código, que en un servidor suele ser UTC.

Sumar días, restar fechas, tiempo relativo

const DIA = 24 * 60 * 60 * 1000;

const inicio = new Date("2026-10-19T00:00:00Z");
const fin = new Date("2026-12-25T00:00:00Z");
console.log("faltan", (fin - inicio) / DIA, "días");

const vence = new Date(inicio);
vence.setUTCDate(vence.getUTCDate() + 30);
console.log("vence el", vence.toISOString().slice(0, 10));

const finDeMes = new Date(Date.UTC(2026, 1 + 1, 0));
console.log("febrero de 2026 tiene", finDeMes.getUTCDate(), "días");

const relativo = new Intl.RelativeTimeFormat("es", { numeric: "auto" });
console.log(relativo.format(-1, "day"), "|", relativo.format(3, "week"), "|", relativo.format(-2, "month"));
salidafaltan 67 días
vence el 2026-11-18
febrero de 2026 tiene 28 días
ayer | dentro de 3 semanas | hace 2 meses

Restar dos fechas da milisegundos, y dividiendo entre los de un día salen días. Para sumar, setUTCDate se encarga de pasar de mes. El día 0 de un mes es el último del anterior: es el truco clásico para saber cuántos días tiene un mes. Y Intl.RelativeTimeFormat escribe «ayer» o «hace 2 meses» sin que tengas que traducir nada.

Las tres trampas de Date

console.log(new Date("2026-10-19").toString());
console.log(new Date("2026-10-19T00:00").toString());

const enero31 = new Date(2026, 0, 31);
enero31.setMonth(1);
console.log(enero31.toDateString());

console.log(new Date("19/10/2026").getTime());
console.log(String(new Date("19/10/2026")));
salidaSun Oct 18 2026 18:00:00 GMT-0600 (Central Standard Time)
Mon Oct 19 2026 00:00:00 GMT-0600 (Central Standard Time)
Tue Mar 03 2026
NaN
Invalid Date

Una fecha sin hora se lee en UTC. "2026-10-19" se interpreta como medianoche en UTC, que en México es la tarde del día 18. Con hora, "2026-10-19T00:00", se interpreta como hora local. La misma cadena con o sin hora cambia de día.

Los métodos set modifican la fecha y se desbordan. Pasar el 31 de enero a febrero da el 31 de febrero, que no existe, y Date lo convierte sin avisar en el 3 de marzo.

El formato día/mes/año no se entiende. "19/10/2026" da una fecha inválida. No hay error: getTime() devuelve NaN y al imprimirla sale Invalid Date. Para leer fechas que escribe una persona, sepáralas tú con split("/") y crea la fecha con números.

Cuál usar

Lo que necesitasLo que conviene
Guardar o enviar una fechatoISOString(): texto estándar, en UTC
Mostrarla a una personaIntl.DateTimeFormat con idioma y timeZone
«Hace 3 días»Intl.RelativeTimeFormat
Cálculos con calendario, zonas y horario de veranoLa biblioteca date-fns, o Temporal cuando llegue
Temporal, la sustituta de Date, todavía no está en Node 24. Lo comprobamos: typeof Temporal devuelve "undefined". Es la nueva API de fechas del lenguaje, con fechas sin hora, zonas horarias de verdad y meses que empiezan en 1, y ya está llegando a los navegadores. Hasta que esté en todas partes, Date e Intl siguen siendo lo estándar.

Todo el código de esta página se ejecutó con Node.js 24.22 en un contenedor limpio antes de publicarla; las salidas, incluidos los errores, están copiadas de esa ejecución. Documentación oficial: Intl.DateTimeFormat en MDN.