2 puntos por GN⁺ 2025-01-31 | 1 comentarios | Compartir por WhatsApp
  • El nuevo objeto Temporal de JavaScript está empezando a llegar a versiones experimentales de navegadores; es un cambio que busca manejar fechas, horas, duraciones y calendarios de forma más precisa y consistente mediante una API integrada
  • El Date existente dificultaba el manejo de zonas horarias y la depuración por su estructura centrada en la hora local del usuario y UTC, su parsing poco confiable y su comportamiento mutable
  • Temporal separa instantes, hora local y duraciones con Temporal.Instant, Temporal.ZonedDateTime, Temporal.PlainDateTime, Temporal.Duration, Temporal.Now, entre otros, y ofrece más de 200 métodos utilitarios
  • Aún hará falta más tiempo para contar con soporte estable entre navegadores; en Firefox Nightly se puede probar Temporal detrás de la configuración javascript.options.experimental.temporal
  • Esta semana se agregaron a MDN más de 270 páginas de documentación sobre Temporal, y en la página de documentación de TC39 que incluye @js-temporal/polyfill se pueden ejecutar ejemplos sin cambiar flags

Las antiguas limitaciones de Date en JavaScript

  • Cuando JavaScript se creó en 1995, el objeto Date copió la implementación inicial de java.util.Date de Java; Java la reemplazó en 1997, pero JavaScript mantuvo casi la misma API durante casi 30 años
  • Los problemas más grandes son la falta de soporte de zonas horarias y el comportamiento de parsing poco confiable
    • Solo soporta la hora local del usuario y UTC
    • Como Date en sí es mutable, puede crear bugs difíciles de rastrear
    • También es difícil manejar cálculos que cruzan Daylight Saving Time (DST) y cambios históricos de calendario
  • Debido a estas limitaciones, muchos desarrolladores han dependido de librerías dedicadas como Moment.js o date-fns para el manejo de fechas y horas en sus aplicaciones

Cómo Temporal cambia el modelo de fecha y hora

  • Temporal fue diseñado como un reemplazo completo del objeto Date, con el objetivo de hacer que la gestión de fechas y horas sea más confiable y predecible
  • Soporta representaciones de zonas horarias y calendarios, y ofrece métodos integrados para conversión, comparación, cálculo y formateo
  • La superficie de la API incluye más de 200 métodos utilitarios, y la documentación de Temporal en MDN ya suma explicaciones y ejemplos

Tipos principales de Temporal

  • Temporal distingue entre un instante único, la hora de reloj local y una duración mediante tipos diferentes
  • Sus estructuras principales son las siguientes
    • Temporal.Duration: diferencia entre dos instantes
    • Temporal.Instant: instante único expresado como timestamp
    • Temporal.ZonedDateTime: fecha y hora con zona horaria incluida
    • Temporal.PlainDateTime: fecha y hora completas sin zona horaria
      • Temporal.PlainDate: representa solo la fecha
      • Temporal.PlainYearMonth: año y mes
      • Temporal.PlainMonthDay: mes y día
      • Temporal.PlainTime: representa solo la hora
    • Temporal.Now: API para obtener la hora actual como instancias de distintas clases o en formatos específicos

Uso con ejemplos

  • Se puede obtener la fecha y hora actuales como una cadena ISO, y pasar una zona horaria a varios métodos
    • Temporal.Now.plainDateTimeISO() devuelve la fecha y hora actuales en la zona horaria del sistema
    • Temporal.Now.plainDateTimeISO("America/New_York") devuelve la fecha y hora actuales en la zona horaria de New York
  • También se pueden manejar calendarios distintos del gregoriano, lo que permite crear fechas de sistemas de calendario como Hebrew, Chinese e Islamic
    • Calcula el próximo Chinese New Year usando el día 1 de M01 del calendario Chinese
    • En la salida de ejemplo al momento de redactar, el próximo Chinese New Year es 1/29/2025
  • Un timestamp Unix Epoch en milisegundos se puede convertir en un instante con Temporal.Instant.fromEpochMilliseconds() y comparar con Temporal.Now.instant() para calcular el tiempo restante
    • Para el timestamp de ejemplo 1851222399924, calcula el tiempo desde ahora con smallestUnit: "hour"
    • La salida de @js-temporal/polyfill es 31,600 hr, y la de Firefox Nightly es PT31600H
  • El toLocaleString de la implementación de Firefox actualmente no imprime una cadena sensible al locale
    • Este comportamiento se acerca más a una decisión de diseño que a una limitación técnica, y el formateo de duraciones en sí es posible
    • El polyfill y la implementación de Firefox podrían converger más adelante
  • El método compare() se puede usar para ordenar arreglos
    • Al usar durations.sort(Temporal.Duration.compare), se ordenan como PT1H, PT1H30M, PT1H45M, PT2H

