CodeGym /Courses /JAVA 25 SELF /ZonedDateTime, Instant, working with time zones

ZonedDateTime, Instant, working with time zones

JAVA 25 SELF
Level 13 , Lesson 3
Available

1. The concept of a time zone

There are many time zones in the world: when it’s noon in Minsk, it’s only morning in New York, and it’s already evening in Tokyo. If you store date and time without taking the time zone into account, it’s easy to get confusion: for example, if your server is in Germany while the user is in Vladivostok, the time display "2025-06-01 12:00" will mean completely different things for each.

Time zone (timezone) — a rule that defines how much to add to or subtract from Coordinated Universal Time (UTC) to get the “local” time for a specific region.

In Java, the ZoneId class is used to work with time zones. Here are a few examples of zone identifiers:

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

Why does this matter?

  • Correct time display for users from different countries.
  • Proper recording of event times (for example, logging, ticket booking, deadlines).
  • Accounting for daylight saving time transitions (thanks, Europe!).

2. ZonedDateTime — date and time with a time zone

ZonedDateTime is a class that stores the date, time and time zone information. It’s like LocalDateTime, except it also “knows” which region it is in.

Creating ZonedDateTime

Current date and time in the system time zone

import java.time.ZonedDateTime;

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

Time in a specific time zone

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);

Creating from 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]

Getting and setting the time zone

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

Converting between zones: withZoneSameInstant()

Sometimes you need to know how the same event looks in another zone. Use withZoneSameInstant() for this:

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("Meeting time in Minsk: " + MinskMeeting);
System.out.println("The same event in New York: " + newYorkMeeting);

Warning: withZoneSameInstant() converts the time so that it corresponds to the same instant in another zone. If you use withZoneSameLocal(), the date and time will remain the same while the zone changes — that’s almost always a mistake!

3. Instant — an absolute point in time

Instant is a class that represents an absolute moment in time, independent of any time zone. Technically, it’s the number of seconds and nanoseconds since 1 January 1970 in UTC. If time had a passport — Instant would be its number.

Creating Instant

import java.time.Instant;

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

Note the letter Z — it means “Zulu time”, i.e., UTC.

Creating from Unix epoch seconds

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

Converting InstantZonedDateTime/LocalDateTime

From ZonedDateTime to Instant

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

From Instant to ZonedDateTime

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

From Instant to 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. Practice: current time in different time zones, converting between zones

Getting the current time in different time zones

Let’s make a small application that shows the current time in Minsk, New York, and 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);
    }
}

Converting time between zones

Suppose you have an event scheduled for 18:00 in Minsk. How do you find out what time that will be in New York and 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("Meeting in Minsk:   " + minskEvent);
        System.out.println("In New York:        " + newYorkEvent);
        System.out.println("In Tokyo:            " + tokyoEvent);
    }
}

Converting LocalDateTime to ZonedDateTime and back

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. Important notes and caveats

Why you shouldn’t store only LocalDateTime

LocalDateTime is just a date and time without a time zone. For most business logic that’s not enough! For example, if you store "2025-06-01 12:00" as a LocalDateTime, for a user in Minsk and in New York these will be completely different moments in real time.

Always store the absolute time (for example, Instant), or time with a zone (ZonedDateTime) if the event is truly tied to a specific zone. LocalDateTime is good only when you work with “floating” dates (for example, a birthday without regard to time of day and zone).

Issues with daylight saving time transitions

Time zones are not only an offset relative to UTC, but also rules for switching to and from daylight saving time. For example, in some countries on a certain day the time is shifted forward or back by an hour — and if you store only a LocalDateTime, you won’t know whether that time existed at all.

Example of a “gap in time”:

  • In the US in March at 2:00 a.m. the clocks are moved to 3:00.
  • The time "2025-03-10 02:30" in New York did not exist!

When working with ZonedDateTime, you’re protected from such surprises: the library will validate the time for you.

Diagram: how LocalDateTime, ZonedDateTime, Instant are related

graph TD
    A["LocalDateTime 
(date + time, no zone)"] -->|+ ZoneId| B["ZonedDateTime
(date + time + zone)"] B -->|"toInstant()"| C["Instant
(absolute time, UTC)"] C -->|"atZone(ZoneId)"| B B -->|"toLocalDateTime()"| A

6. Common mistakes when working with ZonedDateTime and Instant

Error #1: Using LocalDateTime for global events.
If you store the date and time of a meeting between users in different countries as a LocalDateTime, each will see their own “12:00”, even though these are different moments in time. For global events use ZonedDateTime or Instant.

Error #2: Ignoring the time zone when parsing a string.
If you parse the string "2025-06-01T12:00:00" without specifying a zone, you’ll get a LocalDateTime, not a ZonedDateTime. To obtain a ZonedDateTime, use strings that include a zone or add it explicitly.

Error #3: Incorrect conversion between zones.
Using withZoneSameLocal() instead of withZoneSameInstant() can lead to incorrect times. Always use withZoneSameInstant() if you want to get the same instant in another zone.

Error #4: Not accounting for daylight saving time transitions.
If you plan events near transition boundaries, be sure to use ZonedDateTime and trust the library — it knows about all transitions and “gaps” in time.

Error #5: Comparing ZonedDateTime without considering the zone.
Two ZonedDateTime values with different zones but the same local time can represent different instants. For comparison use toInstant().

1
Task
JAVA 25 SELF, level 13, lesson 3
Locked
Global Command Center: World Time 🌍
Global Command Center: World Time 🌍
1
Task
JAVA 25 SELF, level 13, lesson 3
Locked
International conference: fixing the event in a time zone 🌐
International conference: fixing the event in a time zone 🌐
1
Task
JAVA 25 SELF, level 13, lesson 3
Locked
Teleportation Relocation: One Event Across Time Zones 🚀
Teleportation Relocation: One Event Across Time Zones 🚀
1
Task
JAVA 25 SELF, level 13, lesson 3
Locked
Chronicles of Time: a journey through Instant 🌌
Chronicles of Time: a journey through Instant 🌌
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION