Oracle 时区详解

1 简介

由于地球经纬度及地球自转引起的经度方向,不同的经度的地方,所感受到的昼夜是不同 的。有关国际会议决定将地球表面按经线从东到西,每隔经度15度划分一个时区,并且规定 相邻区域的时间相差1小时。

这就是时区的由来。

而实际使用中,往往不是严格按照这个标准来做的。国家的存在,为了行政工作的方便,一 个国家都有一个本国的标准时,大部分国家以首都所在的时区作为国家的标准时。比如, 我大中国以北京所在时区(东八区)作为中国的标准时。在IT领域,很多时候我们都会涉及 到时区的问题。很不幸的是,很多计算机方面的标准都是西欧、北美这些发达国家定制 的.比如东八区的时区名,并不是Beijing,而是Shanghai,当然,完整的时区名应该是 "Asia/Shanghai".

1884年在华盛顿召开的国际经度会议决定以经过格林尼治的经线为本初子午线,是世界计 算时间和地理经度的起点,也是世界标准时,又称其为格林尼治标准时, 其时区名为 UT. 而由于世界原子时(纳秒为基础)与世界时(毫秒为基础)的计算单位不同,两者之间的差别会 越来越大,各政府更倾向于以自然时间UT为标准,因此世界原子时需要向世界时靠拢,于 1972年,世界上达成一致解决方案即为:协调世界时,即UTC。随后UTC,即成为了各个领 域特别是IT行业的时间标准。

而UTC的由来,不过是国力强弱之争妥协的产物。英文(CUT)和法文(TUC)的缩写不同, 作为妥协,简称UTC。

很多同仁在工作时,不会特别关注时区问题,即使用默认的时间配置,而在处理实际数据 的时候才发现,操作时间或者系统显示时间与本地时间相差了几个小时,在我大中国,一 般就是相差8个小时。只要你遇到了反馈时间与实际时间相差8个小时的情况,你就往时区 方面去考虑,这个方向基本上是正确的。

2 Oracle TimeZone

2.1 时区文件

Oracle 将支持的时区信息,比如时区名,时区缩写名都保存在文件中。文件的默认存储 路径为$ORACLE_HOME/oracore/zoneinfo/ 及其子路径中(big和little). 这些路径中中的 同名文件,其实是一样的。都是二进制文件,但是从文件大小来看,可以判断,同名文件 虽然在不同的路径中,但是文件内容是一样的。

文件的内容,在数据库启动时,会自动加载,并呈现在视图"V$TIMEZONE_FILE" 中,告知我 们, 数据库现在使用的是哪个文件。

文件分为两种类型:timezlrg_version.dat和timezone_version.dat .

2.2 时区名字

关于Oracle支持的时区,基本上涵盖了已知的常用的时区。

数据库启动时通过文件加载的内容,呈现在视图:V$TIMEZONE_NAMES 中。

bash 复制代码
SQL> col tzname for a20
SQL> col tzabbrev for a10
SQL> select * from V$TIMEZONE_NAMES where tzname = 'Asia/Shanghai';

TZNAME               TZABBREV       CON_ID
-------------------- ---------- ----------
Asia/Shanghai        LMT                 0
Asia/Shanghai        CST                 0
Asia/Shanghai        CDT                 0

可以看到查到了三个值:

  • LMT(Local Mean Time) :全球标准时间,UTC的前身, 这个基本不用了。
  • CST(China Standard Time) :中国标准时间, 有其他的解释
  • CDT(Central Daylight Time):中部夏令时间,主要用于北美,比UTC晚5小时。另外,还可以理解成(Cuba Daylight Time)古巴夏令时。

更多的时区查询可以去:https://datetime360.com/cn/worldwide-city-list/ 查询。

由于缩写中有很多同名的,为了避免冲突,我们一般使用全名。

3.Oracle 时区设置

Oracle 的时区可以分为两种,一种是数据库的时区,一种是 session 时区,也就是客户端连接时的时区。

数据库的时区在创建数据库时可以通过在 create database 语句中加上 SET TIME_ZONE = ' { { + | - } hh : mi | time_zone_region } ' 来指定,如果,不指定,默认是按照数据库所在的操作系统时区来设定的。

bash 复制代码
--创建数据库时指定时区

CREATE DATABASE databasename SET TIME_ZONE='Asia/Shanghai';

创建之后,可以通过 alter database 来修改。其中 time_zone_region 参数可以通过查询 V$TIMEZONE_NAMES 动态视图来获得所有支持的值。修改之后,需要重启数据库才能生效。

Session 的时区是根据客户端的时区来决定的,当然连接以后也可以通过 alter session 来改变。WITH LOCAL TIME ZONE 类型会根据 TIME_ZONE 的设置,自动把时间转换为 session 所在时区的时间显示出来,而 WITH TIME ZONE 因为保存了时区,不需要根据 TIME_ZONE 的设置来转换。

3.1 查看时区

可以分别使用 SESSIONTIMEZONE / DBTIMEZONE 内建函数查看 session 和数据库时区:

查看会话时区:

bash 复制代码
SQL> select sessiontimezone from dual;

SESSIONTIMEZONE
---------------------------------------------------------------------------
+08:00

