Español

Una guía completa para usar la API Temporal de JavaScript para cálculos de intervalos de tiempo precisos e intuitivos, cubriendo desde la creación básica hasta aritmética y formato avanzados.

Duración Temporal de JavaScript: Dominando los Cálculos de Intervalos de Tiempo

La API Temporal de JavaScript introduce una forma moderna y potente de manejar fechas, horas e intervalos de tiempo. El objeto Temporal.Duration representa una longitud de tiempo, proporcionando un enfoque claro e intuitivo para realizar cálculos con intervalos de tiempo. Este artículo profundiza en los detalles de Temporal.Duration, demostrando cómo crear, manipular y formatear duraciones para diversos casos de uso.

¿Qué es Temporal.Duration?

Temporal.Duration representa un lapso de tiempo, expresándolo en términos de años, meses, días, horas, minutos, segundos y fracciones de segundo (milisegundos, microsegundos, nanosegundos). A diferencia de los objetos Date que representan un punto específico en el tiempo, Temporal.Duration representa una cantidad de tiempo. Se adhiere al formato de duración ISO 8601 (p. ej., P1Y2M10DT2H30M representa 1 año, 2 meses, 10 días, 2 horas y 30 minutos). La API Temporal está diseñada para ser más intuitiva y menos propensa a errores que el objeto Date heredado.

Creación de Objetos Temporal.Duration

Hay varias formas de crear objetos Temporal.Duration:

1. Desde un Objeto Simple

Puedes crear una duración pasando un objeto con las propiedades deseadas:

const duration = new Temporal.Duration(1, 2, 10, 2, 30, 0, 0, 0);
console.log(duration.toString()); // Salida: P1Y2M10DT2H30M

Esto crea una duración de 1 año, 2 meses, 10 días, 2 horas y 30 minutos. Ten en cuenta que los argumentos corresponden al siguiente orden: years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds.

2. Desde una Cadena ISO 8601

También puedes crear una duración a partir de una cadena de duración ISO 8601 usando Temporal.Duration.from():

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.toString()); // Salida: P1Y2M10DT2H30M

Esto es particularmente útil cuando se trata de duraciones almacenadas en formato de cadena o recibidas de una fuente externa.

3. Usando los métodos add() y subtract() con Temporal.Instant, Temporal.ZonedDateTime, etc.

Cuando sumas o restas Temporal.Duration de otros tipos Temporales (como Temporal.Instant o Temporal.ZonedDateTime), se devuelve un Temporal.Duration que representa la diferencia entre los dos puntos en el tiempo si luego los restas. Por ejemplo:

const now = Temporal.Now.zonedDateTimeISO();
const later = now.add({ hours: 5 });
const duration = later.since(now);
console.log(duration.toString()); // Salida: PT5H

Accediendo a los Componentes de la Duración

Puedes acceder a los componentes individuales de un objeto Temporal.Duration usando sus propiedades:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
console.log(duration.years);      // Salida: 1
console.log(duration.months);     // Salida: 2
console.log(duration.days);       // Salida: 10
console.log(duration.hours);      // Salida: 2
console.log(duration.minutes);     // Salida: 30
console.log(duration.seconds);     // Salida: 0
console.log(duration.milliseconds); // Salida: 0
console.log(duration.microseconds); // Salida: 0
console.log(duration.nanoseconds);  // Salida: 0

Realizando Aritmética con Duraciones

Los objetos Temporal.Duration soportan suma y resta usando los métodos add() y subtract(). Estos métodos devuelven un nuevo objeto Temporal.Duration que representa el resultado de la operación.

const duration1 = Temporal.Duration.from("P1Y2M");
const duration2 = Temporal.Duration.from("P3M4D");

const addedDuration = duration1.add(duration2);
console.log(addedDuration.toString()); // Salida: P1Y5M4D

const subtractedDuration = duration1.subtract(duration2);
console.log(subtractedDuration.toString()); // Salida: P10M26D

También puedes encadenar estos métodos para cálculos más complejos:

const duration = Temporal.Duration.from("P1D").add({ hours: 12 }).subtract({ minutes: 30 });
console.log(duration.toString()); // Salida: P1DT11H30M

