16 puntos por GN⁺ 2024-08-25 | 5 comentarios | Compartir por WhatsApp
  • Uno de los cambios recientes más destacables en ECMAScript es la propuesta de Temporal
    • Esta API ya puede usarse mediante el polyfill ofrecido por el equipo de FullCalendar
    • Una de las principales ventajas de esta API es que por fin existe un objeto nativo para representar "Zoned Date Time"

¿Qué es Zoned Date Time?

  • Al trabajar con fechas humanas, normalmente omitimos la zona horaria y hablamos de fecha y hora
  • Sin embargo, el objeto Date de JavaScript solo maneja números, por lo que se pierde el significado original de la fecha
  • Por ejemplo, al intentar registrar el momento de un pago con tarjeta, muchas personas podrían usar un código como este
    const paymentDate = new Date('2024-07-20T10:30:00');  
    
  • Esto hace que el navegador calcule los milisegundos con base en la zona horaria del usuario (CET). Pero la información almacenada puede interpretarse de forma distinta según la zona horaria
  • Además del hecho, muy importante, de que en JavaScript las fechas no usan UTC sino POSIX, donde los segundos intercalares se ignoran por completo, existe el problema de que, si solo hay números, se pierde el significado original de la fecha
  • Muchas personas creen que trabajar en UTC o pasar fechas en formato ISO es seguro, pero eso no es correcto porque igual puede perderse información

UTC no es suficiente

  • Incluso si se trabaja con formato ISO, al mostrar la fecha sigue faltando la información de la zona horaria
  • La función que convierte un timestamp en una fecha legible para humanos no es injective (inyectiva)
  • Por ejemplo, si viajaste de Madrid a Sídney y luego regresaste, pueden surgir confusiones por el problema de zonas horarias en el historial de transacciones bancarias

Introducción a la API Temporal

  • La API Temporal introduce el objeto Temporal.ZonedDateTime, que representa fecha y hora junto con la zona horaria
  • Propone una extensión de RFC 3339 para definir un estándar de serialización y deserialización de fechas en forma de cadena
  • 1996-12-19T16:39:57-08:00[America/Los_Angeles]
    • Esta cadena representa el 19 de diciembre de 1996 a las 16:39:57
    • El offset es -08:00 respecto a UTC (hora estándar del Pacífico, PST, a la que pertenece Los Ángeles)
    • También especifica de forma adicional la zona horaria correspondiente ("hora estándar del Pacífico") para que las aplicaciones con reconocimiento de zona horaria puedan considerarla
  • Soporta distintos sistemas de calendario (por ejemplo: budista, chino, Dangi, gregoriano, islámico, persa, japonés, etc.)

Operaciones básicas

Crear fechas
  • La API Temporal ofrece herramientas potentes para manejar zonas horarias.
  • Por ejemplo, al crear un objeto Temporal.ZonedDateTime, garantiza que la zona horaria se refleje correctamente:
    const zonedDateTime = Temporal.ZonedDateTime.from({  year: 2024,  month: 8,  day: 16,  hour: 12,  minute: 30,  second: 0,  timeZone: 'Europe/Madrid'});  
    
  • Gracias a esto, puede mantener la hora exacta incluso ante cambios de zona horaria o ajustes de hora local como el DST.
Comparar fechas
  • Los objetos ZonedDateTime ofrecen el método compare para comparar dos ZonedDateTime:
    const one = Temporal.ZonedDateTime.from('2020-11-01T01:45-07:00[America/Los_Angeles]');  
    const two = Temporal.ZonedDateTime.from('2020-11-01T01:15-08:00[America/Los_Angeles]');  
    Temporal.ZonedDateTime.compare(one, two);  // => -1  
    
Funciones integradas útiles
  • La propiedad hoursInDay devuelve la cantidad real de horas de esa fecha:
    Temporal.ZonedDateTime.from('2020-03-08T12:00-07:00[America/Los_Angeles]').hoursInDay;  // => 23  (día de inicio del DST)  
    
Conversión de zona horaria
  • Con el método withTimeZone se puede cambiar la zona horaria de un ZonedDateTime:
    zdt = Temporal.ZonedDateTime.from('1995-12-07T03:24:30+09:00[Asia/Tokyo]');  
    zdt.withTimeZone('Africa/Accra').toString(); // => '1995-12-06T18:24:30+00:00[Africa/Accra]'  
    
