CodeGym /Cours /JAVA 25 SELF /ZonedDateTime, Instant, gestion des fuseaux horaires

ZonedDateTime, Instant, gestion des fuseaux horaires

JAVA 25 SELF
Niveau 13 , Leçon 3
Disponible

1. Notion de fuseau horaire

Il existe de nombreux fuseaux horaires dans le monde : lorsqu’il est midi à Minsk, il n’est que le matin à New York, et déjà le soir à Tokyo. Si vous stockez la date et l’heure sans prise en compte du fuseau horaire, la confusion est facile : par exemple, si votre serveur est en Allemagne et l’utilisateur à Vladivostok, l’affichage "2025-06-01 12:00" signifiera des choses totalement différentes pour chacun.

Un fuseau horaire (timezone) — c’est la règle qui détermine combien de temps ajouter ou soustraire à l’heure de Greenwich (UTC) pour obtenir l’heure « locale » d’une région donnée.

En Java, la classe ZoneId est utilisée pour gérer les fuseaux horaires. Voici quelques exemples d’identifiants de zones :

  • "Europe/Minsk"
  • "UTC"
  • "America/New_York"
  • "Asia/Tokyo"

Pourquoi est-ce important ?

  • Affichage correct de l’heure pour des utilisateurs de différents pays.
  • Enregistrement correct de l’heure des événements (par exemple, journalisation, réservation de billets, échéances).
  • Prise en compte du passage à l’heure d’été/d’hiver (merci, l’Europe !).

2. ZonedDateTime — date et heure tenant compte du fuseau horaire

ZonedDateTime est une classe qui stocke la date, l’heure et les informations de fuseau horaire. C’est comme LocalDateTime, mais il « sait » aussi dans quelle région il se trouve.

Création de ZonedDateTime

Date et heure actuelles dans le fuseau horaire du système

import java.time.ZonedDateTime;

ZonedDateTime now = ZonedDateTime.now();
System.out.println(now); // Par exemple: 2025-06-01T15:30:00+03:00[Europe/Minsk]

Heure dans un fuseau horaire spécifique

import java.time.ZoneId;

ZonedDateTime MinskTime = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
ZonedDateTime newYorkTime = ZonedDateTime.now(ZoneId.of("America/New_York"));

System.out.println("Minsk : " + MinskTime);
System.out.println("New York : " + newYorkTime);

Création à partir de LocalDateTime

import java.time.LocalDateTime;

LocalDateTime meeting = LocalDateTime.of(2025, 6, 1, 18, 0);
ZonedDateTime meetingInMinsk = meeting.atZone(ZoneId.of("Europe/Minsk"));
System.out.println(meetingInMinsk); // 2025-06-01T18:00+03:00[Europe/Minsk]

Récupération et définition du fuseau horaire

ZoneId tokyoZone = ZoneId.of("Asia/Tokyo");
ZonedDateTime tokyoTime = ZonedDateTime.now(tokyoZone);
System.out.println("Tokyo : " + tokyoTime);

Conversion entre zones : withZoneSameInstant()

Il arrive que l’on doive savoir à quoi ressemble le même événement dans une autre zone. Pour cela, on utilise withZoneSameInstant() :

ZonedDateTime MinskMeeting = ZonedDateTime.of(2025, 6, 1, 18, 0, 0, 0, ZoneId.of("Europe/Minsk"));
ZonedDateTime newYorkMeeting = MinskMeeting.withZoneSameInstant(ZoneId.of("America/New_York"));

System.out.println("Heure de la réunion à Minsk : " + MinskMeeting);
System.out.println("Le même événement à New York : " + newYorkMeeting);

Attention : withZoneSameInstant() convertit l’heure de façon à ce qu’elle corresponde au même moment dans l’autre zone. Si vous utilisez withZoneSameLocal(), la date et l’heure locales restent les mêmes mais la zone change — c’est presque toujours une erreur !

3. Instant — un point absolu dans le temps

