I am trying to set a server agnostic date time in my database and I believe the best practice to do so is to set a UTC DateTime. My db server is Cassandra and the db driver for Java understands only the Date type.
So assuming that in my code I am using the new Java 8 ZonedDateTime to get the UTC now (ZonedDateTime.now(ZoneOffset.UTC)
), how can I convert this ZonedDateTime instance to the "legacy" Date class?
If you are interested in now only, then simply use:
tl;dr
Though there was no point in that code above. Both
java.util.Date
andInstant
represent a moment in UTC, always in UTC. Code above has same effect as:No benefit here to using
ZonedDateTime
. If you already have aZonedDateTime
, adjust to UTC by extracting aInstant
.Other Answer Correct
The Answer by ssoltanid correctly addresses your specific question, how to convert a new-school java.time object (
ZonedDateTime
) to an old-schooljava.util.Date
object. Extract theInstant
from the ZonedDateTime and pass tojava.util.Date.from()
.Data Loss
Note that you will suffer data loss, as
Instant
tracks nanoseconds since epoch whilejava.util.Date
tracks milliseconds since epoch.Your Question and comments raise other issues.
Keep Servers In UTC
Your servers should have their host OS set to UTC as a best practice generally. The JVM picks up on this host OS setting as its default time zone, in the Java implementations that I'm aware of.
Specify Time Zone
But you should never rely on the JVM’s current default time zone. Rather than pick up the host setting, a flag passed when launching a JVM can set another time zone. Even worse: Any code in any thread of any app at any moment can make a call to
java.util.TimeZone::setDefault
to change that default at runtime!Cassandra
Timestamp
TypeAny decent database and driver should automatically handle adjusting a passed date-time to UTC for storage. I do not use Cassandra, but it does seem to have some rudimentary support for date-time. The documentation says its
Timestamp
type is a count of milliseconds from the same epoch (first moment of 1970 in UTC).ISO 8601
Furthermore, Cassandra accepts string inputs in the ISO 8601 standard formats. Fortunately, java.time uses ISO 8601 formats as its defaults for parsing/generating strings. The
Instant
class’toString
implementation will do nicely.Precision: Millisecond vs Nanosecord
But first we need to reduce the nanosecond precision of ZonedDateTime to milliseconds. One way is to create a fresh Instant using milliseconds. Fortunately, java.time has some handy methods for converting to and from milliseconds.
Example Code
Here is some example code in Java 8 Update 60.
Or according to this Cassandra Java driver doc, you can pass a
java.util.Date
instance (not to be confused withjava.sqlDate
). So you could make a j.u.Date from thatinstantTruncatedToMilliseconds
in the code above.If doing this often, you could make a one-liner.
But it would be neater to create a little utility method.
Notice the difference in all this code than in the Question. The Question’s code was trying to adjust the time zone of the ZonedDateTime instance to UTC. But that is not necessary. Conceptually:
We just extract the Instant part, which is already in UTC (basically in UTC, read the class doc for precise details).
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as
java.util.Date
,Calendar
, &SimpleDateFormat
.The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for
java.sql.*
classes.Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as
Interval
,YearWeek
,YearQuarter
, and more.You can convert ZonedDateTime to an instant, which you can use directly with Date.
You can do this using the java.time classes built into Java 8 and later.
For a docker application like beehuang commented you should set your timezone.
Alternatively you can use withZoneSameLocal. For example:
2014-07-01T00:00+02:00[GMT+02:00] is converted by
to Tue Jul 01 00:00:00 CEST 2014 and by
to Mon Jun 30 22:00:00 UTC 2014
If you are using the ThreeTen backport for Android and can't use the newer
Date.from(Instant instant)
(which requires minimum of API 26) you can use:or:
Please also read the advice in Basil Bourque's answer