Aritmética básica
  • Con el método .add se pueden sumar o restar fechas según las reglas de DST:
    zdt = Temporal.ZonedDateTime.from('2020-03-08T00:00-08:00[America/Los_Angeles]');  
    laterDay = zdt.add({ days: 1 });  // => 2020-03-09T00:00:00-07:00[America/Los_Angeles]  
    
Calcular diferencias entre fechas
  • El método .until calcula la diferencia entre dos momentos y la devuelve como un objeto Temporal.Duration.
    • Por ejemplo, puede usarse como zdt.until(other).

Conclusión

  • La API Temporal transforma de manera radical la forma de manejar el tiempo en JavaScript
  • Este artículo abordó la diferencia entre las fechas legibles para humanos y las fechas en UTC, así como la forma de representarlas correctamente mediante el objeto Temporal.ZonedDateTime
  • En el próximo artículo se explorarán otros objetos interesantes como Instant, PlainDate y Duration

Opinión de GN⁺

  • El problema del manejo de fechas y horas, que durante mucho tiempo causó dificultades a los desarrolladores de JavaScript, podrá resolverse con la API Temporal
  • Como puede manejar automáticamente los problemas de zona horaria y DST, es muy útil al desarrollar aplicaciones globales
  • La compatibilidad con el objeto Date existente y los problemas de migración son aspectos que deben considerarse
  • La API Temporal está diseñada de una manera clara e intuitiva, y también destaca por su soporte de internacionalización, incluyendo compatibilidad con varios sistemas de calendario
  • Se espera que este cambio mejore considerablemente la productividad de los desarrolladores de JavaScript

5 comentarios

 
kyc1682 2024-08-26

¡Por fin!

 
hongminhee 2024-08-26
 
huiya 2024-08-26

Increíble, al diseñar servicios globales siempre me dolía la cabeza con las fechas.
Sí quiero probar esto una vez.

 
jjpark78 2024-08-26

De verdad, ¿por fin ya no habrá que usar moment o dayjs?

 
GN⁺ 2024-08-25
Opiniones de Hacker News
  • Manejar fechas y horas en Javascript es muy difícil

    • La librería Moment confunde fecha y hora, lo que causa muchos problemas
    • La librería Arrow de Python también comete el mismo error
    • La librería Chrono de Rust es predecible y tiene menos defectos
    • Date de JS y Moment son difíciles de usar
  • Se espera que la nueva API resuelva los problemas de zonas horarias en JS

    • Hay un problema donde en algunos casos analiza correctamente una zona horaria específica, pero en otros asume UTC
    • En un trabajo anterior tuve grandes dificultades por este problema
  • La función que convierte una marca de tiempo en una fecha legible para humanos no es inyectiva

    • Se confunden los conceptos de inyectividad y de estar bien definida
    • Para una marca de tiempo t, no existe una fecha legible para humanos x única
  • Broma sobre la curva de dificultad de manejar el tiempo

    • Los principiantes usan solo marcas de tiempo UTC
    • Los intermedios insisten en que hay que guardar zonas horarias y convertirlas
    • Los expertos vuelven a usar solo marcas de tiempo UTC
  • Si se usaran más ejemplos con fechas futuras, el artículo sería más convincente

    • Al registrar una marca de tiempo, solo se necesitan UTC y la ubicación
    • El ejemplo del banco es solo un problema de UX, no una pérdida de información
  • Usuario que se siente inseguro por no entender el manejo del tiempo

    • Pide recomendaciones de una buena introducción para entender los problemas de manejo del tiempo en lenguajes como Python
  • Tener un buen estándar de datetime es la mitad de la batalla

    • La otra mitad es que se adopte ampliamente
    • Para compatibilidad con otros sistemas, es más seguro convertir a cadenas ISO o a marcas de tiempo unix
  • Las cadenas de fecha ISO deben capturar la información correcta

    • Se cuestiona la idea de que JavaScript u otros lenguajes necesiten una estructura integrada
    • Temporal y Date resuelven problemas simples de una forma complicada
  • Pregunta sobre cómo manejar este problema en Postgres

  • Falta evidencia de que Temporal realmente vaya a adoptarse

    • Como muchas propuestas prometedoras de JS, lleva mucho tiempo solo en discusión