TimeZones in Oracle

本文探讨了Oracle数据库中日期和时间戳类型如何处理时区差异,包括TIMESTAMP与DATE类型的使用区别,以及TIMESTAMP WITH TIMEZONE和TIMESTAMP WITH LOCAL TIMEZONE在不同会话中的表现。

The TIMESTAMP datatype was introduced in Oracle 9i. TIMESTAMP s differ from DATE s in that they allow fractions of a second to 9 decimal places and can be time zone aware.

Let's see how TIMESTAMP s and DATE s behave when they are simultaneously accessed by a user in the United States and a user in the United Kingdom. We can simulate this situation in a couple of sessions by setting the time zone of one session to US/Pacific and the other to GB. It is 8am in the US and 4pm in the UK.

Set Up

First we create this table in a database which is located in the UK.

CREATE TABLE t1 (
d DATE,
t TIMESTAMP,
tz TIMESTAMP WITH TIME ZONE,
ltz TIMESTAMP WITH LOCAL TIME ZONE
);

As you can see there are four datetime datatypes; DATE, TIMESTAMP, TIMESTAMP WITH TIME ZONE and TIMESTAMP WITH LOCAL TIME ZONE .

Next we set up a couple of sessions, one with a US/Pacific time zone and the other with a GB time zone. We also set the default date and time formats for the session.

US Session

ALTER SESSION SET time_zone = 'US/Pacific';

ALTER SESSION SET nls_date_format='DD-Mon-RR hh24:MI';
ALTER SESSION SET nls_timestamp_format='DD-Mon-RR HH24:MI';
ALTER SESSION SET nls_timestamp_tz_format='DD-Mon-RR HH24:MI TZR';

UK Session

ALTER SESSION SET time_zone = 'GB';

ALTER SESSION SET nls_date_format='DD-Mon-RR hh24:MI';
ALTER SESSION SET nls_timestamp_format='DD-Mon-RR HH24:MI';
ALTER SESSION SET nls_timestamp_tz_format='DD-Mon-RR HH24:MI TZR';

Database and Session Time Zones

There is a time zone associated with the database and a time zone associated with a session. We can display these values using the DBTIMEZONE and SESSIONTIMEZONE functions.

US session

SELECT dbtimezone, sessiontimezone FROM dual;

DBTIMEZONE SESSIONTIMEZONE
---------- -----------------
+00:00 US/Pacific

1 rows selected

UK session

SELECT dbtimezone, sessiontimezone FROM dual;

DBTIMEZONE SESSIONTIMEZONE
---------- -----------------
+00:00 GB

1 rows selected

Current Date and Time

We can also retrieve the current date or timestamp for the database and the session. SYSDATE and SYSTIMESTAMP return the database date and time and CURRENT_DATE , CURRENT_TIMESTAMP and LOCALTIMESTAMP return the session time. The difference between LOCALTIMESTAMP and CURRENT_TIMESTAMP is that LOCALTIMESTAMP returns a TIMESTAMP value while CURRENT_TIMESTAMP returns a TIMESTAMP WITH TIME ZONE value.

US session

SELECT sysdate, systimestamp FROM dual;

SYSDATE SYSTIMESTAMP
---------------------- --------------------------
05-Nov-2008 16:02 05-Nov-2008 16:02 +00:00

1 rows selected


SELECT current_date, current_timestamp, localtimestamp FROM dual;

CURRENT_DATE CURRENT_TIMESTAMP LOCALTIMESTAMP
--------------------- ------------------------------- ------------------
05-Nov-2008 08:02 05-Nov-2008 08:02 US/PACIFIC 05-Nov-2008 08:02

1 rows selected

UK session

SELECT sysdate, systimestamp FROM dual;

SYSDATE SYSTIMESTAMP
---------------------- --------------------------
05-Nov-2008 16:02 05-Nov-2008 16:02 +00:00

1 rows selected


SELECT current_date, current_timestamp, localtimestamp FROM dual;

CURRENT_DATE CURRENT_TIMESTAMP LOCALTIMESTAMP
--------------------- ----------------------- -------------------
05-Nov-2008 16:02 05-Nov-2008 16:02 GB 05-Nov-2008 16:02

