Inicio / Artículos

Asociación de tipos temporales con tareas de fechas reales: siete ejemplos prácticos

Trabaja en siete tareas cotidianas relacionadas con fechas y horas utilizando JavaScript Temporal, desde fechas simples y desbordamiento de meses hasta zonas horarias, aritmética de horario de verano y validación.

Editor: REACTAPP.TOP9 min de lectura2060 palabras

Fuentes: JavaScript Temporal: 7 Date and Time Problems Explained Simply

Los requisitos relacionados con las fechas suelen parecer triviales: extender una suscripción por un mes, mostrar una reunión en la zona horaria de un colega, contar los días entre dos fechas. Las complicaciones aparecen más tarde, cuando resulta que febrero es más corto, un cambio de horario de verano hace que “mañana” esté a 23 horas de distancia, o cuando es necesario almacenar una fecha de cumpleaños aunque no tenga zona horaria alguna. La API Temporal de JavaScript aborda esto al asignar a cada tipo de valor su propio tipo, de modo que el propio tipo documenta qué significa ese valor. Los siete ejemplos funcionales a continuación muestran qué tipo se adapta a cada tarea, dónde los valores predeterminados pueden sorprender y qué decisiones Temporal deja deliberadamente en manos de la aplicación.

Configuración del entorno para ejecutar los ejemplos

La compatibilidad nativa aún se está implementando. En el momento de redactar este texto, MDN indica que Temporal tiene disponibilidad limitada; por lo tanto, confirme el soporte en los navegadores y la versión de Node.js que utilice antes de confiar en la implementación integrada. La página de referencia de Temporal en MDN muestra el estado actual de compatibilidad.

Para probarlo sin soporte nativo, instale el polyfill de Temporal en una carpeta de proyecto vacía. Necesitará Node.js y npm:

mkdir temporal-demo
cd temporal-demo
npm init -y
npm install @js-temporal/polyfill@0.5.1

Cree un archivo llamado demo.mjs y comiéncelo con esta importación:

import { Temporal } from "@js-temporal/polyfill";

Agregue cualquiera de los ejemplos a continuación de la importación y ejecute el archivo:

node demo.mjs

Todos los ejemplos suponen el mismo import. El polyfill exporta un enlace local Temporal y no instala uno global, por lo que cada archivo que lo utiliza necesita realizar el import. La versión está fijada en 0.5.1 porque esa es la versión contra la cual se escribieron los ejemplos; consulte las versiones del polyfill antes de usarlo en un proyecto real.

1. Almacenar una fecha de calendario sin hora ni zona horaria

Un cumpleaños o una fecha límite de un contrato a menudo no tiene una hora específica relevante. Si el requisito es simplemente “15 de octubre”, asignar la medianoche y una zona horaria implica inventar información, y esa información inventada es precisamente lo que hace que la fecha cambie un día cuando se muestra en otro lugar. Temporal.PlainDate representa únicamente la fecha:

const deadline = Temporal.PlainDate.from("2026-10-15");

Sus campos son números simples y se serializan de vuelta a una cadena ISO:

console.log(deadline.year);       // 2026
console.log(deadline.month);      // 10
console.log(deadline.day);        // 15
console.log(deadline.toString()); // 2026-10-15

El valor no tiene hora, ni desfase UTC ni zona horaria. Utiliza por defecto el calendario ISO y, a diferencia del objeto Date heredado, los meses van numerados del 1 al 12 en lugar de comenzar en cero.

Tenga presente esta limitación: un PlainDate no determina ningún momento en tiempo absoluto. Si la fecha límite realmente significa “5 PM en Delhi”, también se necesita una hora y una zona horaria, y para eso existe ZonedDateTime.

2. Derivar nuevas fechas sin modificar la original

Las fechas mutables compartidas son una fuente clásica de errores: una parte del código ajusta un valor que otra parte sigue leyendo. Los objetos temporales son inmutables, y las operaciones aritméticas siempre generan un nuevo objeto:

const originalDate = Temporal.PlainDate.from("2026-10-15");
const extendedDate = originalDate.add({ days: 7 });

Al registrar ambos valores se puede observar que el original permanece sin cambios:

