让您的工具担心毫秒数
您正在与 SQL、JDBC 和 Java 的类型系统作斗争。不要担心毫秒。您的数据库、驱动程序和 Java 已经在为您执行此操作,但更精细(MySQL 中为微秒,Java 中为纳秒)。
我想将我所有的瞬间表示为自 Unix 纪元以来的毫秒数。
这正是 MySQL 在其 TIMESTAMP 数据类型中为您所做的,只是更精细 - 自 1970 年开始以 UTC 为单位的微秒计数。致quote the doc:
TIMESTAMP 的范围为 '1970-01-01 00:00:01' UTC 到 '2038-01-19 03:14:07' UTC。
…
TIMESTAMP 值可以包括一个尾随小数秒部分,精度高达微秒(6 位)
您对留在 UTC 的担忧……
将 TIMESTAMP 列检索为 milliseconds_since_epoch,而不必担心由于客户端或服务器更改时区而导致的值更改
……已经解决了。 MySQL 以 UTC 格式存储 TIMESTAMP,您的 JDBC 驱动程序应该以 UTC 格式检索该值,而 java.time 类 Instant 以 UTC 格式存储该值。 Instant 类代表UTC 时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。 所以您一直只有 UTC。
尽可能避免使用麻烦的旧日期时间类。而是使用它们的替代品 java.time 类。如果您的 JDBC driver 更新为 JDBC 4.2 及更高版本,您可以直接使用 java.time 类型。
Instant instant = Instant.now() ; // Current moment in UTC with resolution up to nanoseconds.
myPreparedStatement.setObject( … , instant ) ;
……和……
Instant instant = myResultSet.getObject( … , Instant.class ) ;
如果您的 JDBC 驱动程序尚不兼容,请暂时使用旧的 java.sql 类型,但立即与 java.time 进行转换。不要使用 java.sql 类型执行业务逻辑。
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
……和……
Instant instant = myResultSet.getTimestamp( … ).toInstant() ;
当您想要调整 UTC 并进入时区(例如向用户展示)时,应用 ZoneId 以获取 ZonedDateTime。搜索 Stack Overflow 以获取更多示例。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ; // Same moment, different wall-clock time.
如果你坚持访问毫秒以来的纪元,请询问Instant 对象。但要注意数据丢失,因为该时刻可能会保存来自 MySQL 的微秒或来自其他来源的纳秒。要求毫秒意味着截断更小的一秒。
long millisSinceEpoch = instant.toEpochMilli() ;
往另一个方向发展。
Instant instant = Instant.ofEpochMilli( millisSinceEpoch ) ;
关于java.time
java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.Date、Calendar 和 SimpleDateFormat。
Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。
要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。
从哪里获得 java.time 类?
ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如Interval、YearWeek、YearQuarter 和more。