JavaScript Temporal arrive
(developer.mozilla.org)- 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
Daterendait 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
Datea copié la première implémentationjava.util.Datede 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
Datelui-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 instantsTemporal.Instant: instant unique représenté par un timestampTemporal.ZonedDateTime: date et heure avec fuseau horaireTemporal.PlainDateTime: date et heure complètes sans fuseau horaireTemporal.PlainDate: date seuleTemporal.PlainYearMonth: année et moisTemporal.PlainMonthDay: mois et jourTemporal.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èmeTemporal.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
M01dans le calendrier chinois - Dans la sortie d’exemple au moment de la rédaction, le prochain Nouvel An chinois est le
1/29/2025
- Le prochain Nouvel An chinois est calculé en utilisant le jour 1 de
- 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é avecsmallestUnit: "hour" - La sortie de
@js-temporal/polyfillest31,600 hr, tandis que celle de Firefox Nightly estPT31600H
- Pour le timestamp d’exemple
- L’implémentation Firefox de
toLocaleStringne 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’ordrePT1H,PT1H30M,PT1H45M,PT2H
- Avec
Prise en charge par les navigateurs et méthode de test
- La prise en charge de Temporal commence à être intégrée progressivement dans les versions expérimentales des navigateurs, et l’implémentation de Firefox semble actuellement la plus mature
- Dans Firefox, Temporal est compilé dans la version Nightly derrière le paramètre
javascript.options.experimental.temporal - Les principaux tickets de suivi d’implémentation des navigateurs sont les suivants
- Firefox : Build temporal in Nightly by default
- Safari : [JSC] Implement Temporal
- Chrome : Implement the Temporal proposal
- L’état complet de compatibilité est disponible dans la section Browser Compatibility de l’objet Temporal sur MDN
- La documentation TC39 de Temporal inclut
@js-temporal/polyfill, ce qui permet d’exécuter les exemples dans la console des outils de développement de n’importe quel navigateur sans modifier de flag ni de paramètre
1 commentaires
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
chronoen 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).chrono: https://docs.rs/jiff/latest/jiff/_documentation/comparison/i...Personnellement, je ne dirais pas que Temporal s’est fortement inspiré de
chronoen Rust ; l’influence semble plutôt venir de Joda, dejava.timeplus récemment, ainsi que de bibliothèques JavaScript de date/heure comme date-fns ou Moment.js.Ils ne cherchent pas à savoir si
Europe/Parisdé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 genreEurope/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 ».
chronoet 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.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 obtient2021-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.Durationne semble pas proposer de fonctionformatpersonnalisée permettant de produire quelque chose comme1h03m, par exemple avecduration.format('H\hmm\m').1 yr,3 hoursou3m: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...J’en avais implémenté une partie dans la bibliothèque icu4x.
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 ! »
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
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 fortementIl 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
Malheureusement, ce n’est encore qu’à l’étape 1, donc il faudra probablement encore plusieurs années
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
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
pytzoumoment-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 visiteurL’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
Intl, donc en regardant de ce côté-là on trouvera probablement une réponse similairetzdbIANAIl 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 lentIl 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
Bien sûr, cela pourrait aider les bibliothèques
Le fait que
.Nowne soit pas un appel de fonction est très regrettable. Cela ne devrait pas être une propriétéSi
.Nowest évalué plusieurs fois dans une boucle, il peut produire une valeur différente à chaque fois, ce qui est inattenduC# a fait le même choix, et n’aurait pas dû : https://ericlippert.com/2014/05/19/when-should-i-write-a-pro...
Nowest un namespace pour les fonctions qui récupèrent les valeurs réelles, par exempleNow.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
Mapou de les collecter dans desSet. Je comprends pourquoi cette décision a été prise, mais je regrette que ce ne soit pas possibleSi Records et Tuples finissent un jour par vraiment arriver, il y aura peut-être une version basée dessus
==?.epochMillisecondssemble pouvoir servir de clé ou d’élément dans unMap/Set. Cela dit, si l’on doit conserver les données de fuseau horaire dans leMapou effectuer des opérations supplémentaires sur l’Instant, il faut conserver l’Instantd’origine côté valeurPour un
Set, on peut utiliser quelque chose commeMap{v.epochMilliseconds:v}afin de conserver l’InstantCe 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
Map/Setressemble à utiliser des nombres à virgule flottante dans unMap/Set, et c’est généralement une mauvaise idéeString()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’ordrePT1H,PT1H30M,PT1H45M,PT2H. Bravo aux concepteursJe comprends que le nom
Temporalvise à éviter les collisions avec des objets de temps plus génériques comme Moment ou Datetime, mais je n’aime pas ce nomLa première fois que je l’ai vu, j’ai cru que c’était quelque chose comme du contrôle du garbage collection
temporalpeut 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
D’abord,
Temporaln’est pas un nom, mais un adjectif. C’est lié au temps, mais ça ne tombe pas intuitivement justePlus 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
PromiseetGeneratoront é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
TimeBien 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
DateV2serait presque mieuxJe comprends le besoin de rétrocompatibilité, mais
Temporalsonne mal.DateTimen’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 probablementconst DateTime = TemporalArticles 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
L’expression « les gros navires mettent du temps à virer » n’a jamais été aussi juste