2 points par GN⁺ 2025-01-31 | 1 commentaires | Partager sur WhatsApp
  • Le nouvel objet Temporal de JavaScript commence à arriver dans les versions expérimentales des navigateurs ; il marque une évolution visant à gérer plus précisément et plus systématiquement les dates, heures, durées et calendriers via une API intégrée
  • L’ancien Date rendait la gestion des fuseaux horaires et le débogage difficiles en raison de sa structure centrée sur l’heure locale de l’utilisateur et UTC, de son parsing instable et de son comportement mutable
  • Temporal sépare les instants, heures locales et durées avec Temporal.Instant, Temporal.ZonedDateTime, Temporal.PlainDateTime, Temporal.Duration, Temporal.Now, etc., et fournit plus de 200 méthodes utilitaires
  • Il faudra encore du temps avant une prise en charge stable sur tous les navigateurs ; dans Firefox Nightly, Temporal peut être testé derrière le paramètre javascript.options.experimental.temporal
  • Cette semaine, plus de 270 pages de documentation Temporal ont été ajoutées à MDN, et les exemples peuvent être exécutés sans modifier de flag depuis la page de documentation TC39 qui inclut @js-temporal/polyfill

Les anciennes limites de JavaScript Date

  • Lorsque JavaScript a été créé en 1995, l’objet Date a copié la première implémentation java.util.Date de Java ; Java l’a remplacée en 1997, mais JavaScript a conservé quasiment la même API pendant près de 30 ans
  • Les plus gros problèmes sont le manque de prise en charge des fuseaux horaires et un comportement de parsing instable
    • Seuls l’heure locale de l’utilisateur et UTC sont pris en charge
    • Date lui-même est mutable, ce qui peut créer des bugs difficiles à suivre
    • Les calculs qui traversent l’heure d’été (Daylight Saving Time, DST) et les changements historiques de calendrier sont également difficiles à gérer
  • En raison de ces contraintes, de nombreux développeurs se sont appuyés sur des bibliothèques dédiées comme Moment.js ou date-fns pour gérer les dates et heures dans leurs applications

Le modèle date-heure que Temporal change

  • Temporal a été conçu comme un remplacement complet de l’objet Date, avec pour objectif de rendre la gestion des dates et heures plus fiable et plus prévisible
  • Il prend en charge la représentation des fuseaux horaires et des calendriers, et fournit des méthodes intégrées pour la conversion, la comparaison, le calcul et le formatage
  • La surface d’API compte plus de 200 méthodes utilitaires, avec explications et exemples ajoutés dans la documentation Temporal de MDN

Les principaux types de Temporal

  • Temporal distingue les instants uniques, l’heure murale locale et les durées au moyen de types différents
  • Les principales structures sont les suivantes
    • Temporal.Duration : différence entre deux instants
    • Temporal.Instant : instant unique représenté par un timestamp
    • Temporal.ZonedDateTime : date et heure avec fuseau horaire
    • Temporal.PlainDateTime : date et heure complètes sans fuseau horaire
      • Temporal.PlainDate : date seule
      • Temporal.PlainYearMonth : année et mois
      • Temporal.PlainMonthDay : mois et jour
      • Temporal.PlainTime : heure seule
    • Temporal.Now : API permettant d’obtenir l’heure actuelle sous forme d’instances de différentes classes ou dans des formats spécifiques

Exemples d’utilisation

  • Il est possible d’obtenir la date et l’heure actuelles sous forme de chaîne ISO, et de passer un fuseau horaire à plusieurs méthodes
    • Temporal.Now.plainDateTimeISO() renvoie la date et l’heure actuelles dans le fuseau horaire du système
    • Temporal.Now.plainDateTimeISO("America/New_York") renvoie la date et l’heure actuelles dans le fuseau horaire de New York
  • Il est aussi possible de gérer des calendriers autres que le calendrier grégorien, afin de créer des dates dans des systèmes calendaires comme les calendriers hébraïque, chinois ou islamique
    • Le prochain Nouvel An chinois est calculé en utilisant le jour 1 de M01 dans le calendrier chinois
    • Dans la sortie d’exemple au moment de la rédaction, le prochain Nouvel An chinois est le 1/29/2025
  • Un timestamp Unix Epoch en millisecondes peut être transformé en instant avec Temporal.Instant.fromEpochMilliseconds(), puis comparé à Temporal.Now.instant() pour calculer le temps restant
    • Pour le timestamp d’exemple 1851222399924, le temps depuis maintenant est calculé avec smallestUnit: "hour"
    • La sortie de @js-temporal/polyfill est 31,600 hr, tandis que celle de Firefox Nightly est PT31600H
  • L’implémentation Firefox de toLocaleString ne produit actuellement pas de chaîne dépendante de la locale
    • Ce comportement relève davantage d’un choix de conception que d’une limite technique, et le formatage des durées lui-même est possible
    • Le polyfill et l’implémentation Firefox pourraient converger ultérieurement
  • La méthode compare() peut être utilisée pour trier des tableaux
    • Avec durations.sort(Temporal.Duration.compare), le tri donne l’ordre PT1H, PT1H30M, PT1H45M, PT2H

