我有一个时间戳编码为String-例如,"2012-02-12T09:08:13.123456-0400"来自Oracle数据库。
我可以想到的唯一读取此时间戳的方法是使用Timestamp.valueOf(),并且需要一种格式yyyy-[m]m-[d]d hh:mm:ss[.f...]
我坚信这是在不损失精度的情况下读取时间的唯一方法,因为其他方法不支持上述示例中包含的纳秒精度".123456"。
考虑到这一点,我可以简单地修剪所需的值以适合所需的格式。因此,原始字符串将被转换:
"2012-02-12T09:08:13.123456-0400""2012-02-12 09:08:13.123456"如果这样做,我将删除"-0400"时区偏移量。这对我来说是一个危险的信号,直到我看到这篇文章为止。提出的答案之一指出,
我认为正确答案应该是java.sql.Timestamp不是特定于时区的。时间戳是java.util.Date和单独的纳秒值的组合。此类中没有时区信息。因此,就像Date一样,该类仅保留自1970年1月1日00:00:00 GMT + nanos起的毫秒数。
为了向我证明不需要偏移量,我编写了一个简单的集成测试。
将此时间戳插入数据库:中"2015-09-08 11:11:12.123457"。使用Java读取数据库,并打印出详细信息。我得到"2015-09-08 11:11:12.123457",这是相同的值。因为我的JVM和Oracle DB在同一台计算机上运行,所以这没问题。
java.sql.Timestamp吗?org.threeten.bp.OffsetDateTime odt = \n OffsetDateTime.parse(\n "2012-02-12T09:08:13.123456-0400",\n org.threeten.bp.format.DateTimeFormatter.ofPattern( "yyyy-MM-dd\'T\'HH:mm:ssZ" ) // Specify pattern as workaround for Java 8 bug in failing to parse if optional colon is not present.\n )\n;\nRun Code Online (Sandbox Code Playgroud)\n\n您应该检索一个对象,即日期时间对象,特别是 java.time 对象,而不是从数据库接收字符串。
\n\njava.time 类取代了麻烦的旧日期时间类,包括java.sql.Timestamp. 如果您的JDBC 驱动程序支持 JDBC 4.2 及更高版本,则可以直接传递和接收 java.time 对象。
Instant该类表示UTCInstant时间线上的时刻,分辨率为纳秒(最多九 (9) 位小数)。因此,这相当于包括对输入数据的六位数微秒的支持,因此根据您的问题的要求不会损失精度。java.sql.Timestamp
Instant instant = myResultSet.getObject( \xe2\x80\xa6 , Instant.class ) ;\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\ninstant.toString(): 2012-02-12T13:08:13.123456Z
\n
ZonedDateTime如果您想通过特定区域的挂钟时间的镜头看到同一时刻,请应用 aZoneId来获取ZonedDateTime对象。
ZoneId z = ZoneId.of( "America/St_Thomas" ) ;\nZonedDateTime zdt = instant.atZone( z ) ;\nRun Code Online (Sandbox Code Playgroud)\n\n\n\n\nzdt.toString(): 2012-02-12T09:08:13.123456-04:00[美国/St_Thomas]
\n
OffsetDateTime至于如何将字符串理解2012-02-12T09:08:13.123456-0400为日期时间值的直接问题,请解析为OffsetDateTime.
时区的名称格式为continent/region,表示由夏令时 (DST) 等异常引起的区域\xe2\x80\x99s 偏移的过去、现在和未来更改的历史记录。我们知道您的字符串所在的时区,因此我们使用OffsetDateTime而不是ZonedDateTime。
OffsetDateTime odt = OffsetDateTime.parse( "2012-02-12T09:08:13.123456-0400" ) ;\nRun Code Online (Sandbox Code Playgroud)\n\n好吧,上面那行代码应该可以工作,但是在 Java 8 中,在解析小时和分钟之间缺少可选冒号字符的偏移量时存在一个小错误。所以-04:00在Java 8中会解析但不会-0400. Java 9 中修复了错误。您的字符串确实符合 java.time 类中默认使用的日期时间格式的 ISO 8601 标准。提示:通常最好始终使用冒号格式化偏移量,小时/分钟和填充零 \xe2\x80\x93 我已经看到其他协议和库只期望这样的完整格式。
在迁移到 Java 9 之前,请显式指定格式化模式,而不是依赖隐式默认模式,作为此错误的解决方法。
\n\nOffsetDateTime odt = \n OffsetDateTime.parse(\n "2012-02-12T09:08:13.123456-0400",\n DateTimeFormatter.ofPattern( "yyyy-MM-dd\'T\'HH:mm:ssZ" ) // Specify pattern as workaround for Java 8 bug in failing to parse if optional colon is not present.\n )\n;\nRun Code Online (Sandbox Code Playgroud)\n\n如果您的 JDBC 驱动程序尚不兼容 JDBC 4.2,请检索对象java.sql.Timestamp,仅用于短暂使用使用。使用添加到旧日期时间类的新方法立即转换为 java.time。
java.sql.Timestamp ts = myResultSet.getTimestamp( \xe2\x80\xa6 ) ;\nInstant instant = ts.toInstant();\nRun Code Online (Sandbox Code Playgroud)\n\n继续在 java.time 类中执行业务逻辑。要将日期时间发送回数据库,请从 转换Instant为java.sql.Timestamp。
myPreparedStatement.setTimestamp( \xe2\x80\xa6 , java.sql.Timestamp.from( instant ) ) ;\nRun Code Online (Sandbox Code Playgroud)\n\n在 Java 6 和 7 中,上述概念仍然适用,但 java.time 不是内置的。使用ThreeTen-Backport改用图书馆。要获取,请参阅下面的项目符号。
在 Java 7 中,您无法使用 JDBC 4.2 功能。因此我们无法通过 JDBC 驱动程序直接从数据库访问 java.time 对象。如上所示,我们必须简单地转换为java.sql.Timestampfrom Instant。调用实用方法DateTimeUtils.toInstant(Timestamp sqlTimestamp)&DateTimeUtils.toSqlTimestamp(Instant instant)。
java.sql.Timestamp ts = myResultSet.getTimestamp( \xe2\x80\xa6 ) ;\nInstant instant = DateTimeUtils.toInstant( ts ) ;\nRun Code Online (Sandbox Code Playgroud)\n\n\xe2\x80\xa6 和 \xe2\x80\xa6
\n\njava.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( instant ) ;\nmyPreparedStatement.setTimestamp( \xe2\x80\xa6 , ts ) ;\nRun Code Online (Sandbox Code Playgroud)\n\njava.time框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧遗留日期时间类,例如java.util.Date, Calendar, & SimpleDateFormat。
Joda -Time项目现在处于维护模式,建议迁移到java.time类。
\n\n要了解更多信息,请参阅Oracle 教程。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。
\n\n从哪里获取 java.time 类?
\n\nThreeTen -Extra项目通过附加类扩展了 java.time。该项目是 java.time 未来可能添加的内容的试验场。您可能会在这里找到一些有用的类,例如Interval、、等YearWeekYearQuarter。