El método negated() devuelve un nuevo objeto Temporal.Duration con todos los componentes negados:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const negatedDuration = duration.negated();
console.log(negatedDuration.toString()); // Salida: -P1Y2M10DT2H30M

El método abs() devuelve un nuevo objeto Temporal.Duration con todos los componentes como valores positivos (valores absolutos):

const duration = Temporal.Duration.from("-P1Y2M10DT2H30M");
const absoluteDuration = duration.abs();
console.log(absoluteDuration.toString()); // Salida: P1Y2M10DT2H30M

El método with() te permite crear una nueva instancia de Temporal.Duration con algunas, o todas, las propiedades cambiadas a nuevos valores. Si un valor no se especifica en el objeto de argumento, se usará el valor original de la duración. Por ejemplo:

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const newDuration = duration.with({ years: 2, days: 5 });
console.log(newDuration.toString()); // Salida: P2Y2M5DT2H30M

Normalizando Duraciones

Las duraciones a veces se pueden expresar en una forma no normalizada (p. ej., P1Y12M, que podría simplificarse a P2Y). El método normalized() intenta simplificar una duración a su forma más compacta. Sin embargo, requiere una fecha de referencia para manejar las complejidades de las longitudes variables de los meses. Para normalizar correctamente, necesitarás una instancia de Temporal.PlainDate, Temporal.ZonedDateTime o Temporal.Instant.

Por ejemplo, normalizar una duración que involucra meses y días requiere una fecha de referencia:

const duration = Temporal.Duration.from("P1M32D");
const referenceDate = Temporal.PlainDate.from("2024-01-01");
const normalizedDuration = duration.normalized({ relativeTo: referenceDate });
console.log(normalizedDuration.toString()); // Salida: P2M1D

En este ejemplo, la duración P1M32D se normaliza en relación al 1 de enero de 2024, resultando en P2M1D porque enero tiene 31 días.

Si solo estás tratando con componentes de tiempo (horas, minutos, segundos, etc.), puedes normalizar sin una fecha de referencia:

const duration = Temporal.Duration.from("PT25H61M");
const normalizedDuration = duration.normalized({ relativeTo: null }); //o omitir el argumento relativeTo
console.log(normalizedDuration.toString()); // Salida: P1DT2H1M

Comparando Duraciones

Puedes comparar duraciones usando el método compare(). Este método devuelve:

const duration1 = Temporal.Duration.from("P1Y");
const duration2 = Temporal.Duration.from("P6M");

const comparisonResult = Temporal.Duration.compare(duration1, duration2);
console.log(comparisonResult); // Salida: 1

Ejemplos Prácticos

1. Calculando el Tiempo Hasta un Evento

Supongamos que quieres calcular el tiempo restante hasta un evento específico. Usando Temporal.Now.zonedDateTimeISO() para obtener la hora actual, y restando la fecha del evento. Si la fecha del evento ya ha pasado, la salida será negativa.

const eventDate = Temporal.ZonedDateTime.from({ timeZone: 'America/Los_Angeles', year: 2024, month: 12, day: 25, hour: 9, minute: 0, second: 0 });
const now = Temporal.Now.zonedDateTimeISO('America/Los_Angeles');

const durationUntilEvent = eventDate.since(now);

console.log(durationUntilEvent.toString()); // Salida: p. ej., P262DT14H30M (dependiendo de la fecha y hora actuales)

2. Seguimiento de la Duración de Tareas de Proyecto

En la gestión de proyectos, puedes usar Temporal.Duration para hacer un seguimiento de la duración estimada o real de las tareas.

const task1EstimatedDuration = Temporal.Duration.from("PT8H"); // 8 horas
const task2EstimatedDuration = Temporal.Duration.from("PT16H"); // 16 horas

const totalEstimatedDuration = task1EstimatedDuration.add(task2EstimatedDuration);
console.log(`Duración total estimada: ${totalEstimatedDuration.toString()}`); // Salida: Duración total estimada: P1DT

3. Calculando la Edad

