ZonedDateTime, ZoneId and Instant
When people in different countries share a moment, such as a video meeting, a local date and time isn't enough: 9:00 in Pune and 9:00 in London are different moments. The java.time package has three classes for this. ZoneId names a time zone, such as Asia/Kolkata. ZonedDateTime is a date and time in a specific zone, including its daylight-saving rules. Instant is an exact moment on the global timeline, counted in UTC, with no zone at all. In this lesson, you will learn all three and build a small meeting scheduler.
The Classes
| Class | Holds | Example |
|---|---|---|
ZoneId | A time zone name and its rules | Asia/Kolkata, Europe/London |
ZoneOffset | A fixed difference from UTC | +05:30 |
ZonedDateTime | Date, time and zone | 2026-11-12T09:00+05:30[Asia/Kolkata] |
OffsetDateTime | Date, time and a fixed offset | 2026-11-12T09:00+05:30 |
Instant | A moment in UTC (seconds and nanoseconds since 1970) | 2026-11-12T03:30:00Z |
Methods
| Method | Description |
|---|---|
ZoneId.of("Asia/Kolkata") | It is used to get a time zone by its region name. |
ZonedDateTime.of(localDateTime, zone) | It attaches a zone to a local date and time. |
zdt.withZoneSameInstant(zone) | It shows the same moment in another zone. |
zdt.withZoneSameLocal(zone) | It keeps the same clock time but changes the zone (a different moment). |
zdt.toInstant() | It converts to an Instant. |
instant.atZone(zone) | It shows an Instant in a zone. |
Instant.now(), Instant.ofEpochSecond(s) | It gets the current moment, or a moment from epoch seconds. |
Example: The Same Moment in Three Zones
Let's see an example that shows a meeting time around the world.
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Zones {
public static void main(String[] args) {
ZoneId india = ZoneId.of("Asia/Kolkata");
ZonedDateTime meeting = ZonedDateTime.of(
LocalDateTime.of(2026, 11, 12, 19, 0), india);
System.out.println("India: " + meeting);
System.out.println("London: "
+ meeting.withZoneSameInstant(ZoneId.of("Europe/London")));
System.out.println("New York: "
+ meeting.withZoneSameInstant(ZoneId.of("America/New_York")));
Instant moment = meeting.toInstant();
System.out.println("Instant: " + moment);
System.out.println("Epoch seconds: " + moment.getEpochSecond());
}
}India: 2026-11-12T19:00+05:30[Asia/Kolkata]
London: 2026-11-12T13:30Z[Europe/London]
New York: 2026-11-12T08:30-05:00[America/New_York]
Instant: 2026-11-12T13:30:00Z
Epoch seconds: 1794490200Explanation: withZoneSameInstant keeps the moment and changes how it is shown: 19:00 in India is 13:30 in London and 08:30 in New York. The Instant prints in UTC (the Z means "zero offset"); London in November also has zero offset. All four lines describe the same moment.
Daylight Saving Time
Many zones move their clocks forward in spring and back in autumn. ZonedDateTime knows these rules, so arithmetic stays correct:
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Dst {
public static void main(String[] args) {
ZoneId ny = ZoneId.of("America/New_York");
ZonedDateTime before = ZonedDateTime.of(
LocalDateTime.of(2026, 3, 8, 1, 30), ny);
ZonedDateTime oneHourLater = before.plusHours(1);
System.out.println(before + " + 1 hour = " + oneHourLater);
ZonedDateTime gap = ZonedDateTime.of(
LocalDateTime.of(2026, 3, 8, 2, 30), ny);
System.out.println("2:30 on that day becomes " + gap);
ZonedDateTime dayLater = before.plusDays(1);
System.out.println("plusDays(1) is "
+ Duration.between(before, dayLater).toHours() + " hours");
}
}2026-03-08T01:30-05:00[America/New_York] + 1 hour = 2026-03-08T03:30-04:00[America/New_York]
2:30 on that day becomes 2026-03-08T03:30-04:00[America/New_York]
plusDays(1) is 23 hoursExplanation: On 8 March 2026, New York clocks jump from 2:00 to 3:00. So one hour after 1:30 is 3:30, the time 2:30 doesn't exist and is moved to 3:30, and that "day" is only 23 hours long. plusDays keeps the same clock time; plusHours adds real elapsed time.
The Checkpoint: A Meeting Scheduler
This program schedules a meeting in Pune and shows it for colleagues in two other cities, warning when it falls outside working hours (9:00 to 18:00) for anyone.
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Map;
import java.util.TreeMap;
public class Scheduler {
static final Map<String, ZoneId> TEAM = new TreeMap<>(Map.of(
"Navika (Pune)", ZoneId.of("Asia/Kolkata"),
"Srihitha (London)", ZoneId.of("Europe/London"),
"Advitha (Sydney)", ZoneId.of("Australia/Sydney")));
static void show(LocalDateTime puneTime) {
ZonedDateTime meeting = puneTime.atZone(ZoneId.of("Asia/Kolkata"));
System.out.println("Meeting at " + puneTime + " Pune time:");
TEAM.forEach((name, zone) -> {
LocalTime local = meeting.withZoneSameInstant(zone).toLocalTime();
boolean ok = !local.isBefore(LocalTime.of(9, 0))
&& !local.isAfter(LocalTime.of(18, 0));
System.out.println(" " + name + ": " + local
+ (ok ? "" : " (outside working hours)"));
});
}
public static void main(String[] args) {
show(LocalDateTime.of(2026, 11, 12, 14, 0));
show(LocalDateTime.of(2026, 11, 12, 10, 0));
}
}Meeting at 2026-11-12T14:00 Pune time:
Advitha (Sydney): 19:30 (outside working hours)
Navika (Pune): 14:00
Srihitha (London): 08:30 (outside working hours)
Meeting at 2026-11-12T10:00 Pune time:
Advitha (Sydney): 15:30
Navika (Pune): 10:00
Srihitha (London): 04:30 (outside working hours)Explanation: For each team member, the meeting moment is converted with withZoneSameInstant, then its local time is checked against 9:00 to 18:00. Neither slot suits all three cities, which is exactly the kind of fact a scheduler should point out.
Common Mistakes
Storing LocalDateTime for events shared across zones. Store an Instant (or ZonedDateTime) instead.
Using withZoneSameLocal when converting. It changes the moment; use withZoneSameInstant.
Assuming a day is always 24 hours. Daylight-saving days can be 23 or 25 hours.
Interview Corner
What is the difference between Instant, LocalDateTime and ZonedDateTime?
Instant is an exact point on the timeline in UTC, ideal for timestamps and storage. LocalDateTime is a date and time with no zone, so it doesn't identify a moment by itself. ZonedDateTime combines a local date-time with a ZoneId and its rules, so it identifies an exact moment and shows it as people in that zone see it, handling daylight saving. Convert between them with atZone, toInstant and withZoneSameInstant.
Practice
- Easy: Print the current time in Tokyo, Dubai and Los Angeles using
ZonedDateTime.now(zone). - Harder: Extend the scheduler to find, for a given day, every whole hour in Pune time that falls within working hours for all three cities.
Key Takeaways
Was this tutorial helpful?