- Amir Boroumand | Software engineer based in Pittsburgh, PA/
- blog/
- Add a Timezone to LocalDateTime with ZonedDateTime in Java 8/
Add a Timezone to LocalDateTime with ZonedDateTime in Java 8
Note
This article was written over 5 years ago. Some information may be outdated or irrelevant.
Overview #
The LocalDateTime
class introduced in Java 8 stores the date and time but not the timezone. So we need something else when developing applications that need to be aware of different timezones.
Fortunately, we can use the ZonedDateTime
class to represent dates and times with timezone information. The class contains static methods that allow us to perform date conversions and calculations easily.
Code Examples #
ZonedDateTime
objects are immutable so the static methods all return a new instance.
Get current date and time in the local timezone #
ZonedDateTime now = ZonedDateTime.now();
// 2018-05-02T15:45:20.981-04:00[America/New_York]
Get current date and time in a different timezone #
ZonedDateTime nowInParis = ZonedDateTime.now(ZoneId.of("Europe/Paris"));
// 2018-05-02T21:45:20.982+02:00[Europe/Paris]
TimeZone
Id Values #
The list of TimeZone
id values is documented well
here.
We can also retrieve them using a method:
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
Always use timezone values in the Region/City format (America/New_York
) and avoid the short abbreviations (EST
) because they are ambiguous and non-standard.
Create ZonedDateTime
from a String #
ZonedDateTime departureTime = ZonedDateTime.parse("2018-07-01T10:00:00Z[America/New_York]");
ZonedDateTime arrivalTime = ZonedDateTime.parse("2018-07-01T22:00:00Z[Europe/London]");
Convert LocalDateTime
to ZonedDateTime
#
We can convert a LocalDateTime
to ZonedDateTime
a couple different ways.
LocalDateTime ldt = LocalDateTime.parse("2018-07-01T08:00");
ZoneId zoneId = ZoneId.of("Europe/Paris");
ZonedDateTime zdt1 = ZonedDateTime.of(ldt, zoneId);
// 2018-07-01T08:00+02:00[Europe/Paris]
ZonedDateTime zdt2 = ldt.atZone(zoneId);
// 2018-07-01T08:00+02:00[Europe/Paris]
Note that this doesn’t apply any timezone conversion to our LocalDateTime
. It simply adds the timezone to whatever date and time was stored in the source object.
Compare ZonedDateTime
objects #
boolean departureBeforeArrival = departureTime.isBefore(arrivalTime);
boolean arrivalAfterDeparture = arrivalTime.isAfter(departureTime);
This code compares the actual dates and times and not the object references.
LocalDateTime ldt = LocalDateTime.parse("2018-07-01T08:00");
ZonedDateTime zdtParis = ZonedDateTime.of(ldt, ZoneId.of("Europe/Paris"));
// 2018-07-01T08:00+02:00[Europe/Paris]
ZonedDateTime zdtNewYork = ZonedDateTime.of(ldt, ZoneId.of("America/New_York"));
// 2018-07-01T08:00-04:00[America/New_York]
boolean equal = zdtParis.isEqual(zdtNewYork); // false
Find the time difference between ZonedDateTime
objects #
The java.time.temporal.ChronoUnit
class allows us to calculate the time difference between ZonedDateTime objects:
long flightTimeInHours = ChronoUnit.HOURS.between(departureTime, arrivalTime); // 7
long flightTimeInMinutes = ChronoUnit.MINUTES.between(departureTime, arrivalTime); // 420
Create ZonedDateTime
with new timezone from existing ZonedDateTime
object #
Given a ZonedDateTime
object in timezone A, we can create another ZonedDateTime
object in timezone B which represents the same point in time.
ZonedDateTime nowInLocalTimeZone = ZonedDateTime.now();
// 2018-05-02T20:10:27.896-04:00[America/New_York]
ZonedDateTime nowInParis = nowInLocalTimeZone.withZoneSameInstant(ZoneId.of("Europe/Paris"));
// 2018-05-03T02:10:27.896+02:00[Europe/Paris]
Unit Testing Considerations #
Suppose we need a method that determines if we can issue a boarding pass to an airline passenger based on some business logic.
It should return true
when the departure time of the outbound OR return flight is less than 24 hours away.
We could write something like this:
|
|
This works fine but is difficult to unit test because of line 14. We cannot mock ZonedDateTime.now()
because it’s a static method.
java.time.Clock #
The now()
methods in java.time all accept a Clock
argument which we can use to refactor our code so it’s testable.
Here’s the testable version of the class:
|
|
On line 12, we added a single argument constructor which accepts a Clock
object. We can then use the clock on line 21.
In the calling code, we’d pass Clock.systemDefaultZone()
to the the constructor to use the current system time.
In our tests, we’ll create a fixed time clock using Clock.fixed() and pass that.
Unit Tests #
Here are the unit tests for the code.
|
|