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
您正在使用麻烦的旧日期时间类,这些类现在已被遗留,被现代的java.time类取代。
显然,您是在数据库中某个整数类型的列中存储一个时刻。那是不幸的。相反,您应该使用诸如SQL-standard之类的列TIMESTAMP WITH TIME ZONE
。但是,对于这个答案,我们将使用已有的东西。
如果您的记录代表具有毫秒分辨率的时刻,并且您希望整天记录所有记录,那么我们需要一个时间范围。我们必须有一个开始时刻和一个停止时刻。您的查询只有一个日期时间条件,应该有一对。
该LocalDate
级表示没有时间一天和不同时区的日期,唯一的价值。
时区对于确定日期至关重要。在任何给定时刻,日期都会在全球范围内变化。例如,法国巴黎午夜过后的几分钟是新的一天,而在魁北克蒙特利尔仍然是“昨天” 。
如果未指定时区,则JVM隐式应用其当前的默认时区。该默认值可能随时更改,因此您的结果可能会有所不同。最好将所需/预期时区明确指定为参数。
指定适当的时区名称,格式continent/region
,如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用3-4个字母的缩写,例如EST
或,IST
因为它们不是真实的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
如果要使用JVM的当前默认时区,请提出要求并作为参数传递。如果省略,则隐式应用JVM的当前默认值。最好明确一点,因为在运行时的任何时候都可以通过JVM中任何应用程序的任何线程中的任何代码来更改默认值。
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
或指定一个日期。您可以用数字设置月份,一月至十二月的理智编号为1-12。
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.
或者,最好使用Month
预定义的枚举对象,一年中的每个月使用一个。提示:Month
在整个代码库中使用这些对象,而不是仅使用整数,可以使您的代码更具自文档性,确保有效值并提供类型安全性。
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
随着LocalDate
在手,我们接下来需要变换成一对的时刻,一天的开始和结束的。不要假设一天从一天中的00:00:00开始。由于诸如夏时制(DST)之类的异常,一天可能在另一个时间(如01:00:00)开始。因此,让java.time确定一天的第一时刻。我们通过一个ZoneId
参数来LocalDate::atStartOfDay
查找任何此类异常。结果是ZonedDateTime
。
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
通常,定义时间跨度的最佳方法是“半开”方法,其中开始是包容性的,而结尾是排他性的。因此,一天从第一刻开始,一直到第二天的第一刻,但不包括这一天。
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
我们查询一整天不能使用SQL命令BETWEEN
。该命令是Fully-Closed([]
)(包括开始和结束位置),在这里我们需要Half-Open([)
)。我们使用了一对条件>=
和<
。
您的列名称不正确。避免各种数据库保留的千字中的任何一个。placed
在此示例中使用。
您的代码应该使用?
占位符来指定我们的时刻。
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
但是我们ZonedDateTime
手头有对象,而您的数据库显然正在存储整数,如上所述。如果您已经定义了列正常,我们可以简单地将ZonedDateTime
与任何JDBC驱动程序支持JDBC 4.2或更高版本的对象。
但是相反,我们需要在整秒内获得一个从纪元开始的计数。我假设您的纪元参考日期是1970年UTC的第一刻。提防可能的数据丢失,因为ZonedDateTime
该类具有纳秒级的分辨率。在下面的行中,任何小数秒都会被截断。
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
现在,我们准备将这些整数传递给上面定义的SQL代码。
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
从中检索整数值时ResultSet
,您可以转换为Instant
对象(始终以UTC为单位),也可以转换为对象(ZonedDateTime
具有指定的时区)。
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
关于java.time
该java.time框架是建立在Java 8和更高版本。这些类取代麻烦的老传统日期时间类,如java.util.Date
,Calendar
,和SimpleDateFormat
。
现在处于维护模式的Joda-Time项目建议迁移到java.time类。
要了解更多信息,请参见Oracle教程。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
在哪里获取java.time类?
该ThreeTen-额外项目与其他类扩展java.time。该项目是将来可能向java.time添加内容的试验场。你可能在这里找到一些有用的类,比如Interval
,YearWeek
,YearQuarter
,和更多。