oracle、mysql时区设置对timestamp的不同影响

因最近国际去Oracle上MySQL,这就不可避免的涉及到时区和timestamp问题。做一下实验,总结一下。

Oracle

首先看下oracle concepts对timestamp的定义:

The TIMESTAMP data type is an extension of the DATE data type. It stores fractional seconds in addition to the information stored in the DATE data type. TheTIMESTAMP data type is useful for storing precise time values, such as in applications that must track event order.

TIMESTAMP WITH TIME ZONE is a variant of TIMESTAMP that includes a time zone region name or a time zone offset in its value. The time zone offset is the difference (in hours and minutes) between local time and UTC (Coordinated Universal Time—formerly Greenwich Mean Time). This data type is useful for preserving local time zone information.

TIMESTAMP WITH LOCAL TIME ZONE is another variant of TIMESTAMP that is sensitive to time zone information. It differs from TIMESTAMP WITH TIME ZONE in that data stored in the database is normalized to the database time zone, and the time zone information is not stored as part of the column data. When a user retrieves the data, Oracle returns it in the user‘s local session time zone. This data type is useful for date information that is always to be displayed in the time zone of the client system in a two-tier application.

  • TIMESTAMP是对date的更高精度的一种存储,但它不存储时区信息,即不受DBTIMEZONE影响
  • TIMESTAMP WITH TIME ZONE存储客户端的时区信息,所以也不受DBTIMEZONE影响
  • TIMESTAMP WITH LOCAL TIME ZONE类型数据不会存储客户端的时区信息,它根据数据库时区对客户端发来的时间进行转换,基于统一的数据库时区存储时间信息,如果用户没有指定时区信息同TIMESTAMP WITH TIME ZONE一样默认采用会话时区。把客户端输入的时间转换为基于database timezone的时间后存入数据库(这也就是database tmiezone设置的意义所在,作为TIMESTAMP WITH LOCAL TIME ZONE类型的计算标尺)。当用户查看该类型数据时,服务器根据会话所属时区对存储的时间数据进行转换,不同时区的会话将返回不同的时间数据。所以Oracle建议把database timezone设置为标准时间UTC,这样可以节省每次转换所需要的开销,提高性能。

v$nls_parameters表不仅存了数据库的字符集信息,还有关于timestamp和timestamp with local time zone的显示格式:

SQL> select * from v$nls_parameters where parameter in (‘NLS_TIMESTAMP_FORMAT‘,‘NLS_TIMESTAMP_TZ_FORMAT‘);

PARAMETER                      VALUE

------------------------------ ------------------------------

NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM

NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR

看完定义,我们直接用实验更直观地看出它们的不同。

$ date -R

Sun, 24 Apr 2016 13:50:32 +0800

SQL>select dbtimezone,sessiontimezone from dual;

DBTIMEZONE           SESSIONTIMEZONE

-------------------- --------------------

+08:00               +08:00

我们进行插入数据的实验

SQL>create table timezone_test(t0 timestamp,t1 timestamp with time zone,t2 timestamp with local time zone);

Table created.

SQL>insert into timezone_test select current_timestamp,current_timestamp,current_timestamp from dual;

1 row created.

SQL>select * from timezone_test;

T0                             T1                                   T2

------------------------------ ------------------------------------ ------------------------------

24-APR-16 02.35.03.613433 PM   24-APR-16 02.35.03.613433 PM +08:00  24-APR-16 02.35.03.613433 PM

SQL>alter session set time_zone=‘-2:00‘;

Session altered.

SQL>select * from timezone_test;

T0                             T1                                   T2

------------------------------ ------------------------------------ ------------------------------

24-APR-16 02.35.03.613433 PM   24-APR-16 02.35.03.613433 PM +08:00  24-APR-16 04.35.03.613433 AM

留意到T0和T1都是不变的。T2,即timestamp with local time zone所输出的值发生了变化。我们这时从-2:00的另一个db用sqlplus连接,效果也是一样的:

SQL>select dbtimezone,sessiontimezone from dual;

DBTIMEZONE           SESSIONTIMEZONE

-------------------- --------------------

+08:00               -02:00

SQL>insert into timezone_test select current_timestamp,current_timestamp,current_timestamp from dual;

1 row created.

SQL>select * from timezone_test;

T0                             T1                                   T2

------------------------------ ------------------------------------ ------------------------------

24-APR-16 02.35.03.613433 PM   24-APR-16 02.35.03.613433 PM +08:00  24-APR-16 04.35.03.613433 AM