Aunque calcular la edad con precisión requiere considerar años bisiestos y zonas horarias, Temporal.Duration puede proporcionar una estimación razonable:

const birthDate = Temporal.PlainDate.from("1990-05-15");
const currentDate = Temporal.PlainDate.from("2024-01-20");

const ageDuration = currentDate.since(birthDate, { smallestUnit: 'years' });
console.log(`Edad estimada: ${ageDuration.years} años`); // Salida: Edad estimada: 33 años

4. Mostrando Duraciones Legibles para Humanos

A menudo, necesitas mostrar duraciones en un formato legible para humanos. Aunque Temporal.Duration no tiene funciones de formato incorporadas, puedes crear una lógica de formato personalizada:

function formatDuration(duration) {
  const parts = [];
  if (duration.years) parts.push(`${duration.years} año${duration.years > 1 ? 's' : ''}`);
  if (duration.months) parts.push(`${duration.months} mes${duration.months > 1 ? 'es' : ''}`);
  if (duration.days) parts.push(`${duration.days} día${duration.days > 1 ? 's' : ''}`);
  if (duration.hours) parts.push(`${duration.hours} hora${duration.hours > 1 ? 's' : ''}`);
  if (duration.minutes) parts.push(`${duration.minutes} minuto${duration.minutes > 1 ? 's' : ''}`);
  if (duration.seconds) parts.push(`${duration.seconds} segundo${duration.seconds > 1 ? 's' : ''}`);

  return parts.join(', ');
}

const duration = Temporal.Duration.from("P1Y2M10DT2H30M");
const formattedDuration = formatDuration(duration);
console.log(formattedDuration); // Salida: 1 año, 2 meses, 10 días, 2 horas, 30 minutos

Uso Avanzado y Consideraciones

1. Manejo de Zonas Horarias

Cuando se trabaja con intervalos de tiempo que cruzan límites de zonas horarias o transiciones de horario de verano, es crucial usar Temporal.ZonedDateTime para asegurar cálculos precisos. Usar Temporal.PlainDate y Temporal.PlainTime evitará cualquier conversión de zona horaria.

2. Unidad Mínima y Redondeo

Los métodos `since()` y `until()` a menudo aceptan opciones para definir la unidad más pequeña para la duración resultante. Por ejemplo, calcular el tiempo *hasta* un evento y limitar los resultados a días.

const eventDate = Temporal.PlainDate.from("2024-12-25");
const now = Temporal.PlainDate.from("2024-01-20");

const durationUntilEvent = now.until(eventDate, { smallestUnit: 'days' });

console.log(durationUntilEvent.toString()); //ejemplo de salida PT340D

3. Segundos Intercalares

Temporal no tiene en cuenta los segundos intercalares de forma nativa. Si requieres una precisión extrema, necesitarás manejar los segundos intercalares por separado.

4. Zonas Horarias de la IANA

La API Temporal se basa en la base de datos de zonas horarias de la IANA (Autoridad de Números Asignados de Internet). Asegúrate de que tu entorno tenga una versión actualizada de la base de datos de la IANA para manejar con precisión las conversiones de zona horaria.

Mejores Prácticas

Errores Comunes

Casos de Uso del Mundo Real en Diferentes Culturas

La API Temporal puede ser particularmente beneficiosa en aplicaciones globales donde las diferencias de zona horaria y los matices culturales son significativos. Aquí hay algunos ejemplos:

Conclusión

Temporal.Duration proporciona una forma robusta e intuitiva de trabajar con intervalos de tiempo en JavaScript. Al comprender sus características y mejores prácticas, puedes realizar con confianza cálculos de duración precisos y fiables en tus aplicaciones. Adoptar la API Temporal conduce a un código más limpio y mantenible, y reduce el riesgo de errores asociados con el manejo heredado de fechas y horas.

A medida que profundices en la API Temporal, recuerda consultar la documentación oficial y experimentar con diferentes escenarios para comprender plenamente sus capacidades. Con su diseño moderno y características completas, Temporal está destinado a revolucionar la forma en que manejamos fechas, horas y duraciones en JavaScript.