问题描述
这是一个谜题。取以下查询并在oracle sqldeveloper中执行:
select to_char(CAST (sysdate AS TIMESTAMP WITH TIME ZONE),'TZH:TZM') dst,to_char(CAST (sysdate-160 AS TIMESTAMP WITH TIME ZONE),'TZH:TZM') nodst
from dual;
你会得到“-04:00”、“-05:00”的结果。这给出了正确的 dst 调整时区偏移量(东部),一个日期在 dst 中,另一个没有。从 sqlplus 运行相同的查询将两个值都设为“-4:00”。这会导致从 sqlplus 调用时也显示错误值的包出现问题。
解决方法
当您转换为带时区的时间戳时,Oracle 必须选择使用哪个时区;它使用当前会话时区。相当于做:
select to_char(FROM_TZ(CAST (sysdate AS TIMESTAMP),SESSIONTIMEZONE),'TZH:TZM') dst,to_char(FROM_TZ(CAST(sysdate-160 AS TIMESTAMP),'TZH:TZM') nodst
from dual;
当您在 SQL Developer 和 SQL*Plus 中得到不同的结果时,您在这两个客户端中的会话时区似乎不同。您可以通过在每个中查询 sessiontimezone
来检查。 SQL Developer 根据 Java 时区设置会话时区(默认情况下从操作系统获取;您可以通过 passing a user.timezone
value at start-up 覆盖它)。 SQL*Plus uses the ORA_SDTZ
environment variable,因此如果您不想使用 alter session
(手动或通过 login.sql
)从数据库中设置它,您可以将其设置为与您的语言环境相匹配;如果未设置,则默认为“OS_TZ”:
ORA_SDTZ
变量的默认值为 'OS_TZ'
,当变量未设置或设置为无效值时使用。
... 并将其作为偏移量 (-04:00) 而不是一个区域。
如果无论任何用户的会话设置如何,您始终希望结果位于特定时区,那么您可以说明要使用哪个时区,例如:
select to_char(FROM_TZ(CAST (sysdate AS TIMESTAMP),'America/New_York'),'TZH:TZM') nodst
from dual;
db<>fiddle 带有几个会话设置来演示。
大概您的真实查询是从可变日期值开始工作的,而不是 sysdate
;否则,您可以改用 systimestamp
,必要时使用 at time zone
调整到不同的区域。
这给出了正确的 dst 调整时区偏移量(东部),一个日期在 dst 中,另一个没有。
不,它会为您提供一个看似正确的手动调整值;但是,当 DST 结束时,您的查询将不正确,需要调整……然后明年春天需要再次调整……明年秋天。
如果您想要正确的值,那么让 Oracle 调整时区(并且您不需要将 SYSDATE
强制转换为 TIMESTAMP WITH TIME ZONE
,您可以只使用 SYSTIMESTAMP
或 {{ 1}}):
CURRENT_TIMESTAMP