tl;dr
- Never use the legacy classes, use only java.time.
- When you must, convert using new methods added to the old classes.
- As of JDBC 4.2, exchange java.time objects with your database, never strings or
java.sql
types.
- Do most of your work in UTC. Adjust into a time zone only for presentation.
- When communicating date-time values as text, use ISO 8601 formats.
- Search Stack Overflow for more info.
Avoid legacy date-time classes
The terribly troublesome old date-time classes such as java.util.Date
, java.util.Calendar
, java.text.SimpleDateFormat
, java.sql.Timestamp
, and java.sql.Date
are all entirely supplanted by the java.time classes. No need to ever touch those awful old classes again.
The java.time classes have been bundled with Java 8 and later for a few years now. They have proven themselves to be a reliable, industry-leading framework for date-time work. They were inspired by the lessons learned from the excellent Joda-Time project (now in maintenance-mode). So the java.time classes have “an old soul”, and are not some limited “1.0” rough draft.
Conversion
For interoperating with old code not yet updated to the java.time classes, you can easily convert back-and-forth between the legacy classes and modern classes. For conversions, look to new methods added to the old classes. Read Questions such as this to learn more.
Database
As of JDBC 4.2 and later, you can directly exchange java.time objects with your database. No need to ever use the badly-designed java.sql classes again for date-time values.
Generally best to use Instant
for values in a column of type akin to the SQL-standard TIMESTAMP WITH TIME ZONE
.
myPreparedStatement.setObject( … , instant ) ;
And retrieval.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
ISO 8601
When exchanging date-time values outside of a JVM, serialize to text using the standard ISO 8601 formats. These formats are designed to be useful and practical, easy to parse by machines, easy to read by humans across cultures.
The java.time classes use the ISO 8601 formats by default when parsing/generating strings. The ZonedDateTime
class extends the standard by wisely appending the name of the zone in square brackets.
Zones
data that I used to get as a Date object to now get it as a ZonedDateTime object?
The java.util.Date
class is directly replaced by Instant
. Both represent a moment in UTC. The modern class resolves to nanoseconds instead of milliseconds.
Instant instant = myJavaUtilDate.toInstant() ; // Convert from legacy class to modern.
In databases such as Postgres, a column of type TIMESTAMP WITH TIME ZONE
actually does not have its time zone saved. Instead, any offset or zone passed with an incoming value is used by the database to adjust into UTC. The resulting UTC moment is saved to the database, and the original zone/offset is discarded. If you need the original zone/offset, you must manually save it to an extra column yourself.
So, generally best to pass and fetch Instant
objects with your database for a moment on the timeline.
To learn about the concept of a date-time not on the timeline, search for LocalDateTime
class, and read postings like this Question, What's the difference between Instant and LocalDateTime?.
Be aware that the SQL standard barely touches on the tricky topic of date-time handling. It defines a few types but says little about behavior. So databases vary widely in their implementations. Be sure to study and experiment to verify your understanding of your database and driver.
read the data from JDBC, Saxon, etc as ZonedDateTime objects?
Generally the best practice is to work in UTC. That means the Instant
class rather than ZonedDateTime
class.
If receiving a Date
, convert to Instant
as shown above.
Usually you want to do your business logic, storage, exchange, logging, and tracing/debugging all in UTC. Apply a time zone only where required by business requirements or for presentation to a user.
With a Instant
in hand, you can adjust to the wall-clock time used by the people of a particular region by assigning a time zone. Apply a ZoneId
to get a ZonedDateTime
.
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
Search Stack Overflow
Your Question is really a duplicate of many others such as this. I suggest you search Stack Overflow for the various java.time class names. You will learn much as there have been plenty of questions and answers already posted.
Compatible libraries
Many Java libraries have been updated to work with java.time types.
Check for the latest versions of libraries you may be using, and read their documentation to learn about added support.
If not directly supported, libraries that accept converters/formatters/plugins will likely have some java.time ones already written. Search for them, or ask on the sister site: Software Recommendations Stack Exchange.
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.