繁体   English   中英

在Java结果集中跨客户端标准化Oracle日期

[英]Standardise Oracle date across clients in Java Resultset

在我的Java代码中,我使用ResultSet和getTimestamp()方法选择/获取Oracle DATE列,并将其转换为毫秒级。 问题是在我运行它的计算机上,毫秒数的时间有所不同。 例如,如果实际时间戳是1511213580 ms,则在machine1上是1511262180 ms,在machine2上是1511233380 ms。

我已经阅读了这篇文章java.sql.Timestamp时区是否特定? 并且了解Timestamp使用机器的等效时区来存储此数据。

我的问题是,如何标准化时间戳以在客户端之间显示/读取相同的时间戳? 我无权访问保留时间戳的代码。 在运行此fetch-from-db程序的计算机上,我正在使用其他Shell脚本将毫秒时间与计算机的当前时间进行比较。

这是我的代码段:

Timestamp timestamp = rset.getTimestamp(i);
if (timestamp != null)
    timeInSeconds = timestamp.getTime()/1000;

tl; dr

myResultSet.getObject( … , Instant.class ) 
           .isBefore( Instant.now() )

细节

正如已经在堆栈溢出问题上多次讨论的那样,您应该:

  • 将您的日期时间值存储在UTC中。
  • 永远不要依赖JVM的当前默认时区。
  • 避免使用可怕的旧式日期时间类,例如DateCalendar和java.sql。*类。
  • 仅使用java.time而不使用它们取代的旧式日期时间类。
  • 使用与JDBC 4.2或更高版本兼容的驱动程序来检索java.time对象。
  • 避免使用时间作为从计数开始的时间跟踪,而应使用对象。

我不是Oracle用户,但看来DATE是旧类型,缺少任何时区概念。 我建议改为使用标准的TIMESTAMP WITH TIME ZONE

Instant是时间轴上的时刻,始终采用UTC,分辨率为纳秒。

Instant instant = myResultSet.getObject( … , Instant.class ) ;
Boolean isPast = instant.isBefore( Instant.now() ) ;

虽然我建议不要将跟踪时间记为从纪元开始的计数,但是如果您坚持认为,可以提取自1970-01-01T00:00:00Z纪元以来的毫秒数。 当您截断可能存在的任何微秒或纳秒时,请当心数据丢失。

long millis = instant.toEpochMilli() ;

关于java.sql.Timestamp ,这些对象始终位于UTC中。 这种令人迷惑的遗留类,被设计为hack,现在已经遗留了,应该避免。 替换为java.time.Instant

问题是在我运行它的计算机上,毫秒数的时间有所不同。 例如,如果实际时间戳是1511213580 ms,则在machine1上是1511262180 ms,在machine2上是1511233380 ms。

这没有任何意义,因为java.sql.Timestamp将包含跨计算机的UTC中的纪元相同的计数。 我怀疑这些备用机器的时钟设置为错误的时间,可能是有意将其设置为时区调整的错误尝试。

在这种情况下,您的手会一团糟。 唯一的解决方法是测试每台计算机,计算真实UTC时间与该特定计算机的错误时钟之间的差异。 然后从该计算机获取数据时增加或减去该数量。 显然冒险,因为您永远不知道何时该困惑的sysadmin会尝试另一次调整。

真正的解决方案是:将所有服务器保持在UTC并设置为真正准确的时钟时间,使用java.time类处理日期时间值,并将时刻存储在TIMESTAMP WITH TIME ZONE类型的列中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM