tl;dr
myPreparedStatement.setObject( // Pass java.time objects directly to database with JDBC 4.2 or later.
… ,
LocalDate.now() // Get current date today. Better to pass optional `ZoneId` time zone object explicitly than rely implicitly on JVM’s current default.
)
java.time
In Java 8 and later, the new java.time framework is now built-in. This successor to Joda-Time is defined by JSR 310 and extended by the ThreeTen-Extra project.
Hopefully we well eventually see the JDBC drivers updated to directly handle the new java.time types. But until then we continue to need the java.sql.* types. Fortunately, new methods have been added to conveniently convert between the types.
For a date-only, with no time-of-day and no time zone, the Java type is LocalDate
(quite similar to Joda-Time).
As for your concern about time zone related to a LocalDate
, it matters when your are translating a date-only to a date-time, to a moment on the timeline. A date-only is just a vague idea, with no real meaning, until you translate it to a time-span of moment on the timeline (midnight to midnight in some time zone). For example, determining "today" requires a time zone. In java.time we use the ZoneId
class.
LocalDate today = LocalDate.now( ZoneId.of( "America/Montreal" ) );
If omitted, your JVM’s current default time zone is used in determining the date. In other words, the following two lines are equivalent.
LocalDate today = LocalDate.now();
LocalDate today = LocalDate.now( ZoneId.systemDefault() );
I consider the first version, now()
, to be a poor API design choice. This implicit application of the JVM’s current default time zone causes no end of confusion, bugs, and misery among naïve developers. For one thing, the JVM's current default varies by machine, by host OS settings, and by sysadmins. Worse, the JVM’s current default can change at any moment, during runtime, by any code in any thread of any app within that JVM. So best practice is to always specify your desired/expected time zone.
Now that we have a java.time object for "today", how to get it into the database?
With JDBC 4.2 or later, directly exchange java.time
objects with your database.
myPreparedStatement.setObject( … , today ) ;
To retrieve:
LocalDate ld = myResultSet.getObject( … , LocalDate.class ) ;
If you cannot upgrade yet to JDBC 4.2 or later: Use a java.sql.Date
object. In Java 8, that old class gained new methods, toLocalDate
and valueOf
. The latter is our bridge from the java.time type to the java.sql type.
java.sql.Date sqlToday = java.sql.Date.valueOf( today );
From there do the usual PreparedStatement
handling.
myPreparedStatement.setDate( 1 , sqlToday );
Date-only vs Date-time
Perhaps you have concerns about such a date-only fitting your business needs.
If you need to know, for example, if a contract was signed by the end of the day for legal reasons, then date-only is the wrong data-type if mean a specific moment such as the stroke of midnight in Montréal. A new day dawns earlier in Paris than in Montréal, so "today" in Paris is "yesterday" in Montréal. If your contract deadline is defined legally as the end of the day in Montréal, then you must apply a time zone. To apply a time zone, you must have a date-time rather than a date-only. You can make a jump from the LocalDate
into a ZonedDateTime
, but I consider that overly complex. Your database should have used a date-time type from the beginning.
In Postgres, a date-time type means the TIMESTAMP WITH TIME ZONE
type. That name is a misnomer as the time zone is not actually stored. Think of it as “timestamp with respect for time zone”. Postgres uses any offset-from-UTC or time zone information accompanying incoming data to adjust to UTC, and that offset/zone info is then discarded. The other type, TIMESTAMP WITHOUT TIME ZONE
, ignores the offset/zone info entirely, and this is the wrong behavior for most any business app.
I suspect many developers or DBAs may make the naïve mistake of thinking by intuition that the date-only has obvious meaning. But in fact if you have specific or strict moment-oriented needs, such as legalities regarding events such as “contract signed”, “invoice received”, or “company executive hired”, then a date-time value should be used rather than date-only.
In other words, regarding the Question’s author’s comment:
And I expected there should be a way to work with dates without resorting to time instances.
No, I would argue that is asking for trouble. If moments matter, use a date-time rather than a date-only.
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.
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.