24-APR-16 05.03.35.050304 AM   24-APR-16 05.03.35.050304 AM -02:00  24-APR-16 05.03.35.050304 AM

T2为(-2)-(+8)=-10时差

SQL>alter session set time_zone = dbtimezone;

Session altered.

SQL>select * from timezone_test;

T0                             T1                                   T2

------------------------------ ------------------------------------ ------------------------------

24-APR-16 02.35.03.613433 PM   24-APR-16 02.35.03.613433 PM +08:00  24-APR-16 02.35.03.613433 PM

24-APR-16 05.03.35.050304 AM   24-APR-16 05.03.35.050304 AM -02:00  24-APR-16 03.03.35.050304 PM

当dbtimezone与sessiontimezone不同时,插入数据。T0和T1即保留了插入时的时间字符串信息,不会改变,而T2则回到了我们真正插入的时间,即下午3点03分。

SQL>select dbtimezone,sessiontimezone from dual;

DBTIMEZONE           SESSIONTIMEZONE

-------------------- --------------------

+08:00               -02:00

SQL>insert into timezone_test select timestamp ‘2016-04-24 15:14:00 +3:00‘,timestamp ‘2016-04-24 15:14:00 +3:00‘,timestamp ‘2016-04-24 15:14:00 +3:00‘ from dual;

1 row created.

SQL>select * from timezone_test;

T0                             T1                                   T2

------------------------------ ------------------------------------ ------------------------------

24-APR-16 02.35.03.613433 PM   24-APR-16 02.35.03.613433 PM +08:00  24-APR-16 02.35.03.613433 PM

24-APR-16 05.03.35.050304 AM   24-APR-16 05.03.35.050304 AM -02:00  24-APR-16 03.03.35.050304 PM

24-APR-16 03.14.00.000000 PM   24-APR-16 03.14.00.000000 PM +03:00  24-APR-16 08.14.00.000000 PM

我们留意到,T0和T1都是我们插入的timestamp字符串中的时间,而T2的时间则已经是(+3) - (-2) = 5,即已经进行了转化。

SQL>alter session set time_zone=‘+3:00‘;

Session altered.

SQL>select * from timezone_test;

T0                             T1                                   T2

------------------------------ ------------------------------------ ------------------------------

24-APR-16 02.35.03.613433 PM   24-APR-16 02.35.03.613433 PM +08:00  24-APR-16 09.35.03.613433 AM

24-APR-16 05.03.35.050304 AM   24-APR-16 05.03.35.050304 AM -02:00  24-APR-16 10.03.35.050304 AM

24-APR-16 03.14.00.000000 PM   24-APR-16 03.14.00.000000 PM +03:00  24-APR-16 03.14.00.000000 PM

而我们把时区再设置成我们插入时指定的+3时区时,即显示出现的时间即是我们插入时的时间字符串的值了。

所以,在Oracle中,TIMESTAMP WITH LOCAL TIME ZONE会随着用户所在时区(SESSIONTIMEZONE)而变化,而TIMESTAMP WITH TIME ZONE则不随用户所在时区的变化而变,简单的说,这两个时间类型的参照时间不同,一个是参照用户的时区,一个是参照数据库的时区。

timestamp with time zone则要加上时区,插入数据时插的什么时区就显示什么时区,不会改变为别的或数据库所在时区,或查询人所在地的时区。插入数据时,如果写时区,那么显示的时候以插入时候的时区显示出来,而不是数据库所在时区,或查询人所在地的时区的时间;并且也不会转换这个时间。

而timestamp with local time zone 就是显示的时候不加后面的时区如+8:00。会存在转换的问题。插入数据的时候带时区就会转换会数据库所在时区,或查询人所在地的时区来显示数据。插入的时候不带时区,则认为跟数据库所在的时区是一样,这样查询时的所在地如果与数据库一样的时区,则时间不变,如果不一致,则还要转换为查询所在的时区的时间。

但是,如果建库时,时间设置错误或者是将来要改变时区时,表中的值会不会变化呢?由于我没测试环境,因此引用官方的回答:

对于time zone数据类型的数据,即使你更新了数据库时区,原数据也不会进行对应调整,只能你导出数据,然后调整数据库时区,再把原始数据导入即可。所以,一般情况下,一定不要调整数据库时区。官方建议数据库时间采用UTC,因为这种时区性能好。如果没有显式指定数据库时区,数据库会使用操作系统的时区,但是如果操作系统时区不是一个合理的数据库时区,数据库则会使用默认的时区UTC,且UTC的取值范围为-12:00 to +14:00。等等,为什么会有+14?百度一下基里巴斯。这是一个神奇的网站国度。

