【问题标题】:Oracle DB Timestamp to Java Timestamp : ConfusionOracle DB 时间戳到 Java 时间戳:混淆
【发布时间】:2015-02-07 08:32:51
【问题描述】:

我花了几个小时才弄清楚我的代码中 TimeStamps 发生了什么。

Oracle DB 和 java 应用程序都在 PDT 中

从数据库中选择:

select id, time_stamp from some_Table where id = '3de392d69c69434eb907f1c0d2802bf0';
3de392d69c69434eb907f1c0d2802bf0    09-DEC-2014 12.45.41.354000000 PM

select id, time_stamp at time zone 'UTC' from some_Table where id = '3de392d69c69434eb907f1c0d2802bf0';
3de392d69c69434eb907f1c0d2802bf0    09-DEC-2014 12.45.41.354000000 PM

Oracle 数据库中的字段是TimeStamp,因此没有存储时区信息。

Timestamp dbTimeStamp = dbRecord.getLastLoginTime();
System.out.println(dbTimeStamp.toString()); // 2014-12-09 12:16:50.365
System.out.println(dbTimeStamp.getTime()); // 1418156210365 --> Tue Dec 09 2014 20:16:50 UTC?

根据documentationgetTime()

返回自 1970 年 1 月 1 日 00:00:00 GMT 以来的毫秒数 由这个 Timestamp 对象表示。

为什么在getTime() 的响应中增加了8 hours (PDT - UTC) 的额外时间?

【问题讨论】:

  • 因为您会从引用的文本中注意到:since January 1, 1970, 00:00:00 GMT。注意到那里的GMT了吗?

标签: java oracle timestamp gmt


【解决方案1】:

TimeStamp.toString() 内部使用 Date.getHours(),其 javadoc 声明:

返回此 Date 对象表示的小时。这 返回值是一个数字(0 到 23) 表示一天中包含或开始的小时 与此日期所代表的时间瞬间 对象,以当地时区解释。

所以toString 使用您的本地时区,而getDate 没有。

【讨论】:

  • 这是否意味着这里的绝对 UTC 时间是 Tue Dec 09 2014 20:16:50 UTC(数据库中字段的值)?
  • 看来是这样。不知道为什么 SQL select 语句显示分秒不同。
【解决方案2】:

这两个是一致的。 getTime() 方法为您提供绝对毫秒值,您选择以 UTC 解释该值。 toString() 方法为您提供在相关时区中解释的相同毫秒值。所以不是getTime() 增加了时间,而是toString() 减少了时间。这并没有真正记录在案,但这就是它的行为方式。

最重要的一点是不要依赖Timestamp.toString,因为它具有误导性。 Date(和Timestamp 是一个子类)中的整个时区机制很久以前就被弃用了。而是仅使用 getTime() 值并使用其他 API 对其进行格式化,例如 Java 8 Date/Time API。

更新

显然,toString() 输出实际上是正确的,对我来说,这只是 Java 日期/时间处理所有错误的厚目录中的一小部分。您可能从数据库中接收到时间戳作为格式化字符串,而不是毫秒值。然后 JDBC 根据与Timestamp 实例关联的时区将其解析为毫秒值,这样toString() 的输出与数据库返回的内容相匹配,而实际毫秒值是次要的。

【讨论】:

  • 这是否意味着这里的绝对UTC时间是Tue Dec 09 2014 20:16:50 UTC(与我掌握的信息不一致)?我也从数据库中添加了一个选择。
  • 如何确定 Oracle 的输出未适应其默认时区?
  • 我假设是因为当我使用 at time zone 'UTC' --> 执行选择时它给出了相同的结果。
  • 为确保这确实证明了这一点,请使用at time zone 'PDT' 重试。我还会尝试将时间戳转换为数字,以获取原始值。如果它确实证明了这一点,那么在 JDBC 端解释值可能会出现问题。
  • 是的,它返回与PDT 相同的结果。这里有什么问题?
【解决方案3】:

感谢上面的答案和关于 SO 的参考资料。 This answer 终于帮助我理解了我在理解时间戳方面出了什么问题。

引用链接的答案

Note: Timestamp.valueOf("2010-10-23 12:05:16"); means "create a timestamp with the given time in the default timezone".

TimeStamp 表示时间的瞬间。默认情况下,当前时区中的那个时刻。

写入到数据库的时间戳是 UTC 时刻。即正在写入当前的 UTC 时间。因此,无论应用程序部署在何处,写入数据库的值都是相同的时间戳。

但是,在读取生成的时间戳时,假定默认时区是从部署 JVM 读取的。因此,读取的值是 PST 时区的瞬间。实际的 UTC 值比 PST 时间多 8 小时。因此,差异。

TimeStamp.getTime() 从 UTC 返回毫秒数。 TimeStamp.toString() 返回当前TimeZone 中的时间表示。谢谢@marko-topolnik

举个例子,

  • 数据库中的值:2014-12-09 12:16:50.365
  • 在 TimeStamp 中读取此值时,PST 中的瞬间为 2014-12-09 12:16:50.365
  • 将此转换为 UTC,即为 2014-12-09 20:16:50

因此,解决方案是将TimeZone 偏移量添加到从数据库读取的值中,以将瞬间作为UTC TimeStamps。

这里的关键是“TimeStamp is a time instant without TimeZone information. The timestamp is assumed to be relative to the default system TimeZone.”——我花了很长时间才理解这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-10
    • 2012-04-07
    • 1970-01-01
    • 2022-11-14
    • 2012-03-10
    • 2018-10-19
    相关资源
    最近更新 更多