Oracle的日期和时间,不通过JDBC进行夏令时

发出以下SQL会通过PL / SQL,ODBC和JDBC生成不同的结果:

select sysdate from dual 

在PL / SQL或ODBC上运行时,日期和时间是正确的。 在JDBC上,它少了一个小时。 它似乎没有考虑夏令时。

例如,在PL / SQL上,结果是2012-11-05 16:53:53.0 ,在JDBC上,它是2012-11-05 15:53:53.0

它只发生在一些数据库上。 更改数据库时区( select dbtimezone from dual )似乎不会影响结果。

该命令正在巴西执行。 原始GMT偏移为-03:00,因为夏令时,当前偏移为-02:00。

客户端JVM的时区数据库是最新的。

要诊断数据库中的“错误”结果,只需打印结果:

 ((OracleResultSet) statement.executeQuery("select sysdate from dual")).getTIMESTAMP(1).toString(); 

Oracle的TIMESTAMP toString方法不依赖于时区信息。 JVM的时区可能仅在创建TIMESTAMP之前影响结果,即在从网络读取并将其转换为Java中的表示时。

测试更改客户端和数据库服务器时间配置:

  • SYSDATE始终返回在数据库服务器中解析的日期/时间,客户端JVM的user.timezone选项和客户端的机器时间配置无关紧要。
  • 另一方面,使用两个时区信息解析获取SYSTIMESTAMP :看起来它从UTC服务器获取日期和时间,然后在客户端中应用时区以获取本地日期和时间。

客户端运行Windows,服务器运行Linux。

为了让事情变得更奇怪,发出一个TO_CHAR也会产生错误的结果:

 select TO_CHAR(SYSDATE, 'DD/MM/YYYY HH24:MI:SS') from dual 
  • 直接在Oracle: 06/11/2012, 10:38:4906/11/2012, 10:38:4906/11/2012, 10:38:49
  • 关于Java: 06/11/2012 09:38:49

Oracle服务器:

 [root@oracle1 ~]# cat /etc/sysconfig/clock ZONE="America/Sao_Paulo" UTC=false ARC=false [root@oracle1 ~]# echo $TZ [root@oracle1 ~]# date Tue Nov 13 14:58:38 BRST 2012 [root@oracle1 ~]# [root@oracle2 ~]# cat /etc/sysconfig/clock ZONE="America/Sao_Paulo" UTC=false ARC=false [root@oracle2 ~]# echo $TZ [root@oracle2 ~]# date Tue Nov 13 14:59:58 BRST 2012 [root@oracle2 ~]# 

有什么想法吗? 我应该从数据库中收集哪些信息或配置来诊断和解决此问题?

简而言之,在Java Date选择oracle DATE本身就存在问题。 那是因为它们根本不同。 Oracle DATE是年,月,日,小时,分钟,秒的组合,没有任何时区信息,所以它可以是任何时区,有或没有夏令时 – Oracle不知道,因为该信息不包括在内在DATE

另一方面,Java Date基本上是自UTC的1/1/1970 00:00:00以来的毫秒数。

当Oracle DATE进入Java Date ,JDBC驱动程序只能猜测要应用的时区。 结果相当不可预测,尤其是当数据库中的数据使用与用户不同的时区时。

Java日期在格式化之前没有时区。

java日期在内部存储为long – 自1970年1月1日UTC午夜起经过的毫秒数。

查看java Date的时间,以及它报告的时区(但是你将其格式化),你可能会发现它等同于数据库中的时间。

您很可能在格式化日期方面遇到问题 – 使用的时区不是您期望的时区。 我猜你在格式化时没有指定时区(使用默认区域),或者只是使用Date的toString()方法。

如果您使用日历(calendar.setTime(日期)),您可以将“纪元时间”与特定时区结合使用,或者如果它适用于您,请使用默认时区。 您还可以查询日历的时区,如果默认值不正确,那么您确实需要调查计算机本身是否设置为错误的时区,或者该计算机上的java时区数据库是否有问题。 。

“在你格式化之前,Java日期没有时区。” 错了,它被定义为UTC。 ammoQ的原始答案是正确的。 数据库日期列没有时区,虽然它可能存储为自1970年1月1日午夜以来的毫秒数,但它未定义为UTC,它是您喜欢的任何时区(即,无论时区如何都是相同的本地时间)。

特别是,在转换为UTC的Java Date时,JDBC使用JVM的默认时区。 因此,如果数据库存储2014年1月1日02:00,并且您的JVM是“America / Chicago”,那么您将获得相当于2014年1月1日02:00 CST的Java日期。 如果您的JVM是“America / New_York”,那么您将获得相当于美国东部时间2014年1月1日02:00的Java日期。 这是一个非常不同的时刻,一小时(3600000毫秒)不同,恰好相当于1/1/2014 01:00 CST,而不是02:00 CST。

但是,如果该时区符合夏令时,那么使用JVM时区的最大问题是每年在“重叠时间”内发生。 数据库将在周日回归时间01:30(美国时区),但是01:30是白天时间的第一个,也就是切换回标准时间后的第二个? JDBC / JVM必须在这两个时刻之间任意选择。 我记得,我认为大多数JVM总是使用标准时间(第二个01:30)。 虽然在春季跳到夏令时期间,技术上并不存在02:30的时间,但我认为大多数JVM将在spring到夏令时之后的03:30假设。

尝试vm参数

 -Duser.timezone=GMT-2 

要么

 -Duser.timezone=America/Sao_Paulo 

Java有自己的时区设置,可能会影响Date对象的传输。