我在数据库中存储了许多行,该时刻的时间戳以毫秒为单位。现在,如果我需要检索给定日期的所有行,例如今天,如何正确创建当天的开始和结束毫秒?
我简要地了解 SimpleDateFormat 和 Calendar.getInstance((,但我是否需要进行字符串操作(我想避免(来仅获取今天的日期,添加小时部分,然后将其转换回毫秒,或者有更好的方法可以做到这一点?
由于您在问题中没有提供任何代码,请允许我给您一个一般性的回答作为回应。
您要查找的是两个日期/时间,today
和tomorrow
,都指定了"0小时,0分钟,0秒"。
today <= date AND date < tomorrow
请注意两种不同的比较。
最简单的技术是使用 DateFormat
:
String input = "Sat Feb 17 2013";
Date date = new SimpleDateFormat("EEE MMM dd yyyy", Locale.ENGLISH).parse(input);
long milliseconds = date.getTime();
String input1="Sun Feb 18 2013"
Date inputNextDay = new SimpleDateFormat("EEE MMM dd yyyy", Locale.ENGLISH).parse(input);
long millisecondsForNextDay=inputNextDay.getTime();
要获取落在特定日期的行,只需查找时间戳的毫秒值介于 milliseconds
和 millisecondsForNextDay
之间的行:
if(rowsTimestampSeconds>milliseconds && rowsTimestampSeconds<millisecondsForNextDay){
//get the row
}
您可以使用 GregorianCalendar 类来执行此操作,而无需任何字符串。
日历日历=新的公历(年,月,日(; long start_of_day_millis = calendar.getTimeInMillis((; calendar.add(Calendar.DAY_OF_MONTH, 1(; long next_day_millis = calendar.getTimeInMillis((;
使用calendar.add(Calendar.DAY_OF_MONTH);
而不是仅将 (24*60*60*1000( 添加到第一个毫秒值的原因是考虑到跳跃。除了众所周知的闰年之外,还会出现闰秒。请参阅此链接: http://www.timeanddate.com/time/leapseconds.html
确保您的表达式包含 start_of_day_millis
(大于或等于(和排除next_day_millis
(小于(。
即:if(test_time >= start_of_day_millis && test_time < next_day_millis)
更新:
如果你想要今天的日期,你可以省略日历构造函数中的所有参数,只调用new GregorianCalendar()
,但你需要确保在使用日历之前,小时、分钟、秒和毫秒字段通过调用calendar.set(Calendar._FIELD_NAME_, 0);
将其清零,因为它将被初始化到创建对象的确切时刻。
您应该为数据库列使用日期时间类型来存储日期时间数据,而不是毫秒整数。SQL 标准定义了几种日期时间类型。但是对日期时间的支持差异很大,Postgres是最好的之一。
既然你标记了 Java,请阅读这个问题和我的答案,了解如何使用 Java 来确定今天和明天的第一个时刻。那里使用的半开放方法在日期时间工作中很常见。半开放是指开始是包容性的,而结尾是排他性的时间段。对于 SQL,这意味着不使用 BETWEEN
运算符。
java.time
java.time 框架内置于 Java 8 及更高版本中,并向后移植到 Java 6 和 7 以及 Android。有关详细信息,请阅读我的另一个答案。
获得今天和明天的第一个时刻。请注意,时区对于确定日期和"今天"的含义至关重要。对于任何给定时刻,日期因时区而异。新的一天在东方开始得更早。例如,巴黎午夜过后不久,蒙特利尔仍然是"昨天"。
Instant instant = Instant.now();
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );
ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );
有一天,我们可能会看到JDBC驱动程序更新为直接处理java.time类型。事实上,如果您分别在ResultSet
和PreparedStatement
上调用getObject
和setObject
,则符合 JDBC 4.2 的驱动程序可能会起作用。但如果没有,请回退到使用 java.sql 类型。请注意添加到这些旧类的新方法,包括 java.sql.Timestamp
。from
方法采用一个Instant
,我们可以从ZonedDateTime
对象中提取该。
java.sql.Timestamp tsStart = java.sql.Timestamp.from( zdtStart.toInstant() );
java.sql.Timestamp tsStop = java.sql.Timestamp.from( zdtTomorrowStart.toInstant() );
现在将这两个变量设置为PreparedStatement
上的参数。请注意比较运算符,测试从一天的第一个时刻(>=
(开始并运行到但不包括第二天的第一个时刻(<
(的可能值。
String sql =
"SELECT * FROM event_" +
"WHERE when_ >= ? " +
"AND when_ < ? " +
";" ;
…
pstmt.setTimestamp( 1 , tsStart );
pstmt.setTimestamp( 2 , tsStop );
如果您确实存储整数而不是使用日期时间类型,并且您将毫秒存储为 UTC 中 1970 年第一个时刻的纪元参考日期时间的计数,那么您可以从每个Instant
中提取一个数字。请记住,java.time 类使用更精细的纳秒分辨率,某些数据库(如 H2 数据库(和某些数据库(如 Postgres(以微秒的分辨率捕获日期时间。因此,截断到毫秒可能意味着数据丢失。
long millisStart = tsStart.toInstant().toEpochMilli();
long millisStop = tsStop.toInstant().toEpochMilli();
拨打setLong
PreparedStatement
。
pstmt.setLong( 1 , millisStart );
pstmt.setLong( 2 , millisStop );
关于 java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧传统日期时间类,如java.util.Date
、Calendar
和SimpleDateFormat
。
Joda-Time 项目现在处于维护模式,建议迁移到 java.time 类。
要了解更多信息,请参阅 Oracle 教程。并搜索堆栈溢出以获取许多示例和解释。规范为 JSR 310。
您可以直接与数据库交换java.time对象。使用符合 JDBC 4.2 或更高版本的 JDBC 驱动程序。不需要字符串,不需要java.sql.*
类。
从哪里获得java.time类?
- Java SE 8、Java SE 9、Java SE
- 10、Java SE 11 及更高版本 - 标准 Java API 的一部分,具有捆绑实现。
- Java 9添加了一些小功能和修复。
Java SE - 6 和 Java SE 7
- 大多数java.time功能在ThreeTen-Backport中向后移植到Java 6和7。
- 人造人
- java.time 类的 Android 捆绑包实现的更高版本。
- 对于早期的Android(<26(,ThreeTenABP项目适应了ThreeTen-Backport(如上所述(。请参阅如何使用ThreeTenABP...。
ThreeTen-Extra项目通过额外的类扩展了java.time。这个项目是未来可能添加到java.time的试验场。你可以在这里找到一些有用的类,如Interval
、YearWeek
、YearQuarter
等。