Prise en charge par les navigateurs et méthode de test

1 commentaires

 
GN⁺ 2025-01-31
Avis sur Hacker News
  • Temporal est excellent. Je l’utilise depuis un moment en production via un polyfill, et il règle beaucoup de problèmes rencontrés avec l’API Date() existante.
    On a l’impression qu’il s’inspire de bonnes bibliothèques de gestion du temps comme chrono en Rust ou Joda Time en Java, et qu’il les combine bien dans une API pratique à utiliser.
    C’est un peu plus complexe parce qu’il sépare l’heure simple, l’instant et l’heure avec fuseau horaire, mais le temps est intrinsèquement complexe, donc les développeurs doivent faire face à cette complexité.
    Même une opération comme « ajouter un jour à cet horodatage » impose de décider si elle se fait par rapport à un fuseau horaire donné ; sinon, il restera des bugs dus à l’heure d’été, aux changements de fuseau horaire de l’utilisateur ou aux différences de fuseau horaire côté serveur.
    Il résout aussi la différence de sérialisation entre un horodatage à décalage fixe (2025-01-01T00:00+02:00) et un fuseau horaire précis (Europe/Paris).

    • Le crate Rust Jiff vaut aussi le détour. Il s’inspire de Temporal et propose aussi une comparaison avec d’autres crates de date/heure, dont chrono : https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...
      Personnellement, je ne dirais pas que Temporal s’est fortement inspiré de chrono en Rust ; l’influence semble plutôt venir de Joda, de java.time plus récemment, ainsi que de bibliothèques JavaScript de date/heure comme date-fns ou Moment.js.
    • Les personnes d’un certain âge ont appris il y a longtemps qu’en production, il ne faut pas planifier de tâches cron entre 01:00 et 03:00 en heure locale.
    • C’est juste, mais il y a un obstacle. Beaucoup de gens n’aiment pas utiliser des noms de fuseaux horaires qui désignent un lieu réel.
      Ils ne cherchent pas à savoir si Europe/Paris désigne une grande partie de la France ou seulement Paris, et cela les agace parce qu’ils ont l’impression de dire qu’ils vivent à Paris.
      Lorsqu’il s’agissait de planifier des événements pour des communautés en ligne, cela a été pénible. Les gens veulent utiliser des décalages fixes, acceptent des formulations comme CET/CEST, mais n’aiment pas les choses du genre Europe/voscoordonnéesexactes.
      En réalité, je sais que la plupart des fuseaux horaires couvrent des zones assez larges, mais beaucoup de personnes se soucient de leur vie privée tout en n’ayant pas l’idée de chercher la différence entre « heure » et « fuseau horaire ».
    • chrono et Joda Time appartiennent à des époques différentes. Quand Rust 1.0 est sorti, Java avait déjà implémenté cette approche dans sa bibliothèque standard avec java.time, et il n’y avait plus besoin d’une bibliothèque tierce pour cet usage.
    • J’aimerais en savoir plus sur le problème exact posé par la différence de sérialisation entre un horodatage à décalage fixe et un horodatage avec fuseau horaire précis.
  • Il m’a fallu pas mal de temps pour le trouver dans la documentation, mais Temporal.ZonedDateTime.prototype.withTimeZone() permet de convertir d’un fuseau horaire à un autre : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
    Si l’on prend une valeur créée avec Temporal.ZonedDateTime.from("2021-08-01T12:00[America/New_York]") et qu’on la passe à withTimeZone("Europe/Paris"), on obtient 2021-08-01T18:00:00+02:00[Europe/Paris].
    La conversion de fuseaux horaires et la gestion des durées sont des fonctionnalités essentielles qu’une bibliothèque doit absolument couvrir, et c’est appréciable que Temporal gère les deux. En revanche, Temporal.Duration ne semble pas proposer de fonction format personnalisée permettant de produire quelque chose comme 1h03m, par exemple avec duration.format('H\hmm\m').

  • En ce moment, il y a pas mal de propositions qui mûrissent au TC39, et Temporal fait partie de celles qu’on aimerait voir arriver vite
    Je sais que, pour que les grands navigateurs prennent en charge de nouvelles fonctionnalités, il faut un effort de développement de la part des ingénieurs de v8, JSC et SpiderMonkey, donc je suis vraiment reconnaissant envers les personnes qui font avancer les choses
    Du point de vue de quelqu’un d’impatient, on a envie de les encourager en disant : « Dépêchez-vous aussi pour Records et Tuples ! Vous pouvez passer la guerre de syntaxe sur Pipe ! »

    • Records et Tuples ont peu de chances de sortir, ou du moins très peu de chances de sortir avec la sémantique d’égalité que les gens veulent
      Si je comprends bien, les éditeurs de navigateurs s’opposent déjà assez fortement, à ce stade, à l’ajout de nouveaux types primitifs. Les difficultés liées au fonctionnement attendu de l’égalité sont expliquées dans ce fil : https://github.com/tc39/proposal-record-tuple/issues/387
      Au départ, je ne pensais pas que BigInt serait autant utilisé, mais en pratique on se retrouve régulièrement à avoir besoin de collections avec égalité structurelle, donc c’est dommage
      Il me semble que l’opposition à l’ajout de nouveaux types primitifs affecte aussi le pattern matching, mais je ne sais pas bien pourquoi, et je suis moins cette proposition
    • C’est une bonne chose que Temporal arrive, et il y aura sans doute beaucoup d’autres bonnes fonctionnalités, mais il est difficile d’être optimiste concernant les propositions précises mentionnées
      Pipeline, pattern matching et Records+Tuples sont tous en chantier depuis plus de quatre ans et restent encore aux étapes 1 ou 2. Sur la dernière année, il ne semble pas y avoir eu beaucoup de progrès significatifs, à part peut-être pour le pattern matching
      D’après les issues du dépôt Records et Tuples, il y a eu des résistances autour de la sémantique par valeur, c’est-à-dire sur la possibilité de faire fonctionner ===. Sans sémantique par valeur, l’utilité de la proposition diminue fortement
      Il leur faudra au minimum encore un ou deux ans pour atteindre l’étape 3, voire elles pourraient ne jamais y arriver. Cela dit, les fonctions de groupement de tableaux sont bien arrivées
    • Ce que je veux, c’est le pattern matching : https://tc39.es/proposal-pattern-matching/
      Malheureusement, ce n’est encore qu’à l’étape 1, donc il faudra probablement encore plusieurs années
    • À propos de Records, je me demande si Dictionary était une proposition ECMA5 ou simplement une particularité d’AS3
      Le Dictionary d’AS3 était un type qui permettait d’utiliser des références d’objets, des chaînes et des nombres comme clés uniques, et d’y associer des valeurs de n’importe quel type. Le ramasse-miettes était aussi géré de manière à ce que, tant que l’objet dictionnaire lui-même était vivant, les références ne soient pas faibles
      Pensez au Set de JavaScript, mais avec des clés fortement typées du type que vous avez spécifié, et des erreurs levées à la compilation. Cette langue me manque
    • Ce n’est pas seulement un problème lié aux principaux fabricants de navigateurs. À l’époque où ES6 était sur le point de sortir, je développais un moteur d’inférence JavaScript et je prenais assez bien en charge ES5, mais j’ai dû abandonner face à l’avalanche de mises à jour syntaxiques
      Ces mises à jour ont en pratique cassé l’écosystème JavaScript, et on a l’impression qu’il ne reste plus que des langages qui se compilent vers JS. Quelqu’un qui écrit encore du JS vanilla ressemble à quelqu’un qui développe une application en assembleur
  • Je me demande comment les données de chaque fuseau horaire sont maintenues à jour dans Temporal
    Est-ce que TC39 met à jour les données quelque part, puis chaque navigateur les copie en interne et publie une nouvelle version ? Si l’utilisateur n’a pas mis à jour son navigateur avec les nouvelles données, verra-t-il une heure incorrecte sur mon site ?
    Par exemple, le Mexique a supprimé l’heure d’été en 2022 : https://en.wikipedia.org/wiki/Time_in_Mexico
    Avec des bibliothèques tierces comme pytz ou moment-timezone, il suffit de mettre à jour la bibliothèque côté serveur, et si c’est géré par la bibliothèque standard, on met à jour la version du langage ; mais je ne sais pas comment cela se passe avec Temporal et le navigateur du visiteur

    • En lisant la section « Time Zones and Resolving Ambiguity » du document de proposition, on voit seulement qu’elle utilise la base de données des fuseaux horaires IANA, sans préciser quand elle est mise à jour : https://tc39.es/proposal-temporal/docs/timezone.html
      L’ordinateur de l’utilisateur peut lui aussi accéder à cette base de données et gérer les mises à jour, mais je n’en suis pas certain. Le navigateur pourrait aussi télécharger et maintenir séparément cette base de données ; cela dépendra sans doute de l’implémentation
      Base de données associée : https://en.wikipedia.org/wiki/Tz_database
    • Comme c’est une fonctionnalité intégrée au navigateur, elle sera probablement actualisée lors des mises à jour du navigateur. Les éditeurs de navigateurs ont beaucoup travaillé pour accélérer les mises à jour, et sur l’ordinateur d’un utilisateur moyen, le navigateur est probablement l’un des logiciels les mieux tenus à jour
    • Les navigateurs doivent déjà maintenir les locales à jour à cause des helpers Intl, donc en regardant de ce côté-là on trouvera probablement une réponse similaire
    • Les systèmes de type Unix, c’est-à-dire les distributions Linux, macOS et Android, disposent d’une copie système de la IANA Time Zone Database. Je ne sais pas si les navigateurs l’utilisent ou embarquent leur propre copie, mais Temporal utilise clairement la tzdb IANA
      Il semble globalement vrai que, si l’utilisateur ne met pas à jour son navigateur ou son système pour intégrer les nouvelles données de fuseaux horaires, il peut voir une heure incorrecte. Cela dit, ce n’est pas très différent des pratiques courantes actuelles
      L’alternative serait que chaque application embarque sa propre tzdb ; certaines applications le font, et c’est justifié dans certains cas. Mais en général, c’est du gaspillage, cela augmente les incohérences entre applications et le cycle de mise à jour risque d’être encore plus lent
    • Cela dépendra de l’implémentation et du système. L’implémentation devra au final définir un fournisseur de fuseaux horaires, qui sera probablement fourni par les hooks hôte/système que l’implémentation du moteur expose à Temporal
  • Il faudrait ajouter un événement permettant de détecter quand quelqu’un change de fuseau horaire
    À la liste des « fausses croyances des programmeurs sur le temps », on pourrait aussi ajouter « les programmeurs pensent que le fuseau horaire reste fixe pendant l’utilisation ». Dans la réalité, des centaines de millions de personnes changent de fuseau horaire chaque jour

    • Un tel événement risque de servir de point de données supplémentaire pour fingerprinter les gens et estimer leur position
    • Je ne vois pas très bien à quoi servirait cet événement. Pour la plupart des applications web qui dépendent dans une certaine mesure de l’heure, vu le nombre d’utilisateurs concernés, je doute que cela vaille la peine d’écouter directement cet événement et d’y réagir
      Bien sûr, cela pourrait aider les bibliothèques
    • Le fuseau horaire du client ne devrait compter que pour le client. En dehors du confort consistant à afficher des horodatages dans l’heure locale de l’utilisateur, il n’y a aucune condition où l’horloge du client devient importante. Si l’utilisateur change de fuseau horaire, il n’y a aucune raison de s’en soucier
    • Suivre un changement de fuseau horaire nécessite de l’état ; il semble donc préférable que les développeurs définissent eux-mêmes comment le suivre et y réagir. On peut ne pas vouloir que Temporal ait une opinion sur cette gestion
    • Si l’on est dans un avion ou sur l’ISS, on peut même se retrouver sans fuseau horaire du tout
  • Le fait que .Now ne soit pas un appel de fonction est très regrettable. Cela ne devrait pas être une propriété
    Si .Now est évalué plusieurs fois dans une boucle, il peut produire une valeur différente à chaque fois, ce qui est inattendu
    C# a fait le même choix, et n’aurait pas dû : https://ericlippert.com/2014/05/19/when-should-i-write-a-pro...

    • Je pense que tu as mal compris. Now est un namespace pour les fonctions qui récupèrent les valeurs réelles, par exemple Now.instant()
  • J’aime beaucoup la proposition Temporal, mais un point me gêne : elle repose sur l’égalité par référence pour les comparaisons
    Autrement dit, Temporal.Instant.from('2020-01-01') != Temporal.Instant.from('2020-01-01')
    Ce n’est pas intrinsèquement mauvais, mais cela supprime en pratique la possibilité d’utiliser ces objets comme clés de Map ou de les collecter dans des Set. Je comprends pourquoi cette décision a été prise, mais je regrette que ce ne soit pas possible
    Si Records et Tuples finissent un jour par vraiment arriver, il y aura peut-être une version basée dessus

    • Parmi les types non primitifs de JavaScript, y en a-t-il qui aient une égalité non basée sur la référence avec == ?
    • Selon le contexte, .epochMilliseconds semble pouvoir servir de clé ou d’élément dans un Map/Set. Cela dit, si l’on doit conserver les données de fuseau horaire dans le Map ou effectuer des opérations supplémentaires sur l’Instant, il faut conserver l’Instant d’origine côté valeur
      Pour un Set, on peut utiliser quelque chose comme Map{v.epochMilliseconds:v} afin de conserver l’Instant
      Ce n’est pas idéal, mais c’est un problème qu’il faut reprocher à JavaScript lui-même plutôt qu’à Temporal. On pourrait aussi faire gérer cela par le runtime, mais alors le polyfill échouerait
    • Même si c’était possible, utiliser de telles valeurs dans un Map/Set ressemble à utiliser des nombres à virgule flottante dans un Map/Set, et c’est généralement une mauvaise idée
    • N’est-ce pas tout simplement impossible en JavaScript ? À moins d’un hack étrange à la manière du pool de chaînes constantes de Java, où tous les objets utilisés seraient des références vers des valeurs d’un pool global d’objets
    • Pourquoi ne pas simplement utiliser String() ou '2020-01-01' ?
  • Fournir une fonction de comparaison qui fonctionne avec les API existantes est une conception solide. Cela pourrait presque devenir un critère obligatoire lorsqu’on ajoute un nouveau type
    Par exemple, en passant Temporal.Duration.compare à sort, on peut trier dans l’ordre PT1H, PT1H30M, PT1H45M, PT2H. Bravo aux concepteurs

  • Je comprends que le nom Temporal vise à éviter les collisions avec des objets de temps plus génériques comme Moment ou Datetime, mais je n’aime pas ce nom
    La première fois que je l’ai vu, j’ai cru que c’était quelque chose comme du contrôle du garbage collection

    • temporal peut signifier ce qui concerne le temps par opposition à l’éternité, ce qui relève des temps grammaticaux ou des distinctions temporelles, ou ce qui concerne le temps par opposition à l’espace : https://www.merriam-webster.com/dictionary/temporal
      Ça me paraît être un nom correct
    • J’ai cru que c’était Temporal, le moteur de workflows, dans le navigateur
    • C’est un peu une discussion sur la couleur du local à vélos pour une décision déjà prise, mais je suis d’accord pour dire que c’est vraiment un mauvais nom
      D’abord, Temporal n’est pas un nom, mais un adjectif. C’est lié au temps, mais ça ne tombe pas intuitivement juste
      Plus important encore, choisir un nom bizarre pour réduire la probabilité de collision avec du vieux code rend le langage de plus en plus opaque avec le temps
      Quand Promise et Generator ont été ajoutés, beaucoup de bibliothèques utilisaient aussi ces noms d’objets. Contrairement aux mots réservés, les objets intégrés peuvent être écrasés, donc ce n’était pas un gros problème
      À mon avis, pour la santé à long terme du langage, le comité de standardisation aurait dû tenir bon et utiliser Time
      Bien sûr, ce débat est déjà passé et les éditeurs de navigateurs comme les implémenteurs de moteurs JS se seront mis d’accord
    • Je trouve aussi que c’est un nom affreux. DateV2 serait presque mieux
      Je comprends le besoin de rétrocompatibilité, mais Temporal sonne mal. DateTime n’est même pas un nom présent dans le standard JS, donc je ne vois pas pourquoi ils ne l’ont pas utilisé. D’ici là, je garderai probablement const DateTime = Temporal
  • Articles associés :
    JavaScript Temporal Is Coming - https://news.ycombinator.com/item?id=42809834 - janvier 2025, 18 commentaires
    Mozilla: Temporal (Limited Availability) - https://news.ycombinator.com/item?id=42776548 - janvier 2025, 1 commentaire
    Is It Time for the JavaScript Temporal API? - https://news.ycombinator.com/item?id=29712118 - décembre 2021, 100 commentaires
    Temporal: Getting started with JavaScript's new date time API - https://news.ycombinator.com/item?id=27661667 - juin 2021, 194 commentaires
    JavaScript Temporal - https://news.ycombinator.com/item?id=27395236 - juin 2021, 1 commentaire
    JavaScript Proposal Temporal for Dates is now stage 3 - https://news.ycombinator.com/item?id=26432346 - mars 2021, 1 commentaire

    • On ne peut que se réjouir de voir à quel point les Web API sont devenues excellentes ces derniers temps, mais il reste dommage que la première soumission de ce sujet remonte à mars 2021, il y a presque 4 ans
      L’expression « les gros navires mettent du temps à virer » n’a jamais été aussi juste