console.log(originalDate.toString()); // 2026-10-15
console.log(extendedDate.toString()); // 2026-10-22

Esto significa que puede mantener la fecha límite anterior y la ampliada una al lado de la otra sin tener que clonar nada primero.

El lado negativo es un error común: llamar a originalDate.add({ days: 7 }) en una línea por sí solo y esperar que originalDate cambie. Eso no ocurre. Asigne o devuelva el resultado.

3. Decida qué significa “un mes después”

Añadir un mes al 31 de enero no tiene una respuesta única correcta, ya que el 31 de febrero no existe. Su aplicación necesita una regla. La predeterminada de Temporal es restringir el resultado a la fecha válida más cercana. Comience desde el final de enero:

const januaryEnd = Temporal.PlainDate.from("2026-01-31");

Añadir un mes fija el día al final de febrero:

const nextMonth = januaryEnd.add({ months: 1 });console.log(nextMonth.toString()); // 2026-02-28

Cuando es inaceptable mover silenciosamente el día, solicite a Temporal que lance una excepción pasando overflow: "reject". Tomando como ejemplo la fecha de facturación:

const billingDate = Temporal.PlainDate.from("2026-01-31");

El rechazo se manifiesta como un RangeError, el cual el fragmento de código captura y vuelve a lanzar cualquier otro error:

try {
  billingDate.add({ months: 1 }, { overflow: "reject" });
} catch (error) {
  if (!(error instanceof RangeError)) throw error;
  console.log("That day does not exist in the next month.");
}

Ninguna de estas opciones constituye su política de facturación; cada una es solo un mecanismo para implementarla. También existe un efecto compuesto que debe tenerse en cuenta. Una vez que el 31 de enero se ajusta al 28 de febrero, agregar otro mes a ese resultado da como resultado el 28 de marzo y no el 31, por lo que un calendario creado al sumar repetidamente un mes al valor anterior se desvía. Si la regla es “el último día de cada mes”, modele esa regla explícitamente, por ejemplo calculando siempre a partir de la fecha de referencia original o utilizando la duración del mes, en lugar de realizar sumas consecutivas.

4. Mostrar un instante en varias zonas horarias

Una marca de tiempo para un evento identifica un momento exacto. La fecha y hora locales en la pantalla de alguien dependen de su ubicación. Temporal.Instant representa el momento en sí:

const meeting = Temporal.Instant.from("2026-10-15T09:00:00Z");

Al convertirlo a zonas horarias nombradas se obtiene la hora local en cada lugar, y al convertirlo de vuelta se comprueba que ambos siguen refiriéndose al mismo instante:

const delhi = meeting.toZonedDateTimeISO("Asia/Kolkata");
const london = meeting.toZonedDateTimeISO("Europe/London");console.log(delhi.toString());
// 2026-10-15T14:30:00+05:30[Asia/Kolkata]console.log(london.toString());
// 2026-10-15T10:00:00+01:00[Europe/London]console.log(delhi.toInstant().equals(london.toInstant()));
// true

Las representaciones locales difieren, pero el instante subyacente es idéntico. (Las líneas unidas en este fragmento deberían ser declaraciones separadas.)

Use zonas horarias nombradas como Europe/London en lugar de desplazamientos fijos. Una zona horaria nombrada incluye las reglas para cómo cambia su desplazamiento a lo largo del año; un +01:00 codificado directamente solo describe a Londres durante el horario de verano.

Aquí es donde comienzan muchos errores: un desfase no es una zona horaria. Un desfase indica la diferencia con respecto a UTC en un momento específico, mientras que una zona nombrada proporciona las reglas que determinan esa diferencia en cualquier momento.

Las cadenas de texto anteriores están orientadas a las máquinas, incluida la anotación de zona entre corchetes. Para todo lo que se muestra a los usuarios, formatee el valor según su configuración regional, por ejemplo con toLocaleString() o Intl.DateTimeFormat, en lugar de imprimir la cadena en bruto.

5. Mantenga separados “mañana” y “24 horas más tarde”

