Oracle sql时区问题



我的要求是从日期类型列中获取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时间。没有任何会话或数据库设置会向您显示自动转换为特定时区的日期(或时间戳,无时区)。除非你告诉数据库,否则数据库不知道存储的日期/时间代表什么。如果你使用sysdatecurrent_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时间。

相关内容

  • 没有找到相关文章

最新更新