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
Two daylight-saving transitions a year break the assumption that "wall-clock time + timezone = a unique moment". Both are encoded in the IANA timezone database (still often called the Olson database) — the authoritative source of timezone rules used by every modern OS, browser, and language runtime.
- DST gap: 02:00 to 02:59 local time does not exist on the spring-forward day
- DST overlap: 01:00 to 01:59 local time occurs twice on the fall-back day
- Ambiguous local time: in the overlap window you must pick "earlier" or "later" explicitly
- Python uses a fold flag; JS Temporal uses a disambiguation option; java.time defaults to the earlier offset
- Olson database / IANA tzdata / zoneinfo are three names for the same dataset, updated several times a year
- Each IANA name (America/Los_Angeles, Europe/Berlin) maps to a list of (transition, offset, abbreviation) tuples
- Stale tzdata on a device produces silently-wrong local times after any DST or zone change
- Server best practice: do all date arithmetic in UTC, convert to local only at display
FAQ
- Can JavaScript format a date in another timezone without a library?
- Yes. Use Intl.DateTimeFormat or toLocaleString with a timeZone option, such as America/New_York or Asia/Tokyo.
- Does Intl.DateTimeFormat handle daylight saving time?
- Yes. When you use an IANA timezone identifier, the runtime applies the correct offset for that timezone and date.
- Should I store timezone offsets or timezone names?
- Store UTC for event instants. If you need the user's local context, store an IANA timezone name such as America/New_York, not only a numeric offset.
- How do I get the user's timezone in JavaScript?
- Use Intl.DateTimeFormat().resolvedOptions().timeZone, which returns an IANA name like America/New_York. Store that name, not a numeric offset, when you need to reconstruct the user's local time later.
- What is the DST gap (spring-forward)?
- The DST gap is the wall-clock hour that is skipped when daylight saving time begins. In most US zones, when the clock would tick from 02:00 to 02:59 it instead jumps to 03:00. Local times in that window do not exist on the transition day. Unix timestamps themselves never skip; only the wall-clock interpretation does.
- What is the DST overlap (fall-back)?
- The DST overlap is the wall-clock hour that occurs twice when daylight saving time ends. In most US zones, the clock ticks from 01:59 back to 01:00, so every minute from 01:00 to 01:59 happens twice. A wall-clock time in that window maps to two Unix timestamps; converters and schedulers must specify "earlier" or "later".