MySQL

看下MySQL 5.6 Reference Manual对timestamp的定义

The TIMESTAMP data type is used for values that contain both date and time parts. TIMESTAMP has a range of ‘1970-01-01 00:00:01‘ UTC to ‘2038-01-19 03:14:07‘ UTC.

MySQL converts TIMESTAMP values from the current time zone to UTC for storage, and back from UTC to the current time zone for retrieval. (This does not occur for other types such as DATETIME .) By default, the current time zone for each connection is the server‘s time. The time zone can be set on a per-connection basis. As long as the time zone setting remains constant, you get back the same value you store. If you store a TIMESTAMP value, and then change the time zone and retrieve the value, the retrieved value is different from the value you stored. This occurs because the same time zone was not used for conversion in both directions. The current time zone is available as the value of the time_zone system variable.

我们直接看下mysql中date和timestamp的不同:

DATETIME

1.8个字节储存(8 bytes storage)

2.实际格式储存(Just stores what you have stored and retrieves the same thing which you have stored.)

3.与时区无关(It has nothing to deal with the TIMEZONE and Conversion.)

TIMESTAMP

1.4个字节储存(Time stamp value is stored in 4 bytes)

2.值以UTC格式保存( it stores the number of milliseconds)

3.时区转化,存储时对当前的时区进行转换,检索时再转换回当前的时区。

mysql的timestamp没有oracle复杂,直接实验一下。

$date -R

Sun, 24 Apr 2016 05:50:02 -0700

[email protected] 05:50:16>show variables like ‘%time_zone%‘;

+------------------+--------+

| Variable_name    | Value  |

+------------------+--------+

| system_time_zone | PDT    |

| time_zone        | SYSTEM |

+------------------+--------+

[email protected] 05:50:14>create table timezone_test (

->   t1 datetime default null,

->   t2 timestamp not null default current_timestamp on update current_timestamp);

[email protected] 05:52:11>select * from timezone_test;

+---------------------+---------------------+

| t1                  | t2                  |

+---------------------+---------------------+

| 2016-04-24 05:52:11 | 2016-04-24 05:52:11 |

+---------------------+---------------------+

[email protected] 05:52:21>set time_zone=‘+8:00‘;

[email protected] 05:52:47>select * from timezone_test;

+---------------------+---------------------+

| t1                  | t2                  |

+---------------------+---------------------+

| 2016-04-24 05:52:11 | 2016-04-24 20:52:11 |

+---------------------+---------------------+

[email protected] 05:52:53>insert into timezone_test values(current_timestamp,current_timestamp);

[email protected] 05:52:59>select * from timezone_test;

+---------------------+---------------------+

| t1                  | t2                  |

+---------------------+---------------------+

| 2016-04-24 05:52:11 | 2016-04-24 20:52:11 |

| 2016-04-24 20:55:04 | 2016-04-24 20:55:04 |

+---------------------+---------------------+

[email protected] 05:55:04>set time_zone=‘-7:00‘;

[email protected] 05:55:12>show variables like ‘%time_zone%‘;

+------------------+--------+

| Variable_name    | Value  |

+------------------+--------+

| system_time_zone | PDT    |

| time_zone        | SYSTEM |

+------------------+--------+

[email protected] 05:55:22>select * from timezone_test;

+---------------------+---------------------+

| t1                  | t2                  |

+---------------------+---------------------+

| 2016-04-24 05:52:11 | 2016-04-24 05:52:11 |

| 2016-04-24 20:55:04 | 2016-04-24 05:55:04 |

+---------------------+---------------------+

而在刚才创建表时timestamp列时用的default值,有下面的结论,有兴趣可以做实验下:

  • 在创建新记录和修改现有记录的时候都对这个数据列刷新:

TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

  • 在创建新记录的时候把这个字段设置为当前时间,但以后修改时,不再刷新它:

TIMESTAMP DEFAULT CURRENT_TIMESTAMP

  • 在创建新记录的时候把这个字段设置为0,以后修改时刷新它:

TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

  • 在创建新记录的时候把这个字段设置为给定值,以后修改时刷新它:

TIMESTAMP DEFAULT ‘yyyy-mm-dd hh:mm:ss‘ ON UPDATE CURRENT_TIMESTAMP

