Java 8 Convert Instant to ZonedDateTime Example
In this post, we feature a comprehensive Example on Java 8 Convert Instant to ZonedDateTime. We will discuss about the Java Instant
and ZonedDateTime
classes and how we can convert a time represented by the Instant
to a ZonedDateTime
object.
1. Java 8 Convert Instant to ZonedDateTime – Introduction
First we will have a look at the Instant class.
java.time.Instant
This class models a single instantaneous point on the time-line. This might be used to record event time-stamps in the application.
public final class Instant extends Object implements Temporal, TemporalAdjuster, Comparable, Serializable
The range of an instant requires the storage of a number larger than a long
. To achieve this, the class stores a long
representing epoch-seconds and an int representing nanoseconds-of-second, which will always be between 0 and 999,999,999. The epoch-seconds are measured from the standard Java epoch of 1970-01-01T00:00:00Z where instants after the epoch have positive values, and earlier instants have negative values. For both epoch-second and nanosecond parts, a larger value is always later on the time-line than the smaller value.
Time-scale
The length of the solar day is the standard way that humans measure time. This has traditionally been subdivided into 24 hours of 60 minutes of 60 seconds, forming a 86400 second day. Modern timekeeping is based on atomic clocks which precisely define an SI second relative to the transitions of a Caesium atom. The length of an SI second was defined to be very close to the 86400th fraction of a day.
Unfortunately, as the Earth rotates the length of the day varies. In addition, over time the average length of the day is getting longer as the Earth slows. As a result, the length of a solar day in 2012 is slightly longer than 86400 SI seconds. The actual length of any given day and the amount by which the Earth is slowing are not predictable and can only be determined by measurement. The UT1 time-scale captures the accurate length of day, but is only available some time after the day has completed.
The UTC time-scale is a standard approach to bundle up all the additional fractions of a second from UT1 into whole seconds, known as leap-seconds. A leap-second may be added or removed depending on the Earth’s rotational changes. As such, UTC permits a day to have 86399 SI seconds or 86401 SI seconds where necessary in order to keep the day aligned with the Sun.
The modern UTC time-scale was introduced in 1972, introducing the concept of whole leap-seconds. Between 1958 and 1972, the definition of UTC was complex, with minor sub-second leaps and alterations to the length of the notional second. As of 2012, discussions are underway to change the definition of UTC again, with the potential to remove leap seconds or introduce other changes.
Given the complexity of accurate timekeeping described above, this Java API defines its own time-scale, the Java Time-Scale. The Java Time-Scale divides each calendar day into exactly 86400 subdivisions, known as seconds. These seconds may differ from the SI second. It closely matches the de facto international civil time scale, the definition of which changes from time to time.
The Java Time-Scale has slightly different definitions for different segments of the time-line, each based on the consensus international time scale that is used as the basis for civil time. Whenever the internationally-agreed time scale is modified or replaced, a new segment of the Java Time-Scale must be defined for it. Each segment must meet these requirements:
- the Java Time-Scale shall closely match the underlying international civil time scale;
- the Java Time-Scale shall exactly match the international civil time scale at noon each day;
- the Java Time-Scale shall have a precisely-defined relationship to the international civil time scale.
There are currently, as of 2013, two segments in the Java time-scale.
For the segment from 1972-11-03 until further notice, the consensus international time scale is UTC (with leap seconds). In this segment, the Java Time-Scale is identical to UTC-SLS. This is identical to UTC on days that do not have a leap second. On days that do have a leap second, the leap second is spread equally over the last 1000 seconds of the day, maintaining the appearance of exactly 86400 seconds per day.
For the segment prior to 1972-11-03, extending back arbitrarily far, the consensus international time scale is defined to be UT1, applied proleptically, which is equivalent to the (mean) solar time on the prime meridian (Greenwich). In this segment, the Java Time-Scale is identical to the consensus international time scale. The exact boundary between the two segments is the instant where UT1 = UTC between 1972-11-03T00:00 and 1972-11-04T12:00.
The Java time-scale is used for all date-time classes. This includes Instant
, LocalDate
, LocalTime
, OffsetDateTime
, ZonedDateTime
and Duration
.
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Instant
may have unpredictable results and should be avoided. The equals method should be used for comparisons.
java.time.ZonedDateTime
ZonedDateTime
is an immutable representation of a date-time with a time-zone. This class stores all date and time fields, to a precision of nanoseconds, and a time-zone, with a zone offset used to handle ambiguous local date-times. For example, the value “2nd October 2007 at 13:45.30.123456789 +02:00 in the Europe/Paris time-zone” can be stored in a ZonedDateTime
.
public final class ZonedDateTime extends Object implements Temporal, ChronoZonedDateTime, Serializable
This class handles conversion from the local time-line of LocalDateTime
to the instant time-line of Instant. The difference between the two time-lines is the offset from UTC/Greenwich
, represented by a ZoneOffset
.
Converting between the two time-lines involves calculating the offset using the rules accessed from the ZoneId
. Obtaining the offset for an instant is simple, as there is exactly one valid offset for each instant. By contrast, obtaining the offset for a local date-time is not straightforward. There are three cases:
- Normal, with one valid offset. For the vast majority of the year, the normal case applies, where there is a single valid offset for the local date-time.
- Gap, with zero valid offsets. This is when clocks jump forward typically due to the spring daylight savings change from “winter” to “summer”. In a gap there are local date-time values with no valid offset.
- Overlap, with two valid offsets. This is when clocks are set back typically due to the autumn daylight savings change from “summer” to “winter”. In an overlap there are local date-time values with two valid offsets.
Any method that converts directly or implicitly from a local date-time to an instant by obtaining the offset has the potential to be complicated.
For Gaps, the general strategy is that if the local date-time falls in the middle of a Gap, then the resulting zoned date-time will have a local date-time shifted forwards by the length of the Gap, resulting in a date-time in the later offset, typically “summer” time.
For Overlaps, the general strategy is that if the local date-time falls in the middle of an Overlap, then the previous offset will be retained. If there is no previous offset, or the previous offset is invalid, then the earlier offset is used, typically “summer” time.. Two additional methods, withEarlierOffsetAtOverlap()
and withLaterOffsetAtOverlap()
, help manage the case of an overlap.
This class should be viewed primarily as the combination of a LocalDateTime
and a ZoneId
. The ZoneOffset
is a vital, but secondary, piece of information, used to ensure that the class represents an instant, especially during a daylight savings overlap.
This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of ZonedDateTime
may have unpredictable results and should be avoided. The equals method should be used for comparisons.
A ZonedDateTime
holds state equivalent to three separate objects, a LocalDateTime
, a ZoneId
and the resolved ZoneOffset
. The offset and local date-time are used to define an instant when necessary. The zone ID is used to obtain the rules for how and when the offset changes. The offset cannot be freely set, as the zone controls which offsets are valid.
2. Conversion
In this section we will learn how to convert a time represented as Instant
to a ZonedDateTime
object. Let say we want to convert the current time into a ZonedDateTime
. First we will create an Instant object as below:
Instant instant = Instant.now();
Now we will call the atZone()
method on the instant object to convert it to a ZonedDateTime
object.
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("Asia/Kolkata"));
The atZone()
method combines this instant with a time-zone to create a ZonedDateTime
.This returns an ZonedDateTime
formed from this instant at the specified time-zone. An exception will be thrown if the instant is too large to fit into a zoned date-time. This method is equivalent to ZonedDateTime.ofInstant(this, zone)
. The ZoneId.of()
obtains an instance of ZoneId
from an ID ensuring that the ID is valid and available for use. This method parses the ID producing a ZoneId
or ZoneOffset
. A ZoneOffset
is returned if the ID is ‘Z’, or starts with ‘+’ or ‘-‘. The result will always be a valid ID for which ZoneRules
can be obtained.
Below is the full source code of the class:
InstantToZonedDateTime.java
import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; public class InstantToZonedDateTime { public static void main(String[] args) { Instant instant = Instant.now(); System.out.println("Instant: " + instant); ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("Asia/Kolkata")); System.out.println("ZonedDateTime: " + zonedDateTime); } }
3. Conclusion
In this article we learned about the Java Instant
and ZonedDateTime
classes and what they are used for. Then we learned how to convert a dat-time represented as an Instant
to a ZonedDateTime
.
Download the Source Code
You can download the full source code of this example here: Instant to ZonedDateTime.