Instant est une classe qui représente un moment absolu, indépendamment du fuseau horaire. Techniquement, c’est le nombre de secondes et de nanosecondes écoulées depuis le 1er janvier 1970 à Greenwich (UTC). Si le temps avait un passeport — Instant en serait le numéro.

Création d’un Instant

import java.time.Instant;

Instant now = Instant.now();
System.out.println(now); // Par exemple: 2025-06-01T12:30:00.123Z

Remarquez la lettre Z — cela signifie « Zulu time », c’est-à-dire UTC.

Création à partir des secondes depuis l’époque Unix

Instant fromEpoch = Instant.ofEpochSecond(1685616000L);
System.out.println(fromEpoch); // 2023-06-01T00:00:00Z

Conversion InstantZonedDateTime/LocalDateTime

De ZonedDateTime vers Instant

ZonedDateTime zoned = ZonedDateTime.now();
Instant instant = zoned.toInstant();
System.out.println(instant);

De Instant vers ZonedDateTime

ZoneId zone = ZoneId.of("Europe/Minsk");
ZonedDateTime fromInstant = Instant.now().atZone(zone);
System.out.println(fromInstant);

De Instant vers LocalDateTime

import java.time.LocalDateTime;
import java.time.Instant;
import java.time.ZoneId;

LocalDateTime local = LocalDateTime.ofInstant(Instant.now(), ZoneId.of("Europe/Minsk"));
System.out.println(local);

4. Pratique : heure actuelle dans différents fuseaux, conversion entre zones

Obtenir l’heure actuelle dans différents fuseaux

Faisons une mini-application qui affiche l’heure actuelle à Minsk, New York et Tokyo :

import java.time.ZonedDateTime;
import java.time.ZoneId;

public class TimeZonesDemo {
    public static void main(String[] args) {
        ZonedDateTime Minsk = ZonedDateTime.now(ZoneId.of("Europe/Minsk"));
        ZonedDateTime newYork = ZonedDateTime.now(ZoneId.of("America/New_York"));
        ZonedDateTime tokyo = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));

        System.out.println("Minsk :    " + Minsk);
        System.out.println("New York :  " + newYork);
        System.out.println("Tokyo :     " + tokyo);
    }
}

Conversion de l’heure entre les zones

Supposons que vous ayez un événement prévu à 18:00 à Minsk. Comment savoir à quelle heure ce sera à New York et à Tokyo ?

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

public class MeetingTime {
    public static void main(String[] args) {
        LocalDateTime eventTime = LocalDateTime.of(2025, 6, 1, 18, 0);
        ZonedDateTime minskEvent = eventTime.atZone(ZoneId.of("Europe/Minsk"));

        ZonedDateTime newYorkEvent = minskEvent.withZoneSameInstant(ZoneId.of("America/New_York"));
        ZonedDateTime tokyoEvent = minskEvent.withZoneSameInstant(ZoneId.of("Asia/Tokyo"));

        System.out.println("Réunion à Minsk :   " + minskEvent);
        System.out.println("À New York :        " + newYorkEvent);
        System.out.println("À Tokyo :            " + tokyoEvent);
    }
}

Conversion de LocalDateTime en ZonedDateTime et inversement

Local → Zoned :

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;

LocalDateTime localTime = LocalDateTime.of(2025, 6, 1, 14, 0);
ZonedDateTime zonedTime = localTime.atZone(ZoneId.of("Europe/Minsk"));
System.out.println(zonedTime);

Zoned → Local :

LocalDateTime extracted = zonedTime.toLocalDateTime();
System.out.println(extracted);

5. Points importants et subtilités

Pourquoi il ne faut pas stocker seulement LocalDateTime

LocalDateTime, c’est simplement une date et une heure sans fuseau horaire. Pour la plupart des logiques métier, c’est insuffisant ! Par exemple, si vous stockez "2025-06-01 12:00" comme LocalDateTime, alors pour un utilisateur de Minsk et un utilisateur de New York ce seront des moments totalement différents dans le temps réel.