1 rows selected

The SYSDATE and SYSTIMESTAMP values are the same for both sessions as you would expect but the CURRENT_DATE , CURRENT_TIMESTAMP and LOCALTIMESTAMP are different in the US and the UK because they reflect the time zone of the session.

Date and Timestamp

Now, let's insert some data into our table from the US session.

US session

INSERT INTO t1 (d, t, tz, ltz) VALUES (
TIMESTAMP '2008-11-05 11:22:00',
TIMESTAMP '2008-11-05 11:22:00',
TIMESTAMP '2008-11-05 11:22:00',
TIMESTAMP '2008-11-05 11:22:00'
);

COMMIT;


SELECT d, t FROM t1;

D T
----------------- -----------------
05-Nov-08 11:22 05-Nov-08 11:22

1 rows selected

The DATE and TIMESTAMP datatypes have no concept of time zones. They return the exact time that was entered, regardless of the time zone of the client. Therefore, the values will be the same for the US session and the UK session.

But what about timestamps with time zones?

US session

SELECT tz, ltz FROM t1;

TZ LTZ
---------------------------- -----------------
05-Nov-08 11:22 US/PACIFIC 05-Nov-08 11:22

1 rows selected

UK session

SELECT tz, ltz FROM t1;

TZ LTZ
---------------------------- -----------------
05-Nov-08 11:22 US/PACIFIC 05-Nov-08 19:22

1 rows selected


The TIMESTAMP WITH TIME ZONE datatype stores the time zone offset in its value, so the output of TZ is the same in both sessions. The time zone offset is the difference (in hours and minutes) between local time and UTC. Whenever the timestamp value is retrieved, the time zone is retrieved with it.

The LTZ column, on the other hand, displays a different value in each session. When a timestamp is stored in a TIMESTAMP WITH LOCAL TIME ZONE datatype, it is normalized to the database time zone. The time zone offset is not stored as part of the column data. When the user retrieves the data, Oracle automatically converts the value to the local session time zone.

A TIMESTAMP WITH LOCAL TIME ZONE value is the only timestamp datatype that will adjust the display value for a session time zone.

Explicit Conversion

So how do we convert a timestamp to a specific time zone in SQL? The answer is the AT TIME ZONE clause.

Suppose we wish to find out the current time in the US and the current time in Japan.

US session

SELECT current_timestamp us, 
current_timestamp AT TIME ZONE 'Japan' japan
FROM dual;

US JAPAN
---------------------------- ------------------------
05-Nov-08 03:15 US/PACIFIC 05-Nov-08 19:15 JAPAN

1 rows selected

Timestamp Comparisons

Two TIMESTAMP WITH TIME ZONE values are considered identical if they represent the same instant in UTC, regardless of the time zone offset stored in the data.

For example, using the data we inserted earlier.

US session

SELECT tz FROM t1 WHERE tz = TIMESTAMP '2008-11-05 11:22:00';

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC

1 rows selected

The date in the TZ column and the timestamp literal both correspond to a UTC time of 05-Nov-08 19.22

UK session

SELECT tz FROM t1 WHERE tz = TIMESTAMP '2008-11-05 11:22:00';

TZ
-------------

0 rows selected


SELECT tz FROM t1 WHERE tz = TIMESTAMP '2008-11-05 19:22:00';

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC

1 rows selected


SELECT tz FROM t1 WHERE tz = TIMESTAMP '2008-11-05 11:22:00 US/Pacific';

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC

1 rows selected


SELECT tz FROM t1
WHERE tz = TIMESTAMP '2008-11-05 11:22:00' + INTERVAL '8' HOUR;

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC

1 rows selected

In the first statement, the TZ column has a UTC time of 05-Nov-08 19.22 but the timestamp literal has a UTC time of 05-Nov-08 11.22 . The subsequent SQL statements adjust the timestamp literal to match the value in the table.

A Word About Timestamp Literals

A timestamp literal takes the following format.

TIMESTAMP '2008-11-05 11:22:00'

or

TIMESTAMP '2008-11-05 11:22:00 GB'

