首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >时区结果与systimestamp不同。

时区结果与systimestamp不同。
EN

Stack Overflow用户
提问于 2017-06-14 14:25:13
回答 3查看 1.3K关注 0票数 2

我有一个Oracle表,它包含以下列定义:

代码语言:javascript
复制
    COLUMN_NAME : RESERVATIONDATE
    DATA_TYPE   : TIMESTAMP(6)
    NULLABLE    : Yes
    DATA_DEFAULT: null

然后,在Java内部执行以下命令:

代码语言:javascript
复制
    insert into my_table (col1, col2, reservationdate) values (:np1, :np2, systimestamp)

然后在其他地方执行以下命令,目的是返回不到X秒前添加的行。

代码语言:javascript
复制
    select * from my_table where reservationDate >= systimestamp - NUMTODSINTERVAL( :seconds, 'SECOND' )

但是,尽管预订日期晚于所标识的点,但不返回任何行。

因此,我还从同一个应用程序中运行了以下命令:

代码语言:javascript
复制
    select col1,
           col2,
           reservationdate,
           systimestamp as b,
           systimestamp - NUMTODSINTERVAL( 5, 'SECOND' ) as c
    from my_table

它提供了以下输出:

代码语言:javascript
复制
    col1: value1
    col2: value2
    reservationdate:2017-06-14 14:31:00.746173
    b              :2017-06-14 15:31:00.905617
    c              :2017-06-14 15:30:55.905617

请注意,bc返回的值基本上比reservationdate提前一个小时。

在运行在同一台机器上的SQL上运行的请求与Java应用程序提供的值相同:

代码语言:javascript
复制
    reservationdate:14-JUN-17 02.31.00.746173000 PM
    b              :14-JUN-17 02.58.32.863300000 PM +00:00
    c              :14-JUN-17 02.58.27.863300000 PM +00:00

Oracle正在一个虚拟机上运行,其中Unix date的输出是:

代码语言:javascript
复制
    Wed Jun 14 14:18:11 UTC 2017

在Java运行的机器上:

代码语言:javascript
复制
    Wed Jun 14 15:21:24 BST 2017

显然,这是一个时区问题,但我不知道它是从哪里来的。毕竟,我一直在使用systimestamp,目的是在数据库服务器上执行所有时间戳计算。

我的请求通过Spring的NamedParameterJdbcTemplate传递,我使用的是OracleDatabase11gExpress Edition 11.2.0.2.0

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2017-06-14 14:46:53

试一试TIMESTAMP WITH TIMEZONETIMESTAMP WITH LOCAL TIMEZONE的简单TIMEZONE.

票数 3
EN

Stack Overflow用户

发布于 2017-06-14 14:55:26

这不是一个完全有条件的答案,但以下是一些信息:

数据类型TIMESTAMP不存储任何时区信息。当您插入时区时间戳(例如SYSTIMESTAMP)时,时区信息将被切断。

SYSTIMESTAMP数据库服务器的操作系统时区(在您的示例中为UTC)的时区返回TIMESTAMP WITH TIME ZONE数据类型

每当您将TIMESTAMP (没有时区)转换/强制转换为TIMESTAMP WITH TIME ZONE时,Oracle就会考虑SESSIONTIMEZONE --除非您使用TZ或类似的方法显式地设置时区。

票数 4
EN

Stack Overflow用户

发布于 2017-06-14 16:19:48

我认为您看到了两件事;当您进行调试查询时,您正在拖动、格式化和显示bc值,这会导致Java转换时区。但我认为这是一条红鲱鱼,因为这不会直接影响您的原始查询来查找最近更改的行。不过,它可能与Java语言环境有关,因此与主要的查询问题有关。

如果您跟踪最初运行的查询,您应该看到它实际使用的筛选器是:

代码语言:javascript
复制
SYS_EXTRACT_UTC(INTERNAL_FUNCTION(RESERVATIONDATE))
  >=SYS_EXTRACT_UTC(SYSTIMESTAMP(6)-NUMTODSINTERVAL(TO_NUMBER(:SECONDS),'SECOND'))

函数调用的存在是因为“在SELECT FROM操作期间,Oracle将数据从列转换为目标变量的类型。”;因此,列值被转换为带有时区的时间戳,以便与systimestamp进行比较,然后将两个时间戳与区域值进行比较,它们都被转换为UTC。

然后,这可以追溯到@WernfriedDomscheit所说的话。函数以一个datetime_with_timezone值作为其参数,因此当传入一个普通的timestamp时,它将使用SESSIONTIMEZONE隐式转换。您看到的查询结果不同,因为您的Java代码和之间的会话时区不同。

在这两种情况下,您都可以使用select sessiontimezone from dual来查看它们是什么,或者查看时间戳是如何转换的。

您可以通过将系统时间转换回无时区时间戳来避免此问题:

代码语言:javascript
复制
... where reservationDate >= cast(systimestamp as timestamp) - NUMTODSINTERVAL( :seconds, 'SECOND' );

跟踪显示了更简单的:

代码语言:javascript
复制
RESERVATIONDATE
  >=CAST(SYSTIMESTAMP(6) AS timestamp)-NUMTODSINTERVAL(TO_NUMBER(:SECONDS),'SECOND') 

将您的列定义从timestamp更改为timestamp with [local] time zone也是有效的;根据我的跟踪,对于local,向UTC的转换仍然会发生,如果没有本地的话,它就不会发生。这也是一本有趣的读物

如果您查看如何使用不同的会话时区解析sys_extract_utc()调用,您可以看到差异,使用新插入的行并省略date元素以保持简洁性:

代码语言:javascript
复制
insert into my_table (col1, col2, reservationdate) values (:np1, :np2, systimestamp);

alter session set nls_timestamp_format = 'HH24:MI:SS.FF1';
alter session set nls_timestamp_tz_format = 'HH24:MI:SS.FF1 TZR';

alter session set time_zone = 'Europe/London';

select reservationdate a,
  systimestamp b,
  systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND') c,
  cast(reservationdate as timestamp with time zone) d,
  sys_extract_utc(reservationdate) e,
  sys_extract_utc(systimestamp) f,
  sys_extract_utc(systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND')) g
from my_table;

A           B                 C                 D                        E           F           G          
----------- ----------------- ----------------- ------------------------ ----------- ----------- -----------
17:12:13.9  17:12:15.0 +01:00 17:12:10.0 +01:00 17:12:13.9 EUROPE/LONDON 16:12:13.9  16:12:15.0  16:12:10.0 

alter session set time_zone = 'UTC';

select reservationdate a,
  systimestamp(6) b,
  systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND') c,
  cast(reservationdate as timestamp with time zone) d,
  sys_extract_utc(reservationdate) e,
  sys_extract_utc(systimestamp) f,
  sys_extract_utc(systimestamp(6)-numtodsinterval(to_number(:seconds),'SECOND')) g
from my_table;

A           B                 C                 D              E           F           G          
----------- ----------------- ----------------- -------------- ----------- ----------- -----------
17:12:13.9  17:12:15.4 +01:00 17:12:10.4 +01:00 17:12:13.9 UTC 17:12:13.9  16:12:15.4  16:12:10.4 

我的设置似乎与您的不同--我的数据库服务器在英国时间,但是DBTIMEZONE是+00:00 --尽管如此,注意e列中这两个值之间的差异。在我的例子中,它看起来会发现太多的数据,而不是太少--因为e >= g是真的。在您的例子中,我相信如果您从SQL Developer和Java运行这些代码,您会看到这种差异是相反的,因为您的Java应用程序的会话时区(基于它的区域设置)正以相反的方式导致这种变化。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44547543

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档