将EST的时间戳转换为每个特定用户的时区

时间:2018-08-28 13:46:17

标签: oracle timestamp

我正在开发一个包含数据库时间戳记的程序。 db time设置在EST中设置。我已经看到很多关于从一个时区转换为另一个时区的文章,但是有没有办法从EST转换为用户所在的任何时区?

2 个答案:

答案 0 :(得分:0)

Oracle数据库中有两个时区设置:数据库的和会话的。这些可以不同。您可以通过以下内容找到这些内容:

select dbtimezone db, sessiontimezone sess from dual;

DB                             SESS
------------------------------ ------------------------------
+00:00                         Europe/London

alter session set time_zone = 'Australia/Perth';

select dbtimezone db, sessiontimezone sess from dual;

DB                             SESS
------------------------------ ------------------------------
+00:00                         Australia/Perth

您可以使用at time zone子句将时间戳从一个时区转换为另一个时区。例如:

alter session set time_zone = 'Europe/London';

select systimestamp server,
       systimestamp at time zone sessiontimezone sess,
       systimestamp at time zone 'Asia/Calcutta' india,
       current_timestamp curr_ts
from   dual;

SERVER                              SESS                                     INDIA                                    CURR_TS
----------------------------------- ---------------------------------------- ---------------------------------------- ----------------------------------------
28-AUG-18 08.46.59.104889 -07:00    28-AUG-18 16.46.59.104889 EUROPE/LONDON  28-AUG-18 21.16.59.104889 ASIA/CALCUTTA  28-AUG-18 16.46.59.104899 EUROPE/LONDON

注意:systimestamp返回服务器的日期,时间和时区。这可能与DBTIMEZONE不同! Current_timestamp是会话的时区。

此转换的确切工作方式取决于您存储值的方式。

共有三种时间戳数据类型:

  • 时间戳
  • 带有时区的时间戳
  • 带有本地时区的时间戳

普通的旧时间戳没有时区信息。因此,您需要知道您的应用程序将其存储在哪个TZ!添加at time zone子句返回的时间与添加的时区相同。

带有时区存储的时间戳按原样保留时区。使用at time zone返回转换为提供的时区的日期。

本地时区将数据标准化为数据库的时区。查询它们时,数据库会将它们转换为会话时区。使用at time zone返回转换为提供的时区的日期。

create table t (
  ts     timestamp,
  ts_tz  timestamp with time zone,
  ts_ltz timestamp with local time zone
);
insert into t values ( 
  timestamp '2018-01-01 00:00:00', timestamp '2018-01-01 00:00:00 UTC', timestamp '2018-01-01 00:00:00 UTC'
);
insert into t values ( 
  timestamp '2018-01-01 00:00:00', timestamp '2018-01-01 00:00:00 America/Denver', timestamp '2018-01-01 00:00:00 America/Denver'
);

select * from t;

TS                                       TS_TZ                                    TS_LTZ
---------------------------------------- ---------------------------------------- ----------------------------------------
01-JAN-18 00.00.00.000000                01-JAN-18 00.00.00.000000 UTC            01-JAN-18 00.00.00.000000
01-JAN-18 00.00.00.000000                01-JAN-18 00.00.00.000000 AMERICA/DENVER 01-JAN-18 07.00.00.000000

alter session set time_zone = 'Asia/Calcutta';

select * from t;

TS                                       TS_TZ                                    TS_LTZ
---------------------------------------- ---------------------------------------- ----------------------------------------
01-JAN-18 00.00.00.000000                01-JAN-18 00.00.00.000000 UTC            01-JAN-18 05.30.00.000000
01-JAN-18 00.00.00.000000                01-JAN-18 00.00.00.000000 AMERICA/DENVER 01-JAN-18 12.30.00.000000

select ts at time zone sessiontimezone ts,
       ts_tz at time zone sessiontimezone ts_tz,
       ts_ltz at time zone sessiontimezone ts_ltz
from   t;

TS                                       TS_TZ                                    TS_LTZ
---------------------------------------- ---------------------------------------- ----------------------------------------
01-JAN-18 00.00.00.000000 ASIA/CALCUTTA  01-JAN-18 05.30.00.000000 ASIA/CALCUTTA  01-JAN-18 05.30.00.000000 ASIA/CALCUTTA
01-JAN-18 00.00.00.000000 ASIA/CALCUTTA  01-JAN-18 12.30.00.000000 ASIA/CALCUTTA  01-JAN-18 12.30.00.000000 ASIA/CALCUTTA

如果将值存储在date中,则相同的规则适用于纯时间戳记。

答案 1 :(得分:0)

如果我正确理解您的问题,那么当前存储的只是一个时间戳。您知道时间戳记应该在EST中;数据库不知道。

(顺便说一下,为什么要使用EST?您是否不需要一个带有夏时制时间偏移的时区?或者换句话说,您是否100%确定它是EST而不是EDT?)

理想地,如果目的是在本地时区工作,则该列的数据类型不应为timestamptimestamp with time zone;相反,它应该是timestamp with local time zone。每当以这种数据类型输入数据时,您仅提供一个时区。 Oracle认为这是您会话的时区;它将其转换为服务器时区并存储。没有存储时区信息,但列类型有意义。检索到相同的数据后,它将转换为调用者的时区。

在您的情况下,如果时间戳记不带时区存储在数据类型timestamp的列中,则在检索数据时需要做两件事。首先,您需要让Oracle知道时间戳是在时区EST中(或其实际应该在的其他时区中)。然后-在此步骤之后-您可以将其转换为带有 local 时区的时间戳;那么行为就变得与理想安排中的行为相同,在理想安排中,列从一开始就是timestamp with local time zone数据类型。

下图显示了两个步骤。

这是您的基表-带有timestamp类型的单列和单行。您知道时间戳记是时区EST;数据库没有。

create table tbl(ts timestamp);

insert into tbl(ts)
  values (timestamp '2018-08-24 13:50:23.392302000');

这是我建议的解决方案。只有最后一列才是您真正需要的。我显示第二列来说明所需的中间步骤。请注意第二列与第一列之间的差异:第二列的时区显示在末尾(-05:00)。另请注意,第三列将取决于我(或您或任何其他人的)会话时区。矿井(目前是夏令时)比美国东部标准时间晚两个小时;这将比美国东部时间晚三小时。我在美国西海岸。

select ts,
       from_tz(ts, 'EST')                                         ts_with_time_zone,
       cast(from_tz(ts, 'EST') as timestamp with local time zone) ts_in_local_time_zone
from   tbl;

TS                            TS_WITH_TIME_ZONE                    TS_IN_LOCAL_TIME_ZONE        
----------------------------- ------------------------------------ -----------------------------
2018-08-24 13:50:23.392302000 2018-08-24 13:50:23.392302000 -05:00 2018-08-24 11:50:23.392302000