Skip to content
← 1001 Widgets
N° 037 · ●●○

Time zone clock

Brussels, Geneva, Montreal, Tokyo at the exact time, daylight saving included, with a slider to plan a call. The right exercise to let the Intl API handle time zones.

JSIntl
What you'll learn
  • Show a time in a time zone with Intl.DateTimeFormat
  • Compute the gap between two cities at a given moment
  • Spot a change of day
Why this language

Pourquoi une horloge des fuseaux ? Un client à Montréal, une amie à Tokyo, une réunion à Genève : quelle heure est-il là-bas, et est-ce qu'on peut appeler ? Les fuseaux horaires sont pleins de pièges : heure d'été à des dates différentes, décalages d'une demi-heure, changement de jour.

On ne calcule rien à la main. Le navigateur connaît la base officielle des fuseaux, et Intl.DateTimeFormat affiche n'importe quel instant dans n'importe quelle ville avec l'option timeZone.

Le curseur décale l'instant : glissez de trois heures pour voir si l'appel tombe en pleine nuit quelque part.

Pourquoi en JavaScript simple ? Une seule API, aucune bibliothèque, et la règle d'or : on manipule des instants, on n'affiche les fuseaux qu'à la fin.

How it works
  1. LES VILLES

    Des identifiants officiels, pas des décalages fixes comme +1 ou +9.

    const VILLES = [['Bruxelles', 'Europe/Brussels'], ['Genève', 'Europe/Zurich'],
                    ['Montréal', 'America/Toronto'], ['Tokyo', 'Asia/Tokyo']];
  2. AFFICHER UNE HEURE DANS UN FUSEAU

    Un seul instant, plusieurs façons de l'afficher.

    function heure(instant, fuseau) {
      return new Intl.DateTimeFormat('fr-BE', {
        timeZone: fuseau, hour: '2-digit', minute: '2-digit', weekday: 'short', hourCycle: 'h23',
      }).format(instant);
    }
  3. L'ÉCART AVEC UTC

    On relit les morceaux de la date dans le fuseau, et on compare à l'instant réel.

    function ecart(fuseau, instant) {
      const p = {};
      new Intl.DateTimeFormat('en-US', { timeZone: fuseau, hourCycle: 'h23', year: 'numeric',
        month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' })
        .formatToParts(instant).forEach(x => (p[x.type] = x.value));
      const local = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute);
      return Math.round((local - instant.getTime()) / 60000);   // en minutes
    }
  4. LA DIFFÉRENCE ENTRE DEUX VILLES

    Calculée à l'instant affiché : pendant trois semaines par an, Bruxelles et Montréal n'ont pas leur écart habituel.

    const diff = (a, b, instant) => (ecart(b, instant) - ecart(a, instant)) / 60;
  5. PLANIFIER

    Le curseur décale l'instant, pas les fuseaux.

    const decalage = Number(curseur.value);                 // en heures
    const instant = new Date(Date.now() + decalage * 3600000);
    for (const [nom, fuseau] of VILLES) console.log(nom, heure(instant, fuseau));
Pitfalls

PIÈGE 1 : Des décalages écrits en dur

Pitfall

Mauvais : Tokyo = Bruxelles + 8. C'est faux la moitié de l'année, car Tokyo n'a pas d'heure d'été.

Fix

Correct : des identifiants comme Asia/Tokyo, et laisser Intl appliquer les règles du moment.

PIÈGE 2 : Modifier la date pour changer de fuseau

Pitfall

Mauvais : ajouter des heures à un objet Date pour « le mettre à l'heure de Tokyo ». On fabrique un faux instant, et les calculs suivants se trompent.

Fix

Correct : garder un seul instant réel, et ne choisir le fuseau qu'au moment de l'afficher.

PIÈGE 3 : Oublier le changement de jour

Pitfall

Mauvais : n'afficher que l'heure. 08:41 à Tokyo, c'est déjà demain pour Bruxelles.

Fix

Correct : afficher le jour à côté de l'heure, et signaler la nuit pour éviter un appel à 3 h.

PERSONNALISER
  • Des horaires de bureau par ville, et une bande verte quand tout le monde est disponible.
  • Une recherche parmi tous les fuseaux avec Intl.supportedValuesOf('timeZone').
  • Des horloges à aiguilles en SVG, une par ville.
OPTIMISATION
  • Créez chaque Intl.DateTimeFormat une seule fois et gardez-le en cache : c'est l'appel le plus coûteux.
  • Rafraîchissez au changement de minute plutôt qu'à chaque seconde.
VERS REACT

La liste des villes et le décalage vivent dans deux useState.

Un useEffect met à jour un état « maintenant » toutes les quinze secondes, et le nettoie au démontage.

Avec un rendu serveur, l'heure du serveur et celle du navigateur diffèrent : affichez l'horloge seulement côté client.

Comments
0/1000

Loading comments…

Audience measurement

With your consent, this site measures its traffic with Google Analytics and the Meta pixel, via Google Tag Manager. Nothing is loaded until you choose, and you can change your mind at any time with the "Cookies" link at the bottom of the page.