那比如原mysql的时区是-7:00,然后又新增一个读库,这个读库的时区反而是在+8:00,它的实际值会有不同么?我们来实验下:

源库:

$date -R

Tue, 26 Apr 2016 01:47:52 -0700

[email protected] 02:01:21>show variables like ‘%time_zone%‘;

+------------------+--------+

| Variable_name    | Value  |

+------------------+--------+

| system_time_zone | PDT    |

| time_zone        | SYSTEM |

+------------------+--------+

[email protected] 02:01:25>create table timestamp_test(t0 timestamp);

[email protected] 02:01:31>insert into timestamp_test values(current_timestamp);

[email protected] 02:01:46>select * from timestamp_test;

+---------------------+

| t0                  |

+---------------------+

| 2016-04-26 02:01:46 |

+---------------------+

新读库:

$date -R

Tue, 26 Apr 2016 16:52:39 +0800

[email protected] 05:02:54>show variables like ‘%time_zone%‘;

+------------------+--------+

| Variable_name    | Value  |

+------------------+--------+

| system_time_zone | CST    |

| time_zone        | SYSTEM |

+------------------+--------+

[email protected] 05:03:00>select * from timestamp_test;

+---------------------+

| t0                  |

+---------------------+

| 2016-04-26 17:01:46 |

+---------------------+

1 row in set (0.00 sec)

[email protected] 05:03:02>set time_zone=‘-7:00‘;

[email protected] 05:03:48>select * from timestamp_test;

+---------------------+

| t0                  |

+---------------------+

| 2016-04-26 02:01:46 |

+---------------------+

因为mysql的主备同步,同步的还是sql,就算是row模式的同步也只是把其实同步的值以物理块的形式传输,实际还是要在目标端转换为sql来执行,仍然是带有时区信息,会在客户端进行时区转换。

总结一下:

1、Oracle和MySQL中的timestamp的作用是不同的

  • Oracle中,TIMESTAMP是对date的更高精度的一种存储,是作为datetime的延展,但它不存储时区信息
  • Oracle中,TIMESTAMP WITH TIME ZONE存储时区信息
  • Oracle中,TIMESTAMP WITH LOCAL TIME ZONE不会存储时区信息,将时间数据转换为数据库时区的时间数据进行存储,但不存储时区信息;客户端检索时,oracle会将数据库中存储的时间数据转换为客户端session时区的时间数据后返回给客户端
  • MYSQL中,的TIMESTAMP是为了更少的存储单元(DATETIME为4字节,TIMESTAMP为1个字节)但是范围为1970的某时的开始到2037年,而且会根据客户端的时区判断返回值,MYSQL的TIMESTAMP时区敏感这点和ORACLE的TIMESTAMP WITH LOCAL TIME ZONE一致。

2、ORACLE和MYSQL的函数返回不一样

  • oracle读取的时区信息是以client端为准,CURRENT_TIMESTAMP都受到客户端SESSION TIMEZONE影响,而SYSDATE,SYSTIMESTAP不受影响
  • mysql读取的时区信息是以server端为准,NOW(),SYSDATE(),CURRENT_TIMESTAMP 均不受到客户端连接时区影响
  • DTS始终为client端,数据到达DTS时,都统一变成纯字符串

3、Oracle的DBTIMEZONE只和TIMESTAMP WITH LOCAL TIME ZONE有关。MySQL中的time_zone直接影响所有的timestamp取值。

4、为了返回一致的数据MYSQL设置TIME_ZONE参数即可,因为他是每个连接都会用到的,但是ORACLE最好使用SYSDATE或者SYSTIMESTAMP来直接取DB SERVER端时间。

5、MySQL修改时区信息,只要CLIENT端的时区信息不变,此无影响。

6、Oracle修改时间信息,同理,TIMESTAMP WITH LOCAL TIME ZONE不受影响,TIMESTAMP和TIMESTAMP WITH TIME ZONE会发生变化。

7、如果在client中不指定时区信息,oracle以client端的时区信息为准,要进行转换,mysql以server端的时区信息为准。

时间: 2024-11-10 13:08:38

oracle、mysql时区设置对timestamp的不同影响的相关文章

Oracle参数Arraysize设置对于逻辑读的影响分析

