Oracle DST时间转换错误ORA-01878

时间:2018-12-07 07:47:28

标签: sql oracle oracle-sqldeveloper

我有一条select语句,我正在其中转换时区

Select
from_tz(cast(DATE_TIME as timestamp), 'US/Eastern') at time zone 'UTC' DATE_TIME_UTC
From Table1

但是对于某些行,由于DST而出现错误

ORA-01878: specified field not found in datetime or interval

我想写一个类似的查询

select 
if error then do something else do the time conversion from table1

3 个答案:

答案 0 :(得分:2)

当您使用12c时,可以使用增强的子查询因子来定义本地函数;可以尝试使用美国/东部进行转换,如果失败则回落到-4:00。

使用示例数据和几行额外的行将继续转换:

alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS TZR TZD';

with
  function get_tstz(p_date in date) return timestamp with time zone is
    dst_exception exception;
    pragma exception_init(dst_exception, -1878);
  begin
    return from_tz(cast(p_date as timestamp), 'US/Eastern');
    exception
      when dst_exception then
        return from_tz(cast(p_date as timestamp), '-04:00');
  end get_tstz;
select date_time,
  get_tstz(date_time) as date_time_converted,
  get_tstz(date_time) at time zone 'UTC' as date_time_utc
from table1
/

DATE_TIME           DATE_TIME_CONVERTED                DATE_TIME_UTC              
------------------- ---------------------------------- ---------------------------
2018-03-11 01:59:00 2018-03-11 01:59:00 US/EASTERN EST 2018-03-11 06:59:00 UTC UTC
2018-03-11 02:06:00 2018-03-11 02:06:00 -04:00 -04:00  2018-03-11 06:06:00 UTC UTC
2018-03-11 02:08:00 2018-03-11 02:08:00 -04:00 -04:00  2018-03-11 06:08:00 UTC UTC
2018-03-11 02:21:00 2018-03-11 02:21:00 -04:00 -04:00  2018-03-11 06:21:00 UTC UTC
2018-03-11 02:48:00 2018-03-11 02:48:00 -04:00 -04:00  2018-03-11 06:48:00 UTC UTC
2018-03-11 02:06:00 2018-03-11 02:06:00 -04:00 -04:00  2018-03-11 06:06:00 UTC UTC
2018-03-11 02:33:00 2018-03-11 02:33:00 -04:00 -04:00  2018-03-11 06:33:00 UTC UTC
2018-03-11 03:00:00 2018-03-11 03:00:00 US/EASTERN EDT 2018-03-11 07:00:00 UTC UTC

我已经调整了我的NLS设置,以便您可以看到转换后的值的差异,例如EST,EDT或固定的-4:00。


正如评论中提到的那样,您将忽略潜在的数据问题,而更正更正已知错误的数据会更好-假设您可以确定为什么它是错误的,从而可以安全地进行修复;或确认您断言原始数据都应该是美国/东部。

从根本上讲,由于某些人显然并不是真正的美国/东部人,因此信任任何数据似乎都不安全。如果不知道这些特定记录如何以及为什么具有您不期望的值,则无法确定其他任何值也是您期望的。无论插入那些日期的任何应用程序,工具或过程可能已经(并且可能确实)插入了其他时间,这些时间看起来不错,但实际上也不是美国/东部时间。其余的都可以无误地进行转换,但这并不意味着UTC时间必须具有代表性。

您还有一个次要问题,因为您不知道记录为2017-11-05 01:00:00的日期最初是美国东部时间01:00还是美国东部时间01:00,因为该时间重复了夏季结束时。 Oracle只会为您选择。

答案 1 :(得分:1)

您可以创建一个自定义函数,并检查其是否带有时区的有效时间戳,并在查询的where子句中使用该函数,例如,如下所示。

create table t(x varchar(100));

insert into t 
  select '21-FEB-2009 18:00:00'
    from dual
  union all  
  select '31-FEB-2009 18:00:00' /*Junk date here..*/
    from dual;

create or replace function fn_test(dt in varchar2)
return int
as
l_timestamp timestamp with time zone;
begin
   l_timestamp :=from_tz(to_timestamp(dt,'DD-MON-YYYY hh24:mi:ss'), 'US/Eastern') at time zone 'UTC';
return 1;   
exception
 when others then    
    return null;
end;
/

select from_tz(to_timestamp(x,'DD-MON-YYYY hh24:mi:ss'),'US/Eastern') at time zone 'UTC'
  from t
where fn_test(x) is not null

答案 2 :(得分:0)

我收到此错误消息的原因有两个:

最有可能的是:您正在尝试转换由于夏令时切换的原因不存在的本地时间,例如“28-MAR-21 02:34”在德国(柏林/欧洲时区)不存在,因为时钟在夜间从凌晨 1:59 跳到凌晨 3:00。解决方法:加一小时,UTC 就会反映正确的时间。

不太可能:时区字符串中的拼写错误:“Europe/Berlin”或“Berlin/Europe” - 两者都是错误的,正确的是“Europe/Berlin”(模式为大陆/城市)