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:
- -1 si la primera duración es más corta que la segunda.
- 0 si las duraciones son iguales.
- 1 si la primera duración es más larga que la segunda.
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
- Usa el formato ISO 8601 para las cadenas de duración: Esto asegura consistencia e interoperabilidad.
- Elige el tipo Temporal apropiado: Usa
Temporal.PlainDate
,Temporal.PlainTime
,Temporal.ZonedDateTime
, oTemporal.Instant
según si necesitas o no soporte para zonas horarias. - Normaliza las duraciones cuando sea necesario: La normalización simplifica las duraciones y las hace más fáciles de comparar.
- Maneja las zonas horarias con cuidado: Las conversiones de zona horaria pueden ser complejas, así que usa
Temporal.ZonedDateTime
y ten en cuenta las transiciones del horario de verano. - Considera la unidad más pequeña: Al calcular duraciones, especifica la unidad más pequeña para obtener el nivel de precisión deseado.
- Escribe pruebas unitarias: Prueba tu código a fondo para asegurar que los cálculos de duración sean precisos.
Errores Comunes
- Ignorar las zonas horarias: No tener en cuenta las zonas horarias puede llevar a cálculos de duración incorrectos, especialmente al tratar con eventos en diferentes ubicaciones.
- Usar el objeto Date heredado: El objeto
Date
heredado es conocido por sus peculiaridades e inconsistencias. Prefiere la API Temporal para un manejo más fiable de fechas y horas. - No normalizar las duraciones: No normalizar las duraciones puede hacer que las comparaciones y los cálculos sean más complejos.
- Formato ISO 8601 incorrecto: Usar una cadena de duración ISO 8601 inválida puede causar errores.
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:
- Programación de Eventos Globales: Programar eventos con precisión a través de múltiples zonas horarias, teniendo en cuenta las transiciones del horario de verano. Por ejemplo, programar un seminario web que comienza a las 9:00 AM PST y mostrar la hora de inicio correspondiente en varias zonas horarias como CET, JST y AEDT.
- Planificación de Viajes Internacionales: Calcular la duración de los viajes, incluyendo escalas y cambios de zona horaria. Esto es útil para generar itinerarios y gestionar horarios de vuelos. Por ejemplo, calcular el tiempo total de viaje de Nueva York a Tokio, incluyendo una escala en Londres y ajustando por las diferencias de zona horaria.
- Comercio Electrónico Global: Mostrar los tiempos de entrega estimados en la zona horaria local del usuario. Esto requiere considerar la zona horaria de origen, la duración del envío y la zona horaria de destino. Por ejemplo, un artículo enviado desde un almacén en Alemania a un cliente en Australia, con un tiempo de entrega estimado de 7 días, mostrado en la hora local del cliente.
- Transacciones Financieras Transfronterizas: Calcular con precisión la acumulación de intereses o los plazos de pago en diferentes regiones. Esto a menudo implica considerar diferentes días hábiles y festivos en cada país. Por ejemplo, calcular el interés devengado en un préstamo en Singapur, teniendo en cuenta los días festivos de Singapur.
- Aplicaciones de Calendario Multiculturales: Soportar varios sistemas de calendario, como el calendario islámico o hebreo, y calcular con precisión la duración de los eventos y los recordatorios basados en estos calendarios.
- Gestión de Proyectos Globales: Hacer un seguimiento de la duración de las tareas del proyecto y los plazos en equipos distribuidos, teniendo en cuenta los diferentes horarios de trabajo y zonas horarias.
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.