tl;dr
Apparently, you inadvertently used the class java.sql.Date
where you thought you were using java.util.Date
. But that issue is moot: use java.time classes instead of those terrible old date-time classes.
myPreparedStatement.setObject( … , myLocalDateTime ) ;
…and…
myResultSet.getObject( … , LocalDateTime.class )
Check your imports
Apparently you are inadvertently using java.sql.Date
which pretends to have no time of day. Unfortunately, it’s terrible class design inherits from java.util.Date
which does have a time-of-day. So the sql
sets its time to 00:00:00. And the documentation tells us to ignore the fact of their inheritance. Terrible design, terrible names, terrible classes all around.
Avoid legacy date-time classes
For all these reasons and more, these old classes were supplanted years ago by the java.time classes.
JDBC 4.2
As of JDBC 4.2, we can exchange java.time classes with the database. No need to ever touch java.util.Date
, java.sql.Date
, java.sql.Timestamp
again.
Without zone
A LocalDateTime
should be stored in a database column of a type akin to the SQL-standard type TIMESTAMP WITHOUT TIME ZONE
. These type intentionally lack any concept of time zone or offset-from-UTC. As such, these types cannot represent a moment, cannot hold a specific point on the timeline. These are likely not what you want in most business scenarios.
myPreparedStatement.setObject( … , myLocalDateTime ) ;
Retrieval:
myLocalDateTime = myResultSet.getObject( … , LocalDateTime.class ) ;
If they cannot represent a moment, what good are these types? In what scenarios is the use of java.time.LocalDateTime
class & TIMESTAMP WITHOUT TIME ZONE
column appropriate? These three:
- The zone or offset is unknown.
This is bad. This is faulty data. Analogous to having a price/cost without knowing the currency. You should be rejecting such data, not storing it.
- The intention is “everywhere”, as in, every time zone.
For example, a corporate policy that states “All our factories will break for lunch at 12:30" means the factory in Delhi will break hours before the factory in Düsseldorf which breaks hours before the factory in Detroit.
- A specific moment in the future is intended, but we are afraid of politicians redefining the time zone.
Governments change the rules of their time zones with surprising frequency and with surprisingly little warning or even no warning at all. So if you want to book an appointment at 3 PM on a certain date, and you really mean 3 PM regardless of any crazy decision a government might make in the interim, then store a LocalDateTime
. To print a report or display a calendar, dynamically apply a time zone (ZoneId
) to generate a specific moment (ZonedDateTime
or Instant
). This must be done on-the-fly rather than storing the value.
With zone
In contrast, to store a moment, use a database column of a type akin to the SQL-standard type TIMESTAMP WITH TIME ZONE
. For such a column, in Java use Instant
class. This class represents a moment in UTC with a resolution of nanoseconds.
myPreparedStatement.setObject( … , myInstant ) ;
Retrieval:
myInstant = myResultSet.getObject( … , Instant.class ) ;
Applying time zone
If you are certain your LocalDateTime
was meant implicitly to represent a moment in the wall-clock time used by the people of a particular region (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( "Pacific/Auckland" ) ;
ZonedDateTime zdt = myLocalDateTime.atZone( z ) ;
Then extract a Instant
object to adjust into UTC.
Instant instant = zdt.toInstant();
Store in database.
myPreparedStatement.setObject( … , instant ) ;
Retrieve from database.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
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.