tl;dr
LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
.atStartOfDay( ZoneId.of( "Africa/Tunis" ) )
.toEpochSecond()
…
LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
.plusDays( 1 )
.atStartOfDay( ZoneId.of( "Africa/Tunis" ) )
.toEpochSecond()
…
"SELECT * FROM orders WHERE placed >= ? AND placed < ? ; "
…
myPreparedStatement.setObject( 1 , start )
myPreparedStatement.setObject( 2 , stop )
java.time
You are using troublesome old date-time classes that are now legacy, supplanted by the modern java.time classes.
Apparently you are storing a moment in your database in a column of some integer type. That is unfortunate. You should instead be using a column of a type such as the SQL-standard TIMESTAMP WITH TIME ZONE
. But, for this Answer, we will work with what we have.
If your records represent moments with a resolution of milliseconds, and you want all the records for an entire day, then we need a time range. We must have a start moment and a stop moment. Your query has only a single date-time criterion where it should have had a pair.
The LocalDate
class represents a date-only value without time-of-day and without time zone.
A time zone is crucial in determining a date. For any given moment, the date varies around the globe by zone. For example, a few minutes after midnight in Paris France is a new day while still “yesterday” in Montréal Québec.
If no time zone is specified, the JVM implicitly applies its current default time zone. That default may change at any moment, so your results may vary. Better to specify your desired/expected time zone explicitly as an argument.
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" ) ;
LocalDate today = LocalDate.now( z ) ;
If you want to use the JVM’s current default time zone, ask for it and pass as an argument. If omitted, the JVM’s current default is applied implicitly. Better to be explicit, as the default may be changed at any moment during runtime by any code in any thread of any app within the JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Or specify a date. You may set the month by a number, with sane numbering 1-12 for January-December.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Or, better, use the Month
enum objects pre-defined, one for each month of the year. Tip: Use these Month
objects throughout your codebase rather than a mere integer number to make your code more self-documenting, ensure valid values, and provide type-safety.
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
With a LocalDate
in hand, we next need to transform that into a pair of moment, the start and stop of the day. Do not assume the day starts at 00:00:00 time-of-day. Because of anomalies such as Daylight Saving Time (DST), the day may start at another time such as 01:00:00. So let java.time determine the first moment of the day. We pass a ZoneId
argument to LocalDate::atStartOfDay
to look up any such anomalies. The result is a ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Generally the best approach to defining a span of time is the Half-Open approach where the beginning is inclusive while the ending is exclusive. So a day starts with its first moment and runs up to, but not including, the first moment of the next day.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Our query for an entire day cannot make use of SQL command BETWEEN
. That command is Fully-Closed ([]
) (both beginning and ending are inclusive) where as we want Half-Open ([)
). We use a pair of criteria >=
and <
.
Your column is poorly named. Avoid any of the thousand words reserved by various databases. Let’s use placed
in this example.
Your code should have used ?
placeholders in which to specify our moments.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
But we have ZonedDateTime
objects in hand, while your database apparently is storing integers as discussed above. If you had defined your column properly we could simply pass the ZonedDateTime
objects with any JDBC driver supporting JDBC 4.2 or later.
But instead we need to get a count-from-epoch in whole seconds. I will assume your epoch reference date is the first moment of 1970 in UTC. Beware of possible data loss, as the ZonedDateTime
class is capable of nanosecond resolution. Any fractional second will be truncated in the following lines.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Now we are ready to pass those integers to our SQL code defined above.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
When you retrieve your integer values from the ResultSet
, you can transform into Instant
objects (always in UTC), or into ZonedDateTime
objects (with an assigned time zone).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
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.
PreparedStatement
with incorrect syntax. You cannot embed Java variables in the text of your SQL string. Instead, embed?
in the SQL string, then call theset
methods to pass values to the PreparedStatement. See the correct Answer by Sujay and Answer by tenorsax. – Mingreliandate
. Append a trailing underscore to avoid such collisions. The SQL spec explicitly promises to never use a trailing underscore on any keyword or reserved word. – Mingreliandate
datatype if you need the date only, atimestamp
if you need the time. – Tamelatamelessjava.sql.Timestamp
to an equally outdatedjava.sql.Date
? This is not the case. Yournew java.sql.Date(timeStamp.getTime())
is a correctly working conversion. – Tamelatamelessjava.sql.Timestamp
andjava.sql.Date
. Those classes are poorly designed and long outdated. Instead useOffsetDateTime
for a timestamp andLocalDate
for a date; both from are java.time, the modern Java date and time API. – Tamelatameless