发出以下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
06/11/2012, 10:38:49
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 ~]#
有什么想法?我应该从数据库中收集哪些信息或配置来诊断和解决这个问题?
答案 0 :(得分:2)
简而言之,在Java DATE
中选择oracle Date
本身就存在问题。那是因为它们根本不同。 Oracle DATE
是年,月,日,小时,分钟,秒的组合,没有任何时区信息,所以它可以是任何时区,有或没有夏令时 - Oracle不知道,因为该信息未包含在DATE
。
另一方面,Java Date基本上是自UTC时间1/1/1970 00:00:00以来的毫秒数。
当Oracle DATE
进入Java Date
时,JDBC驱动程序只能猜测要应用的时区。结果相当不可预测,尤其是当数据库中的数据使用的时区与用户不同时。
答案 1 :(得分:0)
Java日期在格式化之前没有时区。
java日期在内部存储为long - 自1970年1月1日UTC午夜起经过的毫秒数。
查看java Date的时间,以及它报告的时区(但是你将其格式化),你可能会发现它等同于数据库中的时间。
您很可能在格式化日期时遇到问题 - 使用您所期望的时区以外的时区。我猜你在格式化时没有指定时区(使用默认区域),或者只是使用Date的toString()方法。
如果您使用日历(calendar.setTime(日期)),则可以将“纪元时间”与特定时区结合使用,或者如果它适用于您,请使用默认时区。您还可以查询日历的时区,如果默认的时区不正确,那么您确实需要调查计算机本身是否设置为错误的时区,或者该计算机上的java时区数据库是否有问题。
答案 2 :(得分:0)
" 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 EST的日期。这是一个非常不同的时刻,一小时(3600000毫秒)不同,恰好相当于1/1/2014 01:00 CST,而不是02:00 CST。
然而,使用JVM的时区的最大问题是每年在“重叠时间”#34;如果那个时区是为了纪念夏令时。数据库将在周日回归时间01:30(美国时区)说,但01:30是白天时间的第一个,也就是切换回标准时间后的第二个? JDBC / JVM必须在这两个时刻之间任意选择。我记得,我认为大多数JVM总是使用标准时间(第二个01:30)。虽然在春季跳到夏令时期间,技术上并不存在02:30的时间,但我认为大多数JVM将在春天到夏令时之后的03:30假设。
答案 3 :(得分:0)
尝试使用vm参数
-Duser.timezone=GMT-2
或
-Duser.timezone=America/Sao_Paulo
Java有自己的时区设置,可能会影响Date对象的传输。