在不同时区的查询条件下,Oracle的系统时间戳。

问题描述 投票:0回答:1

我有以下使用列的代码 lock_until TIMESTAMP(3). 该代码插入了 systimestamp + 5 minutes 然后选择所有带有 lock_until < systimestamp. 我希望在结果集中没有记录,因为在 lock_until 是在未来,但该行被返回。最可能的原因是我的本地时区是UTC+2,但我不明白这怎么可能。该代码在使用 current_timestamp 但我更希望代码是客户端时区中立的。

jdbcTemplate.update("delete from shedlock where name = 'test'");
System.out.println(jdbcTemplate.queryForList("SELECT SESSIONTIMEZONE FROM dual"));
// insert, lock_until is now + 5 minutes
jdbcTemplate.update("INSERT INTO shedlock(name, lock_until, locked_at, locked_by) VALUES('test', systimestamp + 5/(24 * 60), systimestamp, 'me')");
// select if lock_until <= systimestamp
System.out.println(jdbcTemplate.queryForList("select * from shedlock where lock_until <= systimestamp"));
// systimestamp ?
System.out.println(jdbcTemplate.queryForList("select systimestamp from dual"));

结果是

Local time: 17:32:48.872
[{SESSIONTIMEZONE=Europe/Prague}]
[{NAME=test, LOCK_UNTIL=2020-04-25 15:37:49.0, LOCKED_AT=2020-04-25 15:32:49.106, LOCKED_BY=me}]
[{SYSTIMESTAMP=2020-04-25 17:32:49.168954}]
oracle jdbc timestamp timezone
1个回答
1
投票

Oracle文件:

SYSTIMESTAMP 返回数据库所在系统的系统日期,包括小数点和时区。返回类型为 TIMESTAMP WITH TIME ZONE.

当你这样做的时候。

INSERT INTO shedlock (
  name,
  lock_until,
  locked_at,
  locked_by
) VALUES (
  'test',
  systimestamp + 5/(24 * 60),
  systimestamp,
  'me'
)

这就是隐性操作

INSERT INTO shedlock (
  name,
  lock_until,
  locked_at,
  locked_by
) VALUES (
  'test',
  CAST( systimestamp + 5/(24 * 60) AS TIMESTAMP(3) ),
  CAST( systimestamp AS TIMESTAMP(3) ),
  'me'
)

这将丢弃时区信息,并保持其他日期时间组件的原样。

然而,当你做 SELECT 它是隐式的。

select *
from   shedlock
where  FROM_TZ( lock_until, SESSIONTIMEZONE ) <= systimestamp

并将会话时区添加到存储值中,然后将其与数据库所在系统的时区进行比较;如果这两个时区不一样,那么你就会得到意外的包含排除的记录。

你可以做的是显式地将 SYSTIMESTAMPTIMESTAMP WITH TIME ZONE 数据类型到一个 TIMESTAMP 数据类型。

select *
from   shedlock
where  lock_until <= CAST( systimestamp AS TIMESTAMP(3) )

db<>fiddle

© www.soinside.com 2019 - 2024. All rights reserved.