java.time.Instant -> java.sql.Timestamp 在 Windows 和 *nix 上的行为不同(与时区相关)

标签 java hibernate timestamp jodatime java-time

在代码中的某个地方,我有 Timestamp ts = Timestamp.from(instant);,其中 instant 是,顾名思义,java.time.Instant。 (为什么?为了允许 Hibernate 4.x 通过 UserType、JDK8 时间类型持续存在,在 Hibernate 5 之前尚不支持)。

事实上,让我把代码放出来,这样就清楚了。日志语句就是针对这个问题的。

public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {
    if (value != null){
        Timestamp ts = Timestamp.from((Instant) value);
        log.info("nullSafeSet from " + value + " (as long: " + ((Instant) value).toEpochMilli() + ") to " + ts + " (as long: " + ts.getTime() + ")");
        StandardBasicTypes.TIMESTAMP.nullSafeSet(st, ts, index, session);
    } else {
        st.setNull(index, Types.TIMESTAMP);
    }
}

问题就在这里。在 Windows(7、64 位,尽管这并不重要)上,时间戳的字符串表示形式对应于 UTC 值。

From: 2015-05-12T19:00:08.191Z (as long: 1431457208191) 
  to: 2015-05-12 19:00:08.191  (as long: 1431457208191)

在 *nix(Linux、OS X)上,字符串表示对应于本地(EST,或者准确地说是 EDT)时间:

From: 2015-05-12T19:16:54.488Z (as long: 1431458214488) 
  to: 2015-05-12 15:16:54.488  (as long: 1431458214488)

绝对时间与可见的相同。问题是 JDBC 驱动程序发送到 Oracle 数据库的 TIMESTAMP 字段是字符串表示形式或等效(我查看了网络流量,该参数是二进制格式,因此不容易弄清楚到底发送了什么)。 相同的代码插入相当于从 Windows 执行时的 UTC 时间和从 Unix 执行时的本地时间。

我已经检查了 user.timezone 系统属性,在这两种情况下都是America/New York。机器本身也是如此。相同的 JDK8 版本、相同的应用服务器、相同的代码。

我对这种行为以及如何解决它感到困惑。

最佳答案

好吧,最终我查出了原因,结果和我想象的不一样。感谢@JBNizet 的提示,让我走上了正确的道路。

正如评论中提到的,在执行特定方法时,一个系统 TimeZone.getDefault() 在 Windows 上返回“UTC”,而在 Windows 上返回“America/New York” 在 Unix 机器上。尽管两个系统都以 user.timezone="America/New York" 启动,但已通过日志记录确认。

真正的原因是在Windows机器上,我还在同一台服务器上部署了不同的Web应用程序。该应用程序是用 Grails 编写的,正在调用 TimeZone.setDefault("UTC"),因此在 JVM 级别覆盖了通过我的应用程序中的属性设置的时区。我通过 TimeZone.setDefault() 中的断点确认了这一点。

关于java.time.Instant -> java.sql.Timestamp 在 Windows 和 *nix 上的行为不同(与时区相关),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30199962/

相关文章:

java - 不支持从 varbinary 到 BLOB 的转换

java - Java 中的字节数组到日期

r - 如何通过使用 R 中时间序列中的第一个和最后一个时间戳来定义遭遇时间段

java - 是否可以去除 JButton 图标上的阴影?

java - 如何声明 int 变量对我的所有方法都可见?

Linux 从 hibernate/挂起唤醒的命令

python - 如何将整数时间戳转换回 UTC 日期时间?

java - 如何在javafx中使用ArcGis

java - 无法在 Java 8 中使用 DateTimeFormatter 和 ZonedDateTime 从 TemporalAccessor 获取 ZonedDateTime

java - Hibernate 复杂的唯一约束