I have a java.util.Date
object, or a java.util.Calendar
object. How do I convert that to the right type in java.time framework?
I have heard that we should now be doing the bulk of our business logic with java.time types. When working with old code not yet updated for java.time I need to be able to convert back and forth. What types map to java.util.Date
or java.util.Calendar
?
Yes, you definitely should be using the java.time framework whenever possible.
Avoid old date-time classes
The old date-time classes including
java.util.Date
,java.util.Calendar
, andjava.text.SimpleTextFormat
and such have proven to be poorly designed, confusing, and troublesome. Avoid them where you can. But when you must interoperate with these old types, you can convert between old and new.Read on for a basic introduction, somewhat over-simplified, to orient you in moving back-and-forth between the old and new date-time classes.
java.time
The java.time framework is defined by JSR 310, inspired by the highly-successful Joda-Time library, and extended by the ThreeTen-Extra project. The bulk of the functionality was back-ported to Java 6 & 7 in the ThreeTen-Backport project, with a further adaptation for Android in the ThreeTenABP project.
What java.time type matches
java.util.Date
? Well, ajava.util.Date
object basically represents a moment on the timeline in UTC, a combination of a date and a time-of-day. We can translate that to any of several types in java.time. Each is discussed below. Note that some new methods have been added to the old date-time classes to facilitate conversions.Instant
The building block in java.time is an
Instant
, a moment on the timeline in UTC with a resolution of nanoseconds.Generally you should do much of your business logic in UTC. In such work,
Instant
will be used frequently. Pass aroundInstant
objects, applying a time zone only for presentation to a user. When you do need to apply an offset or time zone, use the types covered further below.From
java.util.Date
toInstant
Given that both
Instant
andjava.util.Date
are a moment on the timeline in UTC, we can easily move from ajava.util.Date
to anInstant
. The old class has gained a new method,java.util.Date::toInstant
.You can go the other direction, from an
Instant
to ajava.util.Date
. But you may lose information about the fractional second. AnInstant
tracks nanoseconds, for up to nine digits after the decimal place such as2016-01-23T12:34:56.123456789Z
. Both java.util.Date & .Calendar are limited to milliseconds, for up to three digits after the decimal place such as2016-01-23T12:34:56.123Z
. In this example going fromInstant
toDate
means truncation of the456789
.From
java.util.Calendar
toInstant
What about a
java.util.Calendar
instead of ajava.util.Date
? Internal to theCalendar
object the date-time is tracked as a count of milliseconds from the epoch reference date-time of the first moment of 1970 in UTC (1970-01-01T00:00:00.0Z
). So this value can be converted easily to anInstant
.From
java.util.GregorianCalendar
toZonedDateTime
Even better, if your
java.util.Calendar
object is actually ajava.util.GregorianCalendar
you can easily go directly to aZonedDateTime
. This approach has the benefit of retaining the embedded time zone information.Downcast from the interface of
Calendar
to the concrete class ofGregorianCalendar
. Then call thetoZonedDateTime
andfrom
methods to go back and forth.Going the other direction…
As discussed above, beware that you may be losing information about the fraction of a second. The nanoseconds in the java.time type (
ZonedDateTime
) gets truncated to milliseconds in the.Calendar
/.GregorianCalendar
.OffsetDateTime
From an
Instant
we can apply an offset-from-UTC to move into a wall-clock time for some locality. An offset is a number of hours, and possibly minutes and seconds, ahead of UTC (eastward) or behind UTC (westward). TheZoneOffset
class represents this idea. The result is anOffsetDateTime
object.You can go the other direction, from an
OffsetDateTime
to ajava.util.Date
. Extract anInstant
and then proceed as we saw in code above. As discussed above, any nanoseconds get truncated to milliseconds (data loss).ZonedDateTime
Better yet, apply a full time zone. A time zone is an offset plus rules for handling anomalies such as Daylight Saving Time (DST).
Applying a
ZoneId
gets you aZonedDateTime
object. Use a proper time zone name (continent/region). Never use the 3-4 letter abbreviations commonly seen such asEST
orIST
as they are neither standardized nor unique.You can go the other direction, from an
ZonedDateTime
to ajava.util.Date
. Extract anInstant
and then proceed as we saw in code above. As discussed above, any nanoseconds get truncated to milliseconds (data loss).And we saw further above that a
ZonedDateTime
may be converted to aGregorianCalendar
.LocalDate
Sometimes you may want a date-only value, without time-of-day and without time zone. For that, use a
java.time.LocalDate
object.See this Question for more discussion, Convert java.util.Date to java.time.LocalDate, especially this Answer written by the main man behind the invention of both Joda-Time and java.time.
The key is to go through a
ZonedDateTime
(as generated in code above). We need a time zone to determine a date. The date varies around the world, with a new day dawning earlier in the east. For example, after midnight in Paris is a new day while still “yesterday” in Montréal. So while aLocalDate
does not contain a time zone, a time zone is required to determine aLocalDate
.Converting in the other direction from
LocalDate
to a date-time means inventing a time-of-day. You can choose any time-of-day that makes sense in your business scenario. For most people, the first moment of the day makes sense. You may be tempted to hard code that first moment as the time00:00:00.0
. In some time zones, that time may not be valid as the first moment because of Daylight Saving Time (DST) or other anomalies. So let java.time determine the correct time with a call toatStartOfDay
.LocalTime
On rare occasion you may want only a time-of-day without a date and without a time zone. This concept is represented by the
LocalTime
class. As discussed above withLocalDate
, we need a time zone to determine aLocalTime
even though theLocalTime
object does not contain (does not ‘remember’) that time zone. So, again, we go through aZonedDateTime
object obtained from anInstant
as seen above.LocalDateTime
As with the other two
Local…
types, aLocalDateTime
has no time zone nor offset assigned. As such you may rarely use this. It gives you a rough idea of a date-time but is not a point on the timeline. Use this if you mean some general date and some time that might be applied to a time zone.For example, “Christmas starts this year” would be
2016-12-25T00:00:00.0
. Note the lack of any offset or time zone in that textual representation of aLocalDateTime
. Christmas starts sooner in Delhi India than it does in Paris France, and later still in Montréal Québec Canada. Applying each of those areas’ time zone would yield a different moment on the timeline.