我有一个时间戳编码为字符串 – 例如,“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”时区偏移量.这对我来说是一个红旗,直到我看到这个post.其中一个建议的答案说,
I think the correct answer should be java.sql.Timestamp is NOT timezone specific. Timestamp is a composite of java.util.Date and a separate nanoseconds value. There is no timezone information in this class. Thus just as Date this class simply holds the number of milliseconds since January 1, 1970, 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中,时区不是一个因素吗?
>有没有更好的方法来读取整个时间戳,而不会失去Java 7中的任何精度?
最佳答案 TL;博士
org.threeten.bp.OffsetDateTime odt =
OffsetDateTime.parse(
"2012-02-12T09:08:13.123456-0400",
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.
)
;
使用java.time
您应该检索一个对象,一个日期时间对象,特别是一个java.time对象,而不是从您的数据库接收一个String.
java.time类取代了麻烦的旧日期时间类,包括java.sql.Timestamp.如果您的JDBC driver支持JDBC 4.2及更高版本,则可以直接传递和接收java.time对象.
瞬间
Instant
类代表UTC时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位).所以这相当于java.sql.Timestamp,包括对输入数据的六位数微秒的支持,因此不会因为你的问题的要求而丢失精度.
Instant instant = myResultSet.getObject( … , Instant.class ) ;
instant.toString(): 2012-02-12T13:08:13.123456Z
ZonedDateTime
如果您希望通过特定区域的挂钟时间镜头看到同一时刻,请应用ZoneId以获取ZonedDateTime对象.
ZoneId z = ZoneId.of( "America/St_Thomas" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
zdt.toString(): 2012-02-12T09:08:13.123456-04:00[America/St_Thomas]
OffsetDateTime
至于你如何理解字符串2012-02-12T09:08:13.123456-0400作为日期时间值的直接问题,解析为OffsetDateTime.
时区的名称格式为洲/区域,表示由夏令时(DST)等异常引起的区域偏移的过去,现在和将来变化的历史记录.我们已经知道你的字符串的时区,所以我们使用OffsetDateTime而不是ZonedDateTime.
OffsetDateTime odt = OffsetDateTime.parse( "2012-02-12T09:08:13.123456-0400" ) ;
好吧,上面的代码行应该有效,但是在Java 8中,在解析偏移量时会出现一个小错误,缺少小时和分钟之间的可选COLON字符.所以Java 8中的-04:00将解析而不是-0400.在Java 9中修复了错误.您的String确实符合ISO 8601标准,适用于java.time类中默认使用的日期时间格式.提示:通常最好总是用冒号格式化你的偏移量,以及小时/分钟和填充零 – 我已经看到其他协议和库只期望这样的完整格式.
在转到Java 9之前,请明确指定格式化模式,而不是依赖于隐式默认模式,作为此错误的解决方法.
OffsetDateTime odt =
OffsetDateTime.parse(
"2012-02-12T09:08:13.123456-0400",
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.
)
;
转换
如果您的JDBC驱动程序尚不符合JDBC 4.2,请检索java.sql.Timestamp对象,仅供简要使用.使用添加到旧日期时间类的新方法立即转换为java.time.
java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;
Instant instant = ts.toInstant();
继续在java.time类中执行业务逻辑.要将日期时间发送回数据库,请将Instant转换为java.sql.Timestamp.
myPreparedStatement.setTimestamp( … , java.sql.Timestamp.from( instant ) ) ;
Java 6& 7
在Java 6& 7,上述概念仍然适用,但java.time不是内置的.请改用ThreeTen-Backport库.要获得,请参阅下面的子弹.
在Java 7中,您无法使用JDBC 4.2功能.所以我们不能通过JDBC驱动程序直接从数据库访问java.time对象.如上所示,我们必须简单地从Instant转换为java.sql.Timestamp.调用实用程序方法DateTimeUtils.toInstant(Timestamp sqlTimestamp)
& DateTimeUtils.toSqlTimestamp(Instant instant)
.
java.sql.Timestamp ts = myResultSet.getTimestamp( … ) ;
Instant instant = DateTimeUtils.toInstant( ts ) ;
…和…
java.sql.Timestamp ts = DateTimeUtils.toSqlTimestamp( instant ) ;
myPreparedStatement.setTimestamp( … , ts ) ;
关于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类?
>内置.
>部分标准Java API,带有捆绑实现.
> Java 9增加了一些小功能和修复.
>大部分java.time功能都被反向移植到Java 6& 7月在ThreeTen-Backport.
> Android
> ThreeTenABP项目专门针对Android调整了ThreeTen-Backport(如上所述).
>见How to use ThreeTenABP….
ThreeTen-Extra项目使用其他类扩展了java.time.该项目是未来可能添加到java.time的试验场.您可以在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more.