or

TIMESTAMP '2008-11-05 11:22:00 -08:00'


We could just use a character string.

SELECT tz FROM t1 WHERE tz = '5-Nov-08 11:22 US/Pacific'; 

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC

1 rows selected

However, I would recommend against this approach as we are no longer using a timestamp literal. Instead, we are using a character literal which is implicitly converted to a timestamp when it is compared with a date.

The problem with this technique is that the query can return different results depending on the value of the default timestamp format model for the session.

ALTER SESSION SET nls_timestamp_tz_format='DD-Mon-RR HH24:MI TZR';

SELECT tz FROM t1 WHERE tz = '5-Nov-08 11:22 US/Pacific';

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC

1 rows selected

Now we run the same SQL but with a different timestamp format.

ALTER SESSION SET nls_timestamp_tz_format='DD-Mon-YYYY HH24:MI TZR';

SELECT tz FROM t1 WHERE tz = '5-Nov-08 11:22 US/Pacific';

TZ
-------------

0 rows selected

For this reason, be sure to use a date or timestamp literal or an explicit conversion using the TO_DATE , TO_TIMESTAMP or TO_TIMESTAMP_TZ functions.

SELECT tz FROM t1 WHERE tz = 
TO_TIMESTAMP_TZ('5-Nov-08 11:22 US/Pacific', 'DD-Mon-YY HH24:MI TZR');

TZ
----------------------------
05-Nov-08 11:22 US/PACIFIC
### 关于时区 (TimeZone) 的处理方法 #### Java 中的 `TimeZone` 类 在 Java 编程语言中,可以通过 `java.util.TimeZone` 来设置和获取特定的时区。例如,下面展示了如何通过指定字符串 `"Asia/Shanghai"` 获取对应的时区对象[^1]。 ```java // 设置时区为上海 TimeZone timeZone = TimeZone.getTimeZone("Asia/Shanghai"); System.out.println(timeZone.getDisplayName()); ``` 此代码片段会创建一个表示中国标准时间(CST)的 `TimeZone` 对象,并打印其可读名称。 --- #### 时区的应用场景 无论是开发全球化 Web 应用程序还是需要在服务器端处理跨时区的数据,`TimeZone` 都能发挥重要作用[^2]。以下是几个典型应用场景: - **用户界面展示**:根据用户的地理位置动态调整显示的时间。 - **事件记录**:保存事件发生的本地时间,并能够根据不同地区的请求重新计算对应的时间。 - **用户体验优化**:将 UTC 时间转换成用户的本地时间,从而提升交互友好度。 - **复杂业务逻辑支持**:如日历安排或者航班预订系统中的精确时间管理。 这些功能使得开发者能够在多样的环境中灵活应对各种需求变化。 --- #### 数据库层面的支持——TIMESTAMP_LTZ 对于数据库操作而言,某些类型的字段设计也考虑到了国际化与时差因素的影响。比如 Snowflake 或其他现代关系型数据库提供的 `TIMESTAMP_LTZ` 列类型就专门用来存储带有时区信息的时间戳[^3]。它允许应用程序以统一的方式记录绝对时刻点而无需担心具体地域差异带来的干扰。 当查询这样的列时,DBMS 自动依据客户端设定好的偏好来呈现最终结果形式;这意味着即便原始数据是以格林尼治时间为基准储存下来的,在提取出来之后仍然可以根据目标受众所在的区域自动完成相应的偏移修正工作。 --- #### JavaScript 下的解决方案 —— Moment-Timezone 如果是在前端领域遇到类似的挑战,则可以借助第三方类库如 *Moment.js* 及扩展插件 *moment-timezone* 实现相似的功能效果[^4]。这里给出一段简单的示范代码说明基本用法: ```javascript const moment = require('moment-timezone'); let shanghaiTime = moment.tz("2023-10-07 12:00", "Asia/Shanghai").format(); console.log(shanghaiTime); let newYorkTime = moment.tz("2023-10-07 12:00", "America/New_York").format(); console.log(newYorkTime); ``` 上述脚本分别演示了针对两个不同城市定义下的日期格式化过程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值