说明: 当执行一条SQL查询的时候,为了获得满足的数据,查询在这个过程中完成解析,绑定,执行和提取数据等一系列步骤,这些步骤都是单独执行的,满足条件的数据行必须由数据库返回给应用:对于任何大小的结果集,需要返回的数据行很可能不是在一次往返调用过程中传递给应用的! 每次调用过程中,数据库与客户端之间的往返回路数将一定层次上影响总的响应时间,其中除了提取数据(FETCH)步骤,其余步骤(解析,绑定,执行)都只执行一次,这也是必要的,Oracle需要获得满足查询条件的所有数据结果从而执行多次提取操作.

【MySQL】探究之TIMESTAMP

背景 之前有业务反馈表中start_time,end_time时间字段随着时间的推移被自动更新,这可不是业务意愿,说的严重点是要出故障的. MySQL中有DATE,DATETIME,TIMESTAMP时间类型 看看官方文档怎么说 The DATE type is used for values with a date part but no time part. MySQL retrieves and displays DATE values in 'YYYY-MM-DD' format. Th

MySQL 字符集设置

/*************************************************************************** * MySQL 字符集设置 * 说明: * 数据库这块很多时候都会因为字符集不兼容导致数据通信异常,所以这边把 * MySQL的字符集设定为utf-8,这个比较通用. * * 2016-9-24 深圳 南山平山村 曾剑锋 **************************************************************

MySQL 5.6 中 TIMESTAMP 的变化

在MySQL 5.6.6之前,TIMESTAMP的默认行为: TIMESTAMP列如果没有明确声明NULL属性,默认为NOT NULL.(而其他数据类型,如果没有显示声明为NOT NULL,则允许NULL值.)设置TIMESTAMP的列值为NULL,会自动存储为当前timestamp. 表中的第一个TIMESTAMP列,如果没有声明NULL属性.DEFAULT或者 ON UPDATE,会自动分配 DEFAULT CURRENT_TIMESTAMP和ON UPDATE CURRENT_TIMEST

mysql-connector-java 6.x 时区设置

最近遇到了一个问题,java从mysql从读出的时间与本地时间有14个小时的时间差,经查证.测试解决了此问题,在此总结一下: 一.使用mysql-connector-java 6.x版本驱动需要做如下配置: 1.引入pom <dependency>   <groupId>mysql</groupId>   <artifactId>mysql-connector-java</artifactId>   <version>6.0.6<

Lumen 时区设置

根据 Laravel 4.x 和 5.0 的经验, 只需要到 config/app.php 中设置下 'timezone' 参数为 'PRC' 就好了, 找到 Lumen 的 config 目录, 在 /vendor/laravel/lumen-framework/config 路径下, 但是 config/app.php 的参数选项中没有 timezone 参数选项, 手动加上后也是无效的. 然后想到 Laravel 5 的 .env 文件, 结果发现 Lumen 的 .env 文件里也没有关

Mysql 如何设置字段自动获取当前时间

应用场景: 1.在数据表中,要记录每条数据是什么时候创建的,不需要应用程序去特意记录,而由数据数据库获取当前时间自动记录创建时间: 2.在数据库中,要记录每条数据是什么时候修改的,不需要应用程序去特意记录,而由数据数据库获取当前时间自动记录修改时间: 实现方式: 1.将字段类型设为  TIMESTAMP  2.将默认值设为  CURRENT_TIMESTAMP 举例应用: 1.MySQL 脚本实现用例 --添加CreateTime 设置默认时间 CURRENT_TIMESTAMP  ALTER

Navicat for MySQL 选项设置技巧详解

Navicat for MySQL给用户提供了完整的用户自定义设置界面选项,从主菜单选择工具->选项,但是很多的用户第一次接触,对这方面如何设置不是很了解,本教程将详细的给大家介绍介绍Navicat for MySQL 选项设置技巧详解.希望可以帮到大家. Navicat for MySQL 常规选项 Navicat 常规选项主要包括以下内容: 窗口在工作列显示:每打开一个新窗口时会自动显示在 Windows 任务栏.停用该选项后,当退出 Navicat 主窗口时,所有窗口(例如:表.查询)将会

Oracle/Mysql/SqlServer函数区别

mysql日期和时间格式转换 Linux scp 使用详解 Oracle/Mysql/SqlServer函数区别 2011-07-01 12:34:36|  分类: Mysql技术 |  标签:mysql  sqlsever  oracle  函数   |举报 |字号大中小 订阅 Sql代码 1.类型转换 --Oracle select to_number('123') from dual; --123; select to_char(33) from dual;  --33; select t