ZonedDateTime in Java: Convert Between Timezones and Handle Daylight-Saving Time Automatically

Image from https://unsplash.com/.

Overview

1. Introduction

Working with date-time values can be challenging.

Typically, dealing with different time zones and daylight-saving times are the main problems.

In Java, you can use the ZonedDateTime class to avoid reinventing the wheel when working with timezones.

In this article, I'll show you the ZonedDateTime class, how and when to use it, and its difference from the LocalDateTime class.

2. What is ZonedDateTime in Java

ZonedDateTime is a class in the java.time package that represents a date-time with a corresponding time zone. Think of it like a combination of the LocalDateTime, ZoneId, and ZoneOffset classes.

You can use ZonedDateTime to easily create and manipulate date-time values within different time zones. The following two subsections show how to create objects of it using two methods: now() and of().

2.1. How to Create a ZonedDateTime Using .now()

You can use the ZonedDateTime.now() method to create a date-time corresponding to the current clock like follows:

1ZonedDateTime zonedDateTimeNow = ZonedDateTime.now();
2System.out.println("ZonedDateTime using now(): " + zonedDateTimeNow);

The code above outputs the following result:

1ZonedDateTime using now(): 2023-04-05T17:45:56.265144700-03:00[America/Sao_Paulo]

The method correctly picks my current timezone, [America/Sao_Paulo].

2.2. How to Create a ZonedDateTime Using .of()

You can also create a ZonedDateTime object by using the .of() static factory. Look at the example below on how you can use date-time and timezone values to create the object:

1ZonedDateTime zonedDateTimeOf = ZonedDateTime.of(2023, 4, 4, 13, 30, 45, 0, ZoneId.of("Europe/Paris"));
2System.out.println("ZonedDateTime using of(): " + zonedDateTimeOf);

Each argument corresponds to a part of the date-time and the timezone. The of() method signature below indicates in which order each parameter should appear:

1public static ZonedDateTime of(
2        int year, int month, int dayOfMonth,
3        int hour, int minute, int second, int nanoOfSecond, ZoneId zone)

As expected, the code outputs the following result:

1ZonedDateTime using of(): 2023-04-04T13:30:45+02:00[Europe/Paris]

3. When to use ZonedDateTime in Java

In this section, I'll show you when to use the ZonedDateTime class in your Java applications. ZonedDateTime creates easy ways to:

  • Convert the same date-time to different timezones.
  • Handle daylight-saving time shifts without effort.

3.1. Convert the Same Date-Time to Different Timezones

A critical feature of ZonedDateTime is the information about the time zone offset, which is the difference between the local time and the Coordinated Universal Time (UTC). That makes working with date-time values in different time zones easier, as the class automatically adjusts for the offset.

To take advantage of that feature, you can use the withZoneSameInstant() method passing the ZoneId to convert between different timezones, like below:

1ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 4, 4, 13, 30, 45, 50, ZoneId.of("Europe/Paris"));
2
3System.out.println("DateTime in Paris: " + zonedDateTime);
4System.out.println("DateTime in Los Angeles: " + zonedDateTime.withZoneSameInstant(ZoneId.of("America/Los_Angeles")));
5System.out.println("DateTime in London: " + zonedDateTime.withZoneSameInstant(ZoneId.of("Europe/London")));

I've used the withZoneSameInstant to convert the date-time in Europe/Paris to two different timezones: America/Los_Angeles and Europe/London.

That code outputs three different date-time values corresponding to the different timezones desired:

1DateTime in Paris: 2023-04-04T13:30:45+02:00[Europe/Paris]
2DateTime in Los Angeles: 2023-04-04T04:30:45-07:00[America/Los_Angeles]
3DateTime in London: 2023-04-04T12:30:45+01:00[Europe/London]

Notice how Java handles the timezone offsets automatically!

3.2. Handle Daylight-Saving Time Shift

Another helpful feature is the daylight-saving time trigger when you use methods to increment or decrement a date-time value.

Java automatically handles daylight-saving time when you increment or decrement a date-time.

For example, let's say you're in Los Angeles precisely one second before daylight-saving time starts. That date-time could be 12 March 2023 01:59:59. If you increment one second to that, the clock should move forward by one hour.

Take a look at the following example to see an illustration of that:

1ZonedDateTime zonedDateTime = ZonedDateTime.of(2023, 3, 12, 1, 59, 59, 0, ZoneId.of("America/Los_Angeles"));
2System.out.println("date-time before daylight-saving time: " + zonedDateTime);
3zonedDateTime = zonedDateTime.plusSeconds(1);
4System.out.println("date-time after daylight-saving time: " + zonedDateTime);

Note: You must reassign the result of plusSeconds() to the original object to get its effect. That's because, just like Strings are Immutable, ZonedDateTime objects are also immutable.

The output for that code is the following:

1date-time before daylight-saving time: 2023-03-12T01:59:59-08:00[America/Los_Angeles]
2date-time after daylight-saving time: 2023-03-12T03:00-07:00[America/Los_Angeles]

Notice that Java automatically triggers the daylight-saving time feature when needed. That feature is very handy in real-world internationalized applications.

4. Difference Between LocalDateTime and ZonedDateTime

The notable difference between the two classes is that LocalDateTime doesn't store any timezone or offset information. Thus, you can't handle daylight-saving time with a LocalDateTime object. In addition, you can't convert a date-time to different timezones using it.

There's a relationship between these two classes that is worth mentioning. It's possible to convert one to another and vice versa.

To convert a LocalDateTime to a ZonedDateTime, you should use the atZone() method passing the desired timezone as an argument.

On the other hand, you can use the toLocalDateTime() method of ZonedDateTime to get the local date-time part of it. It's also possible to create a ZonedDateTime object from a LocalDateTime using one its static factories: of(), ofInstant(), ofLocal(), or ofStrict().

For completeness, take a look at the following example of how you can create a ZonedDateTime from a LocalDateTime object:

1LocalDateTime localDate = LocalDateTime.of(2023, 6, 4, 10, 20, 30);
2System.out.println(localDate);
3ZonedDateTime zonedFromLocal = ZonedDateTime.of(localDate, ZoneId.of("America/Sao_Paulo"));
4System.out.println(zonedFromLocal);

In the example above, I've used the of() version where you can pass a LocaDateTime object and a ZoneId to get a ZonedDateTime object. That code outputs the following result:

12023-06-04T10:20:30
22023-06-04T10:20:30-03:00[America/Sao_Paulo]

Note that the localDate variable output doesn't contain any information about the timezone and offset.

5. Conclusion

LocalDateTime and ZonedDateTime are classes in Java that you can use to represent date-time values, with some differences.

ZonedDateTime stores the timezone and offset, which makes it easier to work with timezone transitions and daylight-saving times. LocalDateTime doesn't have that information, which makes it impractical for scenarios that need time zones. For instance, internationalized applications.

You can convert ZonedDateTime and LocalDateTime from one to another using methods from the two classes.

The key idea is to use ZonedDateTime when your application is internationalized or needs any timezone information. If that's not the case, LocalDateTime is enough to do the job.