Stockez toujours le temps absolu (par exemple, un Instant), ou l’heure avec fuseau (ZonedDateTime) si l’événement est réellement lié à une zone précise. LocalDateTime n’est bon que si vous travaillez avec des dates « flottantes » (par exemple, un anniversaire sans prise en compte de l’heure du jour ni du fuseau).

Problèmes liés au passage à l’heure d’été/d’hiver

Les fuseaux horaires — ce n’est pas seulement un décalage par rapport à UTC, mais aussi des règles de passage à l’heure d’été/d’hiver. Par exemple, dans certains pays, à une date donnée on avance ou recule les horloges d’une heure — et si vous stockez seulement un LocalDateTime, vous ne saurez pas si cette heure a même existé.

Exemple de « trou temporel » :

  • Aux États-Unis, en mars, à 2:00 du matin, on passe à 3:00.
  • L’heure "2025-03-10 02:30" à New York n’a pas existé !

En travaillant avec ZonedDateTime, vous êtes protégé contre ce genre de surprises : la bibliothèque vérifiera elle-même la validité de l’heure.

Schéma : relations entre LocalDateTime, ZonedDateTime, Instant

graph TD
    A["LocalDateTime 
(date + heure, sans zone)"] -->|+ ZoneId| B["ZonedDateTime
(date + heure + zone)"] B -->|"toInstant()"| C["Instant
(temps absolu, UTC)"] C -->|"atZone(ZoneId)"| B B -->|"toLocalDateTime()"| A

6. Erreurs courantes avec ZonedDateTime et Instant

Erreur n° 1 : utiliser LocalDateTime pour des événements globaux.
Si vous stockez la date et l’heure d’une réunion entre utilisateurs de pays différents comme un LocalDateTime, chacun verra son « 12:00 », alors qu’il s’agit de moments différents. Pour des événements globaux, utilisez ZonedDateTime ou Instant.

Erreur n° 2 : ignorer le fuseau lors de l’analyse d’une chaîne.
Si vous analysez la chaîne "2025-06-01T12:00:00" sans indiquer de zone, vous obtiendrez un LocalDateTime, et non un ZonedDateTime. Pour obtenir un ZonedDateTime, utilisez des chaînes contenant la zone ou ajoutez-la explicitement.

Erreur n° 3 : mauvaise conversion entre les zones.
Utiliser withZoneSameLocal() au lieu de withZoneSameInstant() peut conduire à une heure incorrecte. Utilisez toujours withZoneSameInstant() si vous souhaitez obtenir le même moment dans une autre zone.

Erreur n° 4 : ne pas tenir compte du passage à l’heure d’été/d’hiver.
Si vous planifiez des événements à la frontière d’un changement d’heure, utilisez impérativement ZonedDateTime et faites confiance à la bibliothèque — elle connaît tous les changements et « trous » temporels.

Erreur n° 5 : comparer des ZonedDateTime sans tenir compte de la zone.
Deux ZonedDateTime avec des zones différentes mais la même heure locale peuvent représenter des moments différents. Pour comparer, utilisez toInstant().

1
Mission
JAVA 25 SELF, niveau 13, leçon 3
Bloqué
Centre de commandement mondial : heures locales dans le monde 🌍
Centre de commandement mondial : heures locales dans le monde 🌍
1
Mission
JAVA 25 SELF, niveau 13, leçon 3
Bloqué
Conférence internationale : fixation de l'événement dans un fuseau horaire 🌐
Conférence internationale : fixation de l'événement dans un fuseau horaire 🌐
1
Mission
JAVA 25 SELF, niveau 13, leçon 3
Bloqué
Téléportation : un même événement dans différents fuseaux horaires 🚀
Téléportation : un même événement dans différents fuseaux horaires 🚀
1
Mission
JAVA 25 SELF, niveau 13, leçon 3
Bloqué
Chroniques du temps: voyage à travers Instant 🌌
Chroniques du temps: voyage à travers Instant 🌌
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION