Formatage de dates correct par fuseau en JavaScript sans bibliothèques
Le Intl.DateTimeFormat intégré de JavaScript gère le formatage des fuseaux IANA sans moment.js ni date-fns. Apprenez les options timeZone explicites, l'affichage sûr face à l'heure d'été, formatToParts, les limites de la conversion d'horloge et quand Temporal ou une bibliothèque de fuseaux reste utile.
Pourquoi Date n'a pas de propriété de fuseau
Un Date JavaScript est toujours un compte de millisecondes UTC. Aucun fuseau n'est stocké dans l'objet. Quand vous appelez .toString() ou .toLocaleString() sans options, JavaScript utilise le fuseau local du runtime depuis le système d'exploitation. Le même code produit une sortie différente sur un serveur à New York et un portable à Tokyo, même si le timestamp sous-jacent est identique.
Formater avec Intl.DateTimeFormat
Intl.DateTimeFormat est l'API intégrée pour un formatage tenant compte de la locale et du fuseau. Elle accepte les identifiants de fuseau IANA, gère automatiquement les transitions d'heure d'été et est disponible dans les navigateurs modernes et Node.js. La clé est de passer l'option timeZone explicitement au lieu de se fier au défaut du runtime.
- new Intl.DateTimeFormat('en-US', { timeZone: 'America/New_York', dateStyle: 'full', timeStyle: 'long' }).format(date)
- date.toLocaleString('en-GB', { timeZone: 'Europe/London', hour12: false })
- date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' })
- new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', hour12: false }).format(date)
Extraire des parties individuelles avec formatToParts
Utilisez formatToParts() pour obtenir les composants individuels de la date sous forme d'objets {type, value} et construire des chaînes de date personnalisées. C'est mieux que de découper une chaîne de date localisée car la ponctuation, l'ordre et les écritures diffèrent selon la locale.
- const parts = new Intl.DateTimeFormat('en-US', { timeZone: 'America/Chicago', year: 'numeric', month: '2-digit', day: '2-digit' }).formatToParts(date)
- parts.find(p => p.type === 'year').value → '2023'
- parts.find(p => p.type === 'month').value → '11'
- Object.fromEntries(parts.map(p => [p.type, p.value])) → { year, month, day, hour, minute, second }
Convertir l'heure murale en UTC (le sens difficile)
Passer d'une heure murale dans un fuseau donné à UTC est plus difficile avec l'API Date classique. Formater un instant en America/New_York est facile ; construire l'instant représenté par 2026-03-08 02:30 en America/New_York ne l'est pas, car cette heure locale peut être sautée ou répétée pendant les transitions d'heure d'été.
- Temporal a atteint l'Étape 4 en 2026, mais le support natif des navigateurs n'est toujours pas universel
- Pour une utilisation en production sur tous les navigateurs aujourd'hui, le polyfill Temporal ou date-fns-tz toDate() restent des choix pratiques
- Évitez l'arithmétique manuelle d'offset UTC — les transitions d'heure d'été surviennent à des heures locales différentes selon les années
- Le zonedToEpochMs() de ce site utilise une correction d'offset en une itération — voir src/timeUtils.ts
Choisir le bon identifiant de fuseau
Utilisez des noms de fuseau IANA comme America/New_York, Europe/London, Asia/Tokyo ou UTC. Évitez les offsets fixes comme UTC-5 pour l'heure côté utilisateur car les offsets changent avec l'heure d'été. America/New_York peut être UTC-5 en janvier et UTC-4 en juillet ; le nom IANA permet à la plateforme d'appliquer la bonne règle historique pour la date sélectionnée.
- Bon : America/Los_Angeles — inclut les règles d'heure d'été historiques et futures
- Bon : Europe/Berlin — gère l'heure d'été d'Europe centrale automatiquement
- Bon : Asia/Shanghai — affichage stable UTC+8 sans heure d'été
- Utilisez UTC pour les logs, le stockage d'API, les timestamps de base et la comparaison entre régions
- Utilisez le fuseau IANA de l'utilisateur pour l'affichage final dans l'UI du produit
FAQ sur le formatage des fuseaux
- JavaScript peut-il formater une date dans un autre fuseau sans bibliothèque ?
- Oui. Utilisez Intl.DateTimeFormat ou toLocaleString avec une option timeZone, comme America/New_York ou Asia/Tokyo.
- Intl.DateTimeFormat gère-t-il l'heure d'été ?
- Oui. Quand vous utilisez un identifiant de fuseau IANA, le runtime applique le bon offset pour ce fuseau et cette date.
- Dois-je stocker des offsets de fuseau ou des noms de fuseau ?
- Stockez UTC pour les instants des événements. Si vous avez besoin du contexte local de l'utilisateur, stockez un nom de fuseau IANA comme America/New_York, pas seulement un offset numérique.
- Comment obtenir le fuseau de l'utilisateur en JavaScript ?
- Utilisez Intl.DateTimeFormat().resolvedOptions().timeZone, qui renvoie un nom IANA comme America/New_York. Stockez ce nom, pas un offset numérique, quand vous devez reconstruire plus tard l'heure locale de l'utilisateur.