0

发出以下 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:49
  • 在 Java 上:06/11/2012 09:38:49

甲骨文服务器:

[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 ~]#

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

4

4 回答 4

2

简而言之,DATE在 JavaDate中选择一个 oracle 本质上是有问题的。那是因为它们本质上是不同的。OracleDATE是年、月、日、小时、分钟、秒的组合,没有任何时区信息,因此它可以是任何时区,有或没有夏令时 - Oracle 不知道,因为该信息不包含在DATE. _

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

当 OracleDATE进入 JavaDate时,JDBC 驱动程序只能猜测应用哪个时区。结果是相当不可预测的,尤其是当数据库中的数据使用另一个时区而不是用户时。

于 2012-11-06T13:08:34.213 回答
0

试试vm参数

-Duser.timezone=GMT-2

或者

-Duser.timezone=America/Sao_Paulo

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

于 2015-03-10T09:43:40.450 回答
0

Java Dates don't have timezones until you format them.

A java date is internally stored as a long - the number of milliseconds elapsed since Jan 1, 1970, midnight UTC.

Look at the java Date's time, along with it's reported time zone (however you format it), and you'll probably see that it is equivalent to the time in the database.

You most likely have a problem with formatting the date - using a timezone other than the one that you expect. I'm guessing that you are not specifying the time zone (using the default one) when formatting, or simply using Date's toString() method.

If you use the Calendar (calendar.setTime(date)), you can combine the 'epoch time' with a specific time zone, or if it works for you, use the default time zone. You can also query a Calendar for it's timezone, and if the default one is incorrect, then you will indeed need to investigate whether either the computer itself is set to the wrong timezone, or whether something is amiss with the java timezone database on that computer.

于 2012-11-05T19:45:51.877 回答
0

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

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

但是,如果该时区遵循夏令时,则使用 JVM 时区的最大问题每年都会在“重叠时间”期间发生。数据库将在回退的星期日(美国时区)显示 01:30,但它是哪个 01:30,是夏令时的第一个还是切换回标准时间后的第二个?JDBC/JVM 必须在这两个时刻之间任意选择。我记得,我认为大多数 JVM 将始终使用标准时间(第二个 01:30)。虽然在 Spring 跳转到夏令时期间,02:30 的时间在技术上是不存在的,但我认为大多数 JVM 会在 spring-forward 到夏令时之后假定 03:30。

于 2014-07-13T16:43:58.917 回答