java – 解析时间戳时时区是否重要?

我有一个时间戳编码为字符串 – 例如,“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 SE 8,Java SE 9及更高版本

>内置.
>部分标准Java API,带有捆绑实现.
> Java 9增加了一些小功能和修复.

> Java SE 6Java SE 7

>大部分java.time功能都被反向移植到Java 6& 7月在ThreeTen-Backport.

> Android

> ThreeTenABP项目专门针对Android调整了ThreeTen-Backport(如上所述).
>见How to use ThreeTenABP….

ThreeTen-Extra项目使用其他类扩展了java.time.该项目是未来可能添加到java.time的试验场.您可以在这里找到一些有用的类,例如Interval,YearWeek,YearQuartermore.

点赞