我的要求是从日期类型列中获取GMT/UTC时间。但当我使用cast将日期转换为时间戳时,它使用的是美国/太平洋时区作为参考,尽管会话时区设置为GMT。因此,除非我使用from_tz,否则我看不到期望的结果。oracle sql中是否有其他时区设置需要修改以始终以GMT为参考?
alter session set time_zone='+00:00';
select sessiontimezone from dual;
select current_timestamp from dual;
select sys_extract_utc(cast (sysdate as timestamp)) from dual;
select sys_extract_utc(from_tz(cast (sysdate as timestamp), '-07:00')) from dual;
select sys_extract_utc(current_timestamp) from dual;
Session altered.
SESSIONTIMEZONE
---------------------------------------------------------------------------
+00:00
CURRENT_TIMESTAMP
---------------------------------------------------------------------------
11-APR-16 08.46.42.292173 AM +00:00
SYS_EXTRACT_UTC(CAST(SYSDATEASTIMESTAMP))
---------------------------------------------------------------------------
11-APR-16 01.46.42.000000 AM
SYS_EXTRACT_UTC(FROM_TZ(CAST(SYSDATEASTIMESTAMP),'-07:00'))
---------------------------------------------------------------------------
11-APR-16 08.46.42.000000 AM
SYS_EXTRACT_UTC(CURRENT_TIMESTAMP)
---------------------------------------------------------------------------
11-APR-16 08.46.42.295310 AM
Tasks表有一个名为task_started的日期类型列。我希望从该日期字段中获取UTC时间。作为其中的一部分,我试图在插入数据时将会话时区更改为GMT,这样我就可以简单地将其转换回不起作用的时间戳。
select task_started from tasks where rownum <2;
TASK_STAR
---------
10-APR-16
desc tasks;
Name Null? Type
----------------------------------------- -------- ----------------------------
...
TASK_STARTED DATE
...
这是一个在伦敦时间的系统上使用sysdate
插入的数据的演示,目前在英国夏令时(+01:00)。这种差异比你在西海岸看到的要小,但同样的情况也适用。
模拟您的表,您可以看到会话时区对插入的值没有影响,因为sysdate
使用数据库服务器时间,而不是会话(客户端)时间(如这里所解释的):
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF3';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF3 TZH:TZM';
create table tasks (id number, task_started date);
alter session set time_zone='America/Los_Angeles';
insert into tasks (id, task_started) values (1, sysdate);
select task_started, cast(task_started as timestamp) as ts
from tasks where rownum < 2;
TASK_STARTED TS
------------------- -----------------------
2016-04-11 11:55:59 2016-04-11 11:55:59.000
alter session set time_zone = 'UTC';
select task_started, cast(task_started as timestamp) as ts
from tasks where rownum < 2;
TASK_STARTED TS
------------------- -----------------------
2016-04-11 11:55:59 2016-04-11 11:55:59.000
所以这两种情况下都是BST时间。没有任何会话或数据库设置会向您显示自动转换为特定时区的日期(或时间戳,无时区)。除非你告诉数据库,否则数据库不知道存储的日期/时间代表什么。如果你使用sysdate
、current_date
或日期文字,它也不知道或区分大小写;在将数据插入表时,它根本没有时区信息。
要获得UTC等效值,您需要使用from_tz
来声明存储的值表示特定时区;则可以使用at time zone
(保留时区信息)或sys_extract_utc
(不保留)对其进行转换;并可选地回溯到日期:
select from_tz(cast(task_started as timestamp), 'Europe/London') as db_tstz,
from_tz(cast(task_started as timestamp), 'Europe/London') at time zone 'UTC' as utc_tstz,
sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as utc_ts,
cast(sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as date) as utc_date
from tasks where rownum < 2;
DB_TSTZ UTC_TSTZ UTC_TS UTC_DATE
------------------------------ ------------------------------ ----------------------- -------------------
2016-04-11 11:55:59.000 +01:00 2016-04-11 10:55:59.000 +00:00 2016-04-11 10:55:59.000 2016-04-11 10:55:59
我使用了时区区域名称,所以它可以照顾我的夏季时间;你可以使用dbtimezone
,但它总是使用-08:00,正如你在问题中所展示的,你现在需要使用-07:00。你可以使用sessiontimezone
,但你必须记住正确设置。显然,在你的情况下,你会使用当地的地区,例如美国/洛杉矶,而不是欧洲/伦敦。
从中你可以通过比较时间戳或更简单地通过比较日期来获得历元:
select sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London'))
- timestamp '1970-01-01 00:00:00' as diff_interval,
cast(sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as date)
- date '1970-01-01' as diff_days,
86400 *
(cast(sys_extract_utc(from_tz(cast(task_started as timestamp), 'Europe/London')) as date)
- date '1970-01-01') as epoch
from tasks where rownum < 2;
DIFF_INTERVAL DIFF_DAYS EPOCH
---------------- --------- -----------
16902 10:55:59.0 16902.46 1460372159
如果您将1460372159放入在线转换器(有很多),它将显示UTC时间。