JDBC时间戳和日期GMT问题

8
我有一个JDBC日期列,如果我使用getDate,只会得到“日期”部分,即02 Oct 2009,但如果我使用getTimestamp,则会得到完整的“日期”,即02 Oct 2009 13:56:78:890。这正是我想要的。
然而,由getTimestamp返回的“日期”会“忽略”GMT值,假设日期为02 Oct 2009 13:56:78:890,最终得到的结果是02 Oct 2009 15:56:78:890
我的日期保存在数据库中作为+2GMT日期,但应用服务器位于GMT即比客户端晚2小时。
如何仍然获得我的日期,即02 Oct 2009 13:56:78:890
编辑
我在客户端上(即GMT +2)得到了日期+2。
5个回答

14

这就是MySQL中Timestamp与其他时间类型之间的区别。Timestamp以UTC时间戳格式保存,而其他类型则直接存储日期/时间信息,没有时区信息。

当你调用getTimestamp()方法时,如果类型为Timestamp,则MySQL JDBC驱动程序会将时间从GMT转换为默认时区。对于其他类型,它不会执行任何此类转换。

你可以更改列类型或自己进行转换。我建议采用前者。


关键字“converts”,这就是为什么我得到了+2小时的原因。 - n002213f
我有点困惑,如果转换正常工作,他不应该看到比原来时间早两个小时的日期吗? - wds
这已经足够接近并引导我找到了最终的解决方案。 - n002213f

4
请注意,java.util.Date(以及其子类java.sql.Datejava.sql.Timestamp)并不知道时区,或者说它们始终处于UTC时间。 java.util.Date及其子类只是一个容器,用来存储自1970年1月1日12:00AM UTC以来的毫秒数。
要在特定时区显示日期,请使用java.text.DateFormat对象将其转换为字符串。通过调用setTimeZone()方法设置该对象的时区。例如:
Date date = ...;  // wherever you get this from

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

// Make the date format show the date in CET (central European time)
df.setTimeZone(TimeZone.getTimeZone("CET"));

String text = df.format(date);

3

前几天我遇到了一个类似的问题,一些日期的时间组件被截断了。

我们把问题缩小到Oracle驱动程序版本的差异。

在Oracle的常见问题解答中有一个关于此问题的部分:


select sysdate from dual; ...while(rs.next())

在9201之前,这将返回: sysdate的getObject: java.sql.Timestamp <<<< sysdate的getDate: java.sql.Date sysdate的getTimestamp: java.sql.Timestamp
从9201开始,将返回以下内容:
sysdate的getObject: java.sql.Date <<<< sysdate的getDate: java.sql.Date >>没有更改 sysdate的getTimestamp: java.sql.Timestamp >>没有更改
注意:java.sql.Date没有时间部分,而java.sql.Timestamp有。
随着数据类型映射的更改,当JDBC驱动程序从8i/9iR1升级到920x JBDC驱动程序时,一些应用程序将失败和/或生成不正确的结果。为了在升级后保持兼容性并使应用程序正常工作,提供了一个兼容性标志。现在开发人员有一些选择:
1. 使用oracle.jdbc.V8Compatible标志。
默认情况下,JDBC驱动程序不会检测数据库版本。要更改处理TIMESTAMP数据类型的兼容性标志,请设置连接属性oracle.jdbc.V8Compatible为“true”,驱动程序将像8i、901x、9200一样处理TIMESTAMP。
默认情况下,标志设置为“false”。在OracleConnection构造函数中,驱动程序获取服务器版本并相应地设置兼容性标志。
java.util.Properties prop=newjava.util.Properties(); 
prop.put("oracle.jdbc.V8Compatible","true"); 
prop.put("user","scott"); 
prop.put("password","tiger");
String url="jdbc:oracle:thin:@host:port:sid"; 
Connection conn = DriverManager.getConnection(url,prop);

使用 JDBC 10.1.0.x 版本时,可以使用以下系统属性代替连接属性:java -Doracle.jdbc.V8Compatible=true......注意:此标志仅为客户端标志,用于管理时间戳和日期映射。它不会影响任何数据库特性。
2. 当处理日期和时间戳列数据类型时,请相应地使用 set/getDate 和 set/getTimestamp。
9i 服务器支持日期和时间戳列类型,DATE 映射到 java.sql.Date,TIMESTAMP 映射到 java.sql.Timestamp。
因此,对于我的情况,我有如下代码:
import java.util.Date; 
Date d = rs.getDate(1);

在使用9i时,我得到了一个java.sql.Timestamp(它是java.util.Date的子类),所以一切都很好,我有了小时和分钟。

但是在使用10g时,相同的代码现在得到了一个java.sql.Date(也是java.util.Date的子类,因此仍然可以编译),但是HH:MM被截断了!

第二个解决方案对我来说非常简单-只需将getDate替换为getTimestamp,你就应该没问题了。我想那是一个坏习惯。


0

从这篇文章中,我得出结论:JDBC确实检索了时间戳的时区(我认为MS SQL也不支持此功能,大多数谷歌搜索结果指向Oracle)。

当调用JDBC getTimeStamp方法时,它只获取'毫秒'部分,并创建一个带有服务器时区(即GMT)的日期对象。

当将此日期对象呈现给我的客户端(+2 GMT),它会添加2个小时,这是标准偏移量,导致额外的小时数。

我通过从检索到的日期中删除时间偏移量来纠正这个问题,即转换为真正的GMT日期。


0
private Date convertDate(Date date1) throws ParseException {
        SimpleDateFormat sdfFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String dateStr = sdfFormatter.format(date1);
        SimpleDateFormat sdfParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdfParser.setTimeZone(TimeZone.getTimeZone("GMT"));
        return sdfParser.parse(dateStr);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接