查看数据库时区:

bash 复制代码
SQL> select dbtimezone from dual;

DBTIME
------
+08:00

bash 复制代码
SELECT property_name, property_value
FROM database_properties
WHERE property_name='DBTIMEZONE';

bash 复制代码
SELECT name, value$
FROM sys.props$
WHERE name='DBTIMEZONE';

3.2 调整时区

调整会话时区:

(1)SQL 命令

bash 复制代码
---示例

ALTER SESSION SET TIME_ZONE = local;

ALTER SESSION SET TIME_ZONE = dbtimezone;

ALTER SESSION SET TIME_ZONE = '+08:00';

ALTER SESSION SET TIME_ZONE = 'Asia/Shanghai';

(2)环境变量

bash 复制代码
$ ORA_SDTZ='-05:00'
$ export ORA_SDTZ

调整数据库时区(需要重启数据库)

bash 复制代码
--示例

ALTER DATABASE SET TIME_ZONE = '-05:00';

ALTER DATABASE SET TIME_ZONE = 'Europe/Lisbon';

修改数据库时区时可能会遇到如下报错:

bash 复制代码
SQL> alter database set time_zone='+06:00';
alter database set time_zone='+06:00'
*
ERROR at line 1:
ORA-02231: missing or invalid option to ALTER DATABASE

TOM 对此问题有过解释,TIME_ZONE 的设定主要是为了 WITH LOCAL TIME ZONE,当 session 的时区和数据库的时区不同时,oracle 根据时区的差距转换到数据库的时间,再保存到数据库的 WITH LOCAL TIME ZONE 类型中,他是不保存时区的,所以需要 TIME_ZONE 来进行各种时区之间时间的转换(WITH TIME ZONE 类型保存了原始的时区,所以不需要 TIME_ZONE 的设置也可以进行各种时区之间的转换)。但数据库中一旦有了该类型,就不能通过 alter database 修改时区了,会得到上面的错误,可以通过下面的语句获得所有包含该类型的表,将他们删除之后,再修改。

bash 复制代码
select u.name || '.' || o.name || '.' || c.name TSLTZcolumn
  from sys.obj$ o, sys.col$ c, sys.user$ u
where c.type# = 231
   and o.obj# = c.obj#
   and u.user# = o.owner#;

一般查询后的结果为:OE.ORDERS.ORDER_DATE,指的是OE用户下的ORDERS表的ORDER_DATE字段使用了时区的信息:WITH LOCAL TIME ZONE,将此信息去掉就可以再修改了,修改好了之后需要重启数据库才能生效。

更多内容可参考文章《oracle DBTIMEZONE 时区调整》

3.3 查询在指定时区的当地时间

bash 复制代码
SQL> SELECT TZ_OFFSET('Asia/Shanghai') FROM DUAL;

TZ_OFFS
-------
+08:00

SQL> SELECT TZ_OFFSET('Europe/London') FROM DUAL;

TZ_OFFS
-------
+01:00

3.4 查询某时区和 UTC 之间的差值。

TZ_OFFSET ( { 'time_zone_name'

| '{ + | - } hh : mi'

| SESSIONTIMEZONE

| DBTMEZONE }

)

bash 复制代码
SQL> SELECT TZ_OFFSET('US/Eastern') FROM DUAL;

TZ_OFFS
-------
-04:00

SQL>  SELECT TZ_OFFSET(DBTIMEZONE) FROM DUAL;

TZ_OFFS
-------
+08:00

其中 time_zone_name 也可以从 V$TIMEZONE_NAMES 获得。

4. Oracle 时间函数

Oracle 9i 开始多了 3 个关于时间的数据类型:

  • TIMESTAMP [(precision)]
  • TIMESTAMP [(precision)] WITH TIME ZONE
  • TIMESTAMP [(precision)] WITH LOCAL TIME ZONE

其中 TIMESTAMP [(precision)] WITH TIME ZONE 保存了时区信息。

sysdate/systimestamp 都是返回数据库的时间并且使用数据库的时区,他们返回的是操作系统的时间。

  • sysdate 返回的是 date 类型,没有时区信息,操作系统上是什么时间就返回什么时间;
  • systimestamp 返回 TIMESTAMP WITH TIME ZONE 类型,有时区信息。
bash 复制代码
SQL> select sysdate from dual;


SYSDATE
---------
25-AUG-23

SQL>  select systimestamp from dual;

SYSTIMESTAMP
---------------------------------------------------------------------------
25-AUG-23 11.38.48.569294 AM +08:00

参考文章:

https://www.cnblogs.com/firstyi/archive/2007/09/24/903931.html

https://www.cnblogs.com/halberd-lee/p/11245549.html

https://my.oschina.net/lvkun0223/blog/14438?p=

相关推荐
月光水岸New2 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6752 小时前
数据库基础1
数据库
我爱松子鱼2 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser3 小时前
【SQL】多表查询案例
数据库·sql
Galeoto3 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
人间打气筒(Ada)4 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231114 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql
喝醉酒的小白4 小时前
PostgreSQL:更新字段慢
数据库·postgresql
敲敲敲-敲代码4 小时前
【SQL实验】触发器
数据库·笔记·sql