Soporte en navegadores y cómo probarlo

1 comentarios

 
GN⁺ 2025-01-31
Comentarios en Hacker News
  • Temporal es excelente. Lo he usado en producción durante un tiempo con el polyfill, y resuelve muchos de los problemas que tenía con la API tradicional de Date()
    Se siente como una muy buena combinación, con una API cómoda de usar, inspirada en buenas librerías de manejo de tiempo como chrono de Rust y Joda Time de Java
    Separa el tiempo simple, los instantes y el tiempo con zona horaria, así que es un poco más complejo, pero el tiempo en sí ya es complejo, así que los desarrolladores tienen que enfrentar esa complejidad
    Incluso en operaciones como “sumarle un día a este timestamp”, necesariamente hay que decidir si eso se hace según una zona horaria específica; si no, quedan bugs por horario de verano, cambios de zona horaria del usuario o diferencias de zona horaria del servidor
    También resuelve la diferencia de serialización entre un timestamp con offset fijo (2025-01-01T00:00+02:00) y una zona horaria específica (Europe/Paris)

    • También vale la pena ver Jiff, un crate de Rust. Está inspirado en Temporal y además organiza comparaciones con otros crates de fecha y hora, incluido chrono: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
      Personalmente, no creo que Temporal haya estado muy inspirado por chrono de Rust; parece tener mucha más influencia de Joda, del moderno java.time, y de librerías de fecha y hora de JavaScript como date-fns o Moment.js
    • Los que ya tienen cierta edad aprendieron hace mucho a no programar tareas de cron entre la 01:00 y las 03:00 hora local en entornos de producción
    • Es cierto, pero hay un obstáculo. A mucha gente no le gusta usar nombres de zona horaria que apuntan a una ubicación real
      No quieren ponerse a averiguar si Europe/Paris significa una región amplia de Francia o solo París, y les molesta porque sienten que es como decir que viven en París
      Esto fue doloroso al programar cosas como eventos de comunidades en línea. La gente quiere usar offsets fijos, y expresiones como CET/CEST les parecen bien, pero cosas como Europe/yourexactcoordinates no les gustan
      En la práctica saben que la mayoría de las zonas horarias cubren regiones bastante amplias, pero hay mucha gente a la que le preocupa la privacidad y aun así no piensa investigar la diferencia entre “hora” y “zona horaria”
    • chrono y Joda Time son productos de épocas distintas. Cuando salió Rust 1.0, Java ya había implementado este enfoque con java.time en la biblioteca estándar, y no hacía falta una librería de terceros para este propósito
    • Me gustaría entender con más detalle qué problema causa la diferencia de serialización entre un timestamp con offset fijo y uno con una zona horaria específica
  • Me tomó bastante tiempo encontrarlo en la documentación, pero Temporal.ZonedDateTime.prototype.withTimeZone() convierte de una zona horaria a otra: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
    Si tomas un valor creado con Temporal.ZonedDateTime.from("2021-08-01T12:00[America/New_York]") y lo pasas a withTimeZone("Europe/Paris"), se convierte en 2021-08-01T18:00:00+02:00[Europe/Paris]
    La conversión de zona horaria y el manejo de períodos son funciones clave que una librería sí o sí debe cubrir, y está bien que Temporal se encargue de ambas. Eso sí, no parece que Temporal.Duration tenga una función format personalizada para producir algo como 1h03m, por ejemplo duration.format('H\hmm\m')

  • En TC39 hay varias propuestas que ya van madurando, y Temporal es una de las que ojalá salga pronto.
    Como sé que para que los principales navegadores soporten nuevas funciones hace falta el trabajo de ingeniería de la gente de v8, JSC y SpiderMonkey, de verdad agradezco a quienes están impulsando el progreso.
    Desde la impaciencia, uno termina alentando con algo como: “¡Saquen también Records y Tuples de una vez! ¡Incluso pueden saltarse la guerra de sintaxis de Pipe!”

    • Records y Tuples parecen tener pocas probabilidades de lanzarse, o al menos muy pocas de salir con la semántica de igualdad que la gente quiere.
      Entiendo que los fabricantes de navegadores ya se oponen con bastante fuerza a agregar nuevos tipos primitivos en este punto. Las dificultades de cómo debería funcionar la igualdad están explicadas en este hilo: https://github.com/tc39/proposal-record-tuple/issues/387
      Al principio no esperaba que BigInt se usara tanto de esta manera, pero en la práctica siguen apareciendo casos donde hacen falta colecciones con igualdad estructural, así que da pena.
      Tengo entendido que la oposición a añadir nuevos tipos primitivos también afecta al pattern matching, pero no sé bien por qué y sigo menos esa propuesta.
    • Está bien que llegue Temporal y hay muchas otras buenas funciones, pero cuesta ser optimista con respecto a las propuestas específicas que mencioné.
      Pipeline, pattern matching y Records+Tuples llevan más de 4 años en desarrollo y todavía siguen en etapa 1 o 2. En el último año casi no parece haber habido avances significativos, salvo quizá en pattern matching.
      Según los issues del repositorio de Records y Tuples, hubo rechazo a la semántica de valor, es decir, a si === puede funcionar. Si se quita la semántica de valor, la utilidad de la propuesta baja muchísimo.
      Para que lleguen a etapa 3 probablemente falten por lo menos 1 o 2 años más, o quizá nunca lleguen. Aun así, por lo menos ya tenemos las funciones de agrupación de arrays.
    • Lo que quiero es pattern matching: https://tc39.es/proposal-pattern-matching/
      Lamentablemente todavía está en etapa 1, así que parece que faltan varios años más.
    • Hablando de Records, me pregunto si Dictionary fue una propuesta de ECMA5 o si era una función peculiar de AS3.
      El Dictionary de AS3 era un tipo donde se podían usar referencias a objetos, cadenas y números como claves únicas, y asociarles valores de cualquier tipo. El recolector de basura también lo manejaba para que no fueran referencias débiles mientras el propio objeto diccionario siguiera vivo.
      Era como pensar en el Set de JavaScript, pero con cualquier tipo de clave especificado, fuertemente tipado, y lanzando errores en tiempo de compilación. Extraño ese lenguaje.
    • No es solo un problema de los grandes fabricantes de navegadores. Justo cuando estaba saliendo ES6, yo estaba creando un motor de inferencia para JavaScript, y aunque soportaba bastante bien ES5, tuve que abandonarlo cuando empezaron a llegar en masa las actualizaciones de sintaxis.
      Esas actualizaciones, en la práctica, arruinaron el ecosistema de JavaScript, y ahora da la impresión de que solo quedan lenguajes que compilan a JS. Quien todavía usa JS vainilla parece alguien construyendo una app en ensamblador.
  • Me pregunto cómo se mantiene actualizada la información de cada zona horaria dentro de Temporal.
    ¿La estructura es que TC39 actualiza los datos en algún lado y luego cada navegador copia internamente esos datos y saca una nueva versión? ¿Y si el usuario no actualizó a un navegador con los datos nuevos, entonces en mi sitio web verá horas incorrectas?
    Por ejemplo, México eliminó el horario de verano en 2022: https://en.wikipedia.org/wiki/Time_in_Mexico
    En librerías de terceros como pytz o moment-timezone, basta con actualizar la librería del lado del servidor; y si lo maneja la biblioteca estándar, basta con subir la versión del lenguaje, pero no sé cómo funciona con Temporal y el navegador del visitante.

    • Si lees “Time Zones and Resolving Ambiguity” en la documentación de la propuesta, solo dice que usa la base de datos de zonas horarias de IANA, pero no dice cuándo se actualiza: https://tc39.es/proposal-temporal/docs/timezone.html
      Parece probable que la computadora del usuario también pueda acceder a esa base de datos y encargarse de las actualizaciones, pero no estoy seguro. También podría ser que el navegador descargue y mantenga esa base de datos por su cuenta; supongo que dependerá de la implementación.
      Base de datos relacionada: https://en.wikipedia.org/wiki/Tz_database
    • Como será una función integrada en el navegador, probablemente se actualizará con las actualizaciones del navegador. Los proveedores de navegadores han trabajado mucho para que las actualizaciones sean rápidas, y es muy posible que, en la computadora del usuario promedio, el navegador sea el software más actualizado que tiene.
    • Los navegadores ya tienen que mantener actualizados los datos de configuración regional por los helpers de Intl, así que si investigas por ese lado probablemente encuentres una respuesta parecida.
    • Los sistemas tipo Unix, o sea distribuciones de Linux, macOS y Android, tienen una copia a nivel de sistema de la IANA Time Zone Database. No sé si el navegador usa esa copia o trae la suya, pero Temporal definitivamente usa IANA tzdb.
      En general, sí parece correcto decir que si el usuario no actualiza el navegador o el sistema para reflejar los nuevos datos de zonas horarias, podría ver horas incorrectas. Pero esto no sería muy distinto de la práctica común actual.
      La alternativa sería que cada aplicación trajera su propia tzdb, y algunas apps hacen eso y a veces está justificado. Pero en general es un desperdicio, aumenta las inconsistencias entre aplicaciones y además es probable que haga más lento el ciclo de actualizaciones.
    • Va a depender de la implementación y del sistema. Al final, la implementación tendrá que definir un proveedor de zona horaria, y es probable que eso venga de hooks de host/sistema que la implementación del motor le proporciona a Temporal.
  • Habría que agregar un evento para detectar cuando alguien cambie de zona horaria
    A la lista de “mentiras que los programadores creen sobre el tiempo” también se podría sumar “los programadores creen que la zona horaria en uso es fija”. En la realidad, millones de personas cambian de zona horaria todos los días

    • Parece que ese tipo de evento sería otro punto de datos para huellas digitales y estimar la ubicación de una persona
    • No me queda claro para qué se usaría este evento. Pensando en cuántos usuarios se verían afectados en la mayoría de las aplicaciones web que dependen en cierta medida del tiempo, dudo que tenga suficiente valor como para escuchar directamente este evento y reaccionar a él
      Aunque claro, podría ser útil para las librerías
    • La zona horaria del cliente solo debería importarle al cliente. Fuera de la conveniencia de mostrar marcas de tiempo en la hora local del usuario, no hay ninguna condición en la que el reloj del cliente deba volverse importante. Aunque el usuario cambie de zona horaria, no hay motivo para preocuparse
    • Para rastrear cambios de zona horaria se necesita estado, así que parece correcto dejar que el desarrollador defina cómo rastrearlos y reaccionar. Puede que no se quiera que Temporal tenga una opinión sobre cómo manejar eso
    • Si estás en un avión o en la ISS, incluso podrías estar en una situación sin zona horaria
  • Es una gran lástima que .Now no sea una llamada de función. No debería ser una propiedad
    Si evalúas .Now varias veces dentro de un bucle, podrías obtener un valor distinto cada vez, y eso no es lo esperable
    C# tomó la misma decisión y no debería haberlo hecho: https://ericlippert.com/2014/05/19/when-should-i-write-a-pro...

    • Creo que lo entendiste mal. Now es un namespace para funciones que obtienen valores reales; por ejemplo, Now.instant()
  • La propuesta de Temporal realmente me gusta, pero hay una cosa que me molesta. En las comparaciones depende de la igualdad por referencia
    Es decir, Temporal.Instant.from('2020-01-01') != Temporal.Instant.from('2020-01-01')
    No es inherentemente malo, pero prácticamente elimina la posibilidad de usar estos objetos como claves de Map o juntarlos en un Set. Entiendo por qué se tomó esa decisión, pero igual da pena que no sea posible
    Quizá algún día, si Records y Tuples llegan a existir de verdad, aparezca una versión basada en eso

    • ¿Hay algún tipo no primitivo en JavaScript que tenga igualdad no referencial en ==?
    • Dependiendo del caso, parece que se podría usar .epochMilliseconds como clave o elemento en Map/Set. Claro, si necesitas conservar datos de zona horaria en el Map o hacer operaciones adicionales de Instant, entonces tendrías que guardar el Instant original en el valor
      En el caso de Set, podrías usar algo como Map{v.epochMilliseconds:v} para conservar el Instant
      No es ideal, pero esto es más culpa de JavaScript en sí que de Temporal. Podrían hacer que el runtime lo manejara, pero entonces el polyfill fallaría
    • Incluso si se pudiera, usar este tipo de valores en Map/Set es parecido a usar números de punto flotante en Map/Set, y por lo general es una mala idea
    • ¿No será simplemente porque en JavaScript es imposible? Salvo algún hack raro como el pool de cadenas constantes de Java, donde todos los objetos usados sean referencias a valores dentro de un pool global de objetos, parece difícil
    • ¿No bastaría con usar String() o '2020-01-01'?
  • Proveer una función de comparación que funcione junto con las APIs existentes es un diseño sólido. Hasta podría tomarse como requisito obligatorio al agregar nuevos tipos
    Por ejemplo, si pasas Temporal.Duration.compare a sort, puedes ordenar en el orden PT1H, PT1H30M, PT1H45M, PT2H. Aplausos para los diseñadores

  • Entiendo que el nombre Temporal busca evitar choques con objetos de tiempo más genéricos como Moment o Datetime, pero el nombre no me gusta
    La primera vez que lo vi pensé que era algo como control del recolector de basura

    • temporal puede significar algo relativo al tiempo en contraste con la eternidad, algo relacionado con los tiempos gramaticales o la distinción temporal, o algo relativo al tiempo en contraste con el espacio: https://www.merriam-webster.com/dictionary/temporal
      Me suena a un nombre razonable
    • Pensé que se refería a Temporal, el motor de workflows dentro del navegador
    • Aunque esto ya es discutir el color del cobertizo sobre una decisión tomada, coincido en que es un nombre realmente malo
      Para empezar, Temporal no es un sustantivo sino un adjetivo. Tiene relación con el tiempo, pero no encaja de forma intuitiva
      Más importante aún, elegir nombres raros para reducir la probabilidad de chocar con código viejo hace que, con el tiempo, el lenguaje se vuelva más opaco
      Cuando se agregaron Promise y Generator, también había muchas librerías que usaban esos nombres de objetos. A diferencia de las palabras reservadas, los objetos integrados se pueden sobrescribir, así que no era un gran problema
      Creo que el comité de estándares debería haberse mantenido firme y usar Time por la salud a largo plazo del lenguaje
      Claro, esa discusión ya pasó y seguramente los fabricantes de navegadores y quienes implementan motores de JS ya se pusieron de acuerdo
    • A mí también me parece un nombre horrible. Preferiría DateV2
      Entiendo que se necesita compatibilidad hacia atrás, pero Temporal suena mal. DateTime ni siquiera es un nombre del estándar de JS, así que no sé por qué no usaron ese. Hasta entonces, probablemente deje const DateTime = Temporal
  • Artículos relacionados:
    JavaScript Temporal Is Coming - https://news.ycombinator.com/item?id=42809834 - enero de 2025, 18 comentarios
    Mozilla: Temporal (Limited Availability) - https://news.ycombinator.com/item?id=42776548 - enero de 2025, 1 comentario
    Is It Time for the JavaScript Temporal API? - https://news.ycombinator.com/item?id=29712118 - diciembre de 2021, 100 comentarios
    Temporal: Getting started with JavaScript's new date time API - https://news.ycombinator.com/item?id=27661667 - junio de 2021, 194 comentarios
    JavaScript Temporal - https://news.ycombinator.com/item?id=27395236 - junio de 2021, 1 comentario
    JavaScript Proposal Temporal for Dates is now stage 3 - https://news.ycombinator.com/item?id=26432346 - marzo de 2021, 1 comentario

    • Aunque se agradece que últimamente las Web API hayan mejorado muchísimo, igual da pena que la primera publicación sobre este tema fuera hace casi 4 años, en marzo de 2021
      Le queda perfecto eso de que los barcos grandes tardan en cambiar de rumbo