1. 云栖社区>
  2. >
  3. 正文

数据库获取时间的时区问题以及getTimestamp和getDate的坑

作者:用户 来源:互联网 时间:2018-09-11 18:59:25

数据库工程

数据库获取时间的时区问题以及getTimestamp和getDate的坑 - 摘要: 本文讲的是数据库获取时间的时区问题以及getTimestamp和getDate的坑, 今天程序中需要用到对数据库时间的获取,数据库在美国,用的是UTC时区,客户端在中国,用的时GMT+8时区,但是发现用getTime和getTimestamp时候获取日期时得到的时间不一致。于是就看了下JDBC的源码了解了这个

今天程序中需要用到对数据库时间的获取,数据库在美国,用的是UTC时区,客户端在中国,用的时GMT+8时区,但是发现用getTime和getTimestamp时候获取日期时得到的时间不一致。于是就看了下JDBC的源码了解了这个Bug或者说Feature的原理。
就是getTimestamp这个函数比如在数据库连接的url里面加入useTimezone = true这个参数才可以转换时区,否则传入的时区参数是无效的。然而getDate这个函数,是数据库连接的url里设置的时区对它都是无效的,需要再单独传入时区参数才可以

#
详细可以参考官方文档,比如Mysql的官方文档
在数据库url中会有一些参数,其中关于时区的有如下三个:

serverTimezone
useTimeZone
useLegacyDatetimeCode

serverTimezone是用来服务器时区的【如果不设置就选择服务器的默认时区】
useTimeZone是用来设置时区转换的,当其为true时,就会开启服务器和客户端之间的时区转换,而其只有当useLegacyDatetimeCode = true时才回生效

useLegacyDatetimeCode这个参数是一个开关参数,默认为true,但是当其设置为false时,"useTimezone," "useJDBCCompliantTimezoneShift," "useGmtMillisForDatetimes," and "useFastDateParsing."这几个参数都会变的无效化。

getTimestamp函数中,其中在最开始会传入一个时区的参数,

public Timestamp getTimestamp(int columnIndex) throws SQLException {
      return this.getTimestampInternal(columnIndex, (Calendar)null, this.getDefaultTimeZone(), false);
  }

private TimeZone getDefaultTimeZone() {
        return !this.useLegacyDatetimeCode && this.connection != null ? this.serverTimeZoneTz : this.connection.getDefaultTimeZone();
    }

static final Timestamp fastTimestampCreate(TimeZone tz, int year, int month, int day, int hour, int minute, int seconds, int secondsPart) {
    Calendar cal = tz == null ? new GregorianCalendar() : new GregorianCalendar(tz);
    cal.clear();
    cal.set(year, month - 1, day, hour, minute, seconds);
    long tsAsMillis = 0L;

    try {
        tsAsMillis = cal.getTimeInMillis();
    } catch (IllegalAccessError var12) {
        tsAsMillis = cal.getTime().getTime();
    }

    Timestamp ts = new Timestamp(tsAsMillis);
    ts.setNanos(secondsPart);
    return ts;
}

Util.getDefaultTimeZone()

public Date getDate(int columnIndex) throws SQLException {
        return this.getDate(columnIndex, (Calendar)null);
    }

    protected Date fastDateCreate(Calendar cal, int year, int month, int day) throws SQLException {
        synchronized(this.checkClosed().getConnectionMutex()) {
            if (this.useLegacyDatetimeCode) {
                return TimeUtil.fastDateCreate(year, month, day, cal);
            } else {
                if (cal == null) {
                    this.createCalendarIfNeeded();
                    cal = this.fastDateCal;
                }

                boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();
                return TimeUtil.fastDateCreate(useGmtMillis, useGmtMillis ? this.getGmtCalendar() : cal, cal, year, month, day);
            }
        }
    }

getDate的时候, 不管数据库的连接的url的时区的
所以不论数据库连接的url是何种时区,调用getDate的时候都是默认的时区
所以要加Calendar

而getTimestamp的时候,如果数据库连接里没有加useTimezone=true,则不论其怎么加Calendar参数结果都是不变的,都是按照默认值。

rs.getTimestamp(2, Calendar.getInstance(TimeZone.getTimeZone("GMT+8")));
public Timestamp getTimestamp(int columnIndex) throws SQLException {
    return this.getTimestampInternal(columnIndex, (Calendar)null, this.getDefaultTimeZone(), false);
}


public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
    return this.getTimestampInternal(columnIndex, cal, cal.getTimeZone(), true);
}

以上是云栖社区小编为您精心准备的的内容,在云栖社区的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索数据库 工程 ,以便于您获取更多的相关知识。

弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率

40+云计算产品,6个月免费体验

现在注册,免费体验40+云产品,及域名优惠!

云服务器9.9元/月,大学必备