时区问题

Posted yizw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了时区问题相关的知识,希望对你有一定的参考价值。

CST时区名为CST的时区是一个很混乱的时区,有四种含义:

-美国中部时间CentralStandardTime(USA)UTC-06:00

-澳大利亚中部时间CentralStandardTime(Australia)UTC+09:30

-中国标准时ChinaStandardTimeUTC+08:00

-古巴标准时CubaStandar

 

可能存在mysql读取TimeStamp字段时区和java时区相差13小时的现象

其原因为:

public void configureTimezone() { 
String configuredTimeZoneOnServer = getServerVariable("time_zone"); 
if ("SYSTEM".equalsIgnoreCase(configuredTimeZoneOnServer)) { 
configuredTimeZoneOnServer = getServerVariable("system_time_zone"); 

String canonicalTimezone = getPropertySet().getStringReadableProperty(PropertyDefinitions.PNAME_serverTimezone).getValue(); 
if (configuredTimeZoneOnServer != null) { 
// user can override this with driver properties, so don‘t detect if that‘s the case 
if (canonicalTimezone == null || StringUtils.isEmptyOrWhitespaceOnly(canonicalTimezone)) { 
try { 
canonicalTimezone = TimeUtil.getCanonicalTimezone(configuredTimeZoneOnServer, getExceptionInterceptor()); 
} catch (IllegalArgumentException iae) { 
throw ExceptionFactory.createException(WrongArgumentException.class, iae.getMessage(), getExceptionInterceptor()); 



if (canonicalTimezone != null &;&; canonicalTimezone.length() > 0) { 
this.serverTimezoneTZ = TimeZone.getTimeZone(canonicalTimezone); 
// The Calendar class has the behavior of mapping unknown timezones to ‘GMT‘ instead of throwing an exception, so we must check for this... 
if (!canonicalTimezone.equalsIgnoreCase("GMT") 
&;&; this.serverTimezoneTZ.getID().equals("GMT")) { 
throw ... 


this.defaultTimeZone = this.serverTimezoneTZ; 

mysql> show variables like ‘%time_zone%‘; 
+------------------+--------+ 
| Variable_name| Value| 
+------------------+--------+ 
| system_time_zone | CST| 
| time_zone | SYSTEM | 
+------------------+--------+ 

当 JDBC 与 MySQL 开始建立连接时,会调用 `com.mysql.cj.jdbc.ConnectionImpl.initializePropsFromServer()` 获取服务器参数

代码中可见当 MySQL 的 `time_zone` 值为 `SYSTEM` 时,会取 `system_time_zone` 值作为协调时区。

若 `String configuredTimeZoneOnServer` 得到的是 `CST` 那么 Java 会误以为这是 `CST -0500`,

当服务器的时区为`CST +0800`时误认为java误认为时区为 `CST -0500`,导致java获取结果相差13小时。

`com.mysql.cj.jdbc.PreparedStatement.setTimestamp()`。 
```java 
public void setTimestamp(int parameterIndex, Timestamp x) throws java.sql.SQLException { 
synchronized (checkClosed().getConnectionMutex()) { 
setTimestampInternal(parameterIndex, x, this.session.getDefaultTimeZone()); 

这里的this.session.getDefaultTimeZone()是java默认得到的`CST -0500`,

private void setTimestampInternal(int parameterIndex, Timestamp x, TimeZone tz) throws SQLException {
    if (x == null) {
        setNull(parameterIndex, MysqlType.TIMESTAMP);
    } else {
        if (!this.sendFractionalSeconds.getValue()) {
            x = TimeUtil.truncateFractionalSeconds(x);
        }

        this.parameterTypes[parameterIndex - 1 + getParameterIndexOffset()] = MysqlType.TIMESTAMP;

        if (this.tsdf == null) {
            this.tsdf = new SimpleDateFormat("‘‘yyyy-MM-dd HH:mm:ss", Locale.US);
        }

        this.tsdf.setTimeZone(tz);

        StringBuffer buf = new StringBuffer();
        buf.append(this.tsdf.format(x));
        if (this.session.serverSupportsFracSecs()) {
            buf.append(‘.‘);
            buf.append(TimeUtil.formatNanos(x.getNanos(), true));
        }
        buf.append(‘‘‘);

        setInternal(parameterIndex, buf.toString());
    }
}

此处 Timestamp 被转换为会话时区的时间字符串了

在此时jdbc误认为会话时区在 CST-5 

JBDC 把 Timestamp+0 转为 CST-5 的 String-5 

MySQL 认为会话时区在 CST+8,将 String-5 转为 Timestamp-13 

最终结果相差 13 个小时!如果处在冬令时还会相差 14 个小时! 

解决办法也很简单,明确指定 MySQL 数据库的时区,不使用引发误解的 `CST`: 

mysql> set global time_zone = ‘+08:00‘; 
Query OK, 0 rows affected (0.00 sec) 
mysql> set time_zone = ‘+08:00‘; 
Query OK, 0 rows affected (0.00 sec) 

或者修改 `my.cnf` 文件,在 `[mysqld]` 节下增加 `default-time-zone = ‘+08:00‘`。 
修改时区操作影响深远,需要重启 MySQL 服务器,建议在维护时间进行。










































以上是关于时区问题的主要内容,如果未能解决你的问题,请参考以下文章

使用日期时间和时区时出现代码问题

程序代码中的时区问题

使用 pytz 获取时区的国家代码?

如何在android中获取移动时区的缩写/国家代码?

如何在 PHP 中将默认时区设置为用户的本地时区? [复制]

时刻时区和 DST