一次data migration看Java.sql.Timestamp和database timestamp的转换

这个例子是这样的,我们有三种数据库表需要做data migration,两种表Deliverable和Local_Deliverable存储了常规数据,另一张表叫做Deliverable_Object。后一张表是前两张表里的java bean对象序列化以后的存储,这样做主要是为了效率考虑,不需要跨表查询得到数据对象。真正显示在用户界面上的就是这张Deliverable_Object表里的java bean对象。

Java bean对象里有个数据字段叫做GADate,这个在数据库里 (DB2)是一个timestamp对象,目前的现象是data migration完以后,我们发现显示的GADate和用户的原始输入差一天,更具体一点,Deliverable_Object和Deliverable(Local_Deliverable)对象的GAdata差一天。

让我们看一下数据的产生和录入的过程:

1.用户前台输入(dojo控件),javascript转数据,通过json object传入到后台(long)

       var ega = Util.getDateLongValue(dijit.byId("fp_GADate").attr("value"));
        if (ega != null) { 
            datawrapper["ega"] = ega;
        }

    getDateLongValue: function(date){
        return date != null ? this.toGMTTimestamp(date) : null;
},

    toGMTTimestamp: function(date){
        if (date == null) {
            return null;
        }
        
        var millsecond = date.getTime();
        var offset = date.getTimezoneOffset();
        return millsecond - offset * 60000;
    }

这里利用dojo的date控件,将时区信息直接抹去,存入了相对于GMT+0的long型数据。

2.后台new Timestamp(value), 存入数据库:

Local_Deliverable: setTimestamp(time)
Deliverable: setTimestamp()
Deliverable_Cache setDeliverableObject()

3.后台从数据库读Timestamp,转成long

  Local_Deliverable:  getTimestamp(time)
   Deliverable:        getTimestamp()
   Deliverable_Cache   getDeliverableObject()

4.前台解析long值显示。

让我们看一个具体的例子:

步骤 注解
用户输入 Tue Feb 15 2011 00:00:00 GMT+8 前台取得的时间和用户TimeZone有关
经JS转换 Tue Feb 15 2011 00:00:00 GMT 这里将用户TimeZone信息抹去得到GMT时间
后台得到 New Timestamp(Tue Feb 15 2011 00:00:00 GMT) Java Code
存储数据库 Deliverable (2011-2-15:00:00:00:00000 JVM-timezone )
Local_Deliverable(2011-2-15:00:00:00:00000 JVM-timezone)
Deliverable_Object(2011-2-15:00:00:00:00000 JVM-timezone)
Java.sql.Timestamp和
SQL timestamp(string)之间转换
CachResultsetImpl.setTimestamp
系统默认用JVM 时区转换

注意到,这里从Java数据类型java.sql.Timestamp向数据库timestamp类型字段写值时,需要使用JVM时区进行转换,其中注意用到的方法为:

CachResultsetImpl.setTimestamp(TimeStamp)
CachResultsetImpl.setTimestamp(TimeStamp,Calendar)
CachResultsetImpl.getTimestamp(TimeStamp)
CachResultsetImpl.getTimestamp(TimeStamp,Calendar)

针对我们开始的问题,最终的结论为:

1. 数据库中timestamp的存储为2011-2-15:00:00:00:00000这样的字符串,本身没有任何时区概念

2. 从数据库中读取timestamp初始化一个java.sql.Timestamp对象时,需要对数据库中的字符串进行时区的解释,即
2011-2-15:00:00:00:00000 = 2011-2-15:00:00:00:00000 GMT+8 还是2011-2-15:00:00:00:00000GMT+0
这时候,默认用JVM的timezone来转换,也可以指定Calendar(TimeZone)。

反之,将一个java.sql.Timestamp对象写进数据库时,也要对其进行转换。
这里有一个典型的例子,对同一个java.sql.Timestamp按不同的Timezone写进数据库我们可以看到如下结果:

Asian/Shang Hai GMT +8	2011-03-09 14:04:51.21
GMT	2011-03-09 06:04:51.21
EST GMT -5	2011-03-09 01:04:51.21


3.从我们的例子来看,Production WAS Server JVM应该是GMT, 测试环境的WAS Server JVM也应该是GMT。如若不然,显示在View Port里的Deliverable 时间应该少一天 (GMT+8 比GMT多8个小时,按GMT+8理解的2011-2-15:00:00:00:00000对应的GMT应该为2011-2-14 16:00:00:00000)。本地Tomcat 6也是 GMT。
(经过尝试,我们发现,对于Tomcat和WAS虽然可以设置给JDK –Duser.timezone=”GMT+8”,但是在程序中TimeZone.getDefault()均为GMT。这说明,server程序已经充分考虑到时区,在startup时将defatult Timezone始终设置为GMT,这就保证了程序在不同AppServer和不同机器上的兼容性)

4. 对TimeStamp做data migration本身不会有任何问题,因为读了一次,又写了一次,两次就将转换抵消最终写进数据库的Timestamp值还是本身的;问题是我们用了blob存了java bean,也就是读了一次的那个java.sql.Timestamp,这个时候做data migration的JVM的timezone设置就成了问题的关键。由于我们做data migration时候的机器time zone设置成GMT+8,所以会有问题发生。

步骤 注解
migration前 Tue Feb 15 2011 00:00:00  
migration后 Deliverable (2011-2-15:00:00:00:00000 GMT)
Local_Deliverable(2011-2-15:00:00:00:00000 GMT)
Deliverable_Object(2011-2-15:00:00:00:00000 JVM-timezone)
cachresultsetImpl.getTimestamp
cachresultsetImpl.setTimestamp
系统默认用JVM 时区转换
对于Deliverable转换2次所以没变;
对于Object存入的是读取的值

那么我们在自己的机器上如何设置JVM timezone呢?

方法1:

在control panel里如下设置(注意:如果选中”夏令时”那么对于4-9月的时间会有一个偏移量,此时相当于GMT+1。所以最好不选中“夏令时”)

一次data migration看Java.sql.Timestamp和database timestamp的转换_第1张图片

方法2:

JVM 启动选项
-Duser.timezone=GMT

你可能感兴趣的:(java)