La mayor parte del año estas frases dan el mismo resultado. Pero durante los cambios de horario de verano no ocurre lo mismo. Tome el mediodía en Nueva York el 7 de marzo de 2026, el día anterior al cambio de hora:

const beforeClockChange = Temporal.ZonedDateTime.from(
  "2026-03-07T12:00:00-05:00[America/New_York]"
);

Agregar un día calendario y agregar 24 horas produce diferentes horas locales:

const tomorrow = beforeClockChange.add({ days: 1 });
const after24Hours = beforeClockChange.add({ hours: 24 });console.log(tomorrow.toString());
// 2026-03-08T12:00:00-04:00[America/New_York]console.log(after24Hours.toString());
// 2026-03-08T13:00:00-04:00[America/New_York]

La aritmética de calendarios mantiene la hora según el reloj de pared en el mediodía. La aritmética del tiempo transcurrido cuenta exactamente 24 horas, y como el reloj avanza una hora durante la noche, se detiene a la 1 de la tarde.

Cada uno es adecuado para diferentes requisitos. Una cita diaria generalmente significa “la misma hora local del día siguiente”, por lo que se deben sumar días. Un token o reserva que vence exactamente después de 24 horas debería tener en cuenta las horas. Temporal puede calcular cualquiera de los dos casos; debes decidir qué significado necesita tu función, y vale la pena anotar esa decisión.

6. Rechazar fechas inválidas y horas locales inexistentes

Al validar la entrada del formulario, una fecha imposible debe generar un error, en lugar de convertirse silenciosamente en otra fecha. Para la entrada de objetos, solicita un manejo estricto con overflow: "reject":

try {
  Temporal.PlainDate.from(
    { year: 2026, month: 2, day: 30 },
    { overflow: "reject" }
  );
} catch (error) {
  if (!(error instanceof RangeError)) throw error;
  console.log("Please choose a valid calendar date.");
}

Sin esa opción, el objeto { year: 2026, month: 2, day: 30 } quedaría restringido al 28 de febrero. Las cadenas ISO se comportan de manera diferente: el análisis de "2026-02-30" es rechazado directamente en lugar de ser corregido. No asuma que el análisis de cadenas y la construcción de objetos comparten el mismo comportamiento ante desbordamientos.

Una fecha válida aún puede combinarse con una hora local que nunca ocurre. Cuando Nueva York avance sus relojes el 8 de marzo de 2026, la hora 2:30 AM se omite por completo. La opción disambiguation controla qué sucede con dichas entradas:

try {
  Temporal.ZonedDateTime.from(
    "2026-03-08T02:30[America/New_York]",
    { disambiguation: "reject" }
  );
} catch (error) {
  if (!(error instanceof RangeError)) throw error;
  console.log("Choose a time that exists and is unambiguous.");
}

Con disambiguation: "reject", Temporal lanza excepciones tanto para las horas omitidas como para las repetidas, es decir, aquellas horas que aparecen dos veces cuando los relojes retroceden. Las otras configuraciones seleccionan automáticamente una interpretación. En el caso de un formulario de reserva, pedir al usuario que elija una hora válida suele ser más claro que cambiar su cita sin informarle.

7. Compare, mida y ordene fechas de forma explícita

Para comparaciones cronológicas, utilice los métodos propios del tipo. Comience con dos fechas:

const startDate = Temporal.PlainDate.from("2026-10-15");
const endDate = Temporal.PlainDate.from("2026-10-20");

Temporal.PlainDate.compare() devuelve -1, 0 o 1 según la primera fecha sea anterior, igual o posterior. until() devuelve un Temporal.Duration, que se muestra en formato de duración ISO 8601; por lo tanto, cinco días aparecen como P5D:

console.log(Temporal.PlainDate.compare(startDate, endDate));
// -1const difference = startDate.until(endDate, {
  largestUnit: "days",
});console.log(difference.days);       // 5
console.log(difference.toString()); // P5D

Para PlainDate, la diferencia se mide en días calendario. No calcula las horas transcurridas en ninguna zona horaria, ni decide si su producto cuenta tanto con el día de inicio como con el de finalización; esa regla relacionada con el error de “uno en exceso” la debe definir usted.

Dado que compare tiene la firma que espera Array.prototype.sort, se puede pasar directamente. Teniendo una lista de fechas sin ordenar:

const dates = [
  "2026-11-03",
  "2026-10-15",
  "2026-10-20",
].map(value => Temporal.PlainDate.from(value));

el ordenamiento se convierte en una sola línea de código:

dates.sort(Temporal.PlainDate.compare);console.log(dates.map(date => date.toString()).join(", "));
// 2026-10-15, 2026-10-20, 2026-11-03

Evite los hábitos heredados de Date. Restar objetos temporales o compararlos con < y > genera errores en estos tipos, lo cual es intencional: evita comparaciones accidentales del tipo incorrecto. Además, === verifica la identidad del objeto, por lo que dos fechas iguales creadas por separado no son ===; utilice .equals() para la igualdad de valores. Para conocer formas más sutiles en las que los viejos hábitos reaparecen, consulte nuestra guía sobre las trampas de la API Temporal que reintroducen errores con fechas.

Elija el tipo antes de realizar el cálculo

La mayoría de los errores relacionados con fechas son en realidad errores de modelado, así que primero defina qué significa un valor:

  • Una fecha sin hora ni zona horaria: PlainDate
  • Una hora sin fecha ni zona horaria: PlainTime
  • Una fecha y hora local sin zona horaria: PlainDateTime
  • Un punto exacto en la línea de tiempo global: Instant
  • Una fecha y hora vinculada a una zona horaria específica: ZonedDateTime
  • Cantidad de tiempo calendárico o horario: Duration
  • Esa elección merece ser discutida al diseñar un contrato de API o una columna de base de datos. Un campo llamado simplemente date puede generar desacuerdos sobre si debe contener una fecha calendárica, una marca de tiempo en UTC o una hora local.

    La migración no tiene por qué hacerse de golpe. Elija una funcionalidad con requisitos claros, defina su formato de entrada, tipo temporal, política de zona horaria y salida esperada, y agregue casos de prueba para finales de mes y cambios en el reloj cuando sea necesario. El código relacionado con las fechas puede dejarse intacto hasta que haya una razón para modificarlo.

    Lista de verificación previa a la producción

    • Verifique el soporte temporal en cada entorno al que envíe su aplicación, y si utiliza un polyfill, tenga en cuenta el costo de descarga y procesamiento en su presupuesto del paquete.
    • Acuerde con su backend cómo se realizará la serialización. Una fecha simple, un instante en UTC y una cadena con un identificador de zona entre corchetes son tres formatos diferentes.
    • Haga explícitas las reglas comerciales: ¿puede “un mes después” modificar el día, deben rechazarse las horas de cita ambiguas, y ¿una duración se refiere a días calendario o a horas transcurridas?

    Conclusión

    La principal contribución de Temporal no es una interfaz API más amigable, sino que impone distinciones que Date había difuminado: fechas frente a instantes, desplazamientos frente a zonas horarias, aritmética calendárica frente al tiempo transcurrido. Ofrece herramientas precisas para cada caso, incluyendo modos estrictos que generan errores claros en lugar de hacer conjeturas. Lo que no puede hacer es elegir por usted las políticas a seguir. Decida esas reglas deliberadamente, encódíguelas en los tipos y opciones que proporcione, y cubra los casos límite con pruebas.

    Lecturas relacionadas

  • Cinco trampas de la API temporal que reintroducen silenciosamente errores con fechas — Aprenda cómo la API Temporal de JavaScript aún puede causar errores relacionados con zonas horarias, serialización, precisión y duración, a menos que se corrijan cinco patrones comunes de uso incorrecto.
  • Siete tareas de programación básica con Node.js y Express, resueltas y explicadas — Resuelve siete tareas típicas de entrevista para principiantes en Node.js, desde rutas y middleware de Express hasta manejo de errores, inversión de cadenas y detección de duplicados, y aprende a explicar cada una de ellas.
  • Características de Node.js 26 que reemplazan silenciosamente años de soluciones alternativas — Un recorrido por la API Temporal de Node.js 26, la ejecución nativa en TypeScript, los auxiliares de caché y otras adiciones que eliminan soluciones temporales de larga data.