发出以下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
)似乎不会影响结果。
该命令在巴西执行。RAW 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中的服务器获得日期和时间,然后将TimeZone应用于客户端以获取本地日期和时间。
客户端正在运行Windows,服务器正在运行Linux。
要使事情变得更奇怪,发出TO_CHAR
也会产生错误的结果:
select TO_CHAR(SYSDATE, 'DD/MM/YYYY HH24:MI:SS') from dual
- 直接在Oracle上:
06/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 ~]#
有什么想法吗?我应该从数据库中收集什么信息或配置来诊断和解决此问题?
简单地放置,将Oracle DATE
选择到Java Date
本质上是有问题的。那是因为它们根本不同。Oracle DATE
是一年,月,每日,小时,分钟,秒,没有任何时区信息的组合,因此可以是任何时区,无论有没有夏令时 - 甲骨文都不知道,因为不包括这些信息在DATE
。
另一方面,Java日期基本上是自1970年1/1/1/1 00:00:00 UTC。
以来的毫秒数当Oracle DATE
进入Java Date
时,JDBC驱动程序只能猜测要应用哪个时区。结果是不可预测的,尤其是当数据库中的数据比用户使用另一个时区时。
java日期直到您格式化它们,。
Java日期在内部存储为长期 - 自1970年1月1日,午夜UTC以来所经过的毫秒数量。
查看Java日期的时间,以及报告的时区(无论您是格式化),您可能会发现它等同于数据库中的时间。
您很可能在格式化日期时遇到问题 - 使用您期望的时区以外的时区。我猜想您在格式化时没有指定时区(使用默认一个),或者仅使用日期的toString()方法。
。如果使用日历(calendar.settime(date)),则可以将"时期时间"与特定时区组合在一起,或者如果对您有用,请使用默认时区。您还可以查询其时区的日历,如果默认一个不正确,那么您确实需要调查计算机本身是否设置为错误的时区,或者该计算机上使用Java TimeZone数据库不对劲。。
" java日期在格式化之前没有时区。"错误,将其定义为UTC。AMMOQ的原始答案是正确的。数据库日期列没有时区,尽管自1970年1月1日午夜以来可以将其存储为毫秒,但并未将其定义为UTC,它是您喜欢的任何时区(即当地时间,无论是时机,无论是时机)。
特别是,JDBC在UTC中转换为Java日期时使用JVM的默认时区。因此,如果数据库存储1/1/2014 02:00,而您的JVM为"美国/芝加哥",那么您将获得与1/1/2014 02:00 CST相当的Java日期。如果您的JVM为" America/new_york",则将获得相当于1/1/2014 02:00 EST的Java日期。这是一个非常不同的时刻,一个小时(3600000毫秒)不同,恰好相当于1/1/2014 01:00 CST,而不是02:00 CST。
使用JVM的时区最大的问题是每年在"重叠小时"中发生的,如果那个时区尊重日光节省时间。数据库将在秋季周日(在美国时区)上说01:30,但是是哪个01:30,是白天时间的第一个或切换回标准时间之后的第二个?JDBC/JVM必须任意在这两个时刻之间进行选择。我记得,我认为大多数JVM将始终使用标准时间(第二个01:30)。尽管在春季跳到日光节省时间,但从技术上讲,02:30的时间不存在,但我认为大多数JVM都会在春季到日光时间之后的03:30。
尝试VM参数
-Duser.timezone=GMT-2
或
-Duser.timezone=America/Sao_Paulo
Java具有自己的时区设置,可能会影响日期对象的传输。