如何在Oracle中将日期格式从'1965-08-01 00:00:00.0'更改为'Tue Aug 01 00:00:00 PDT 2000'

时间:2016-06-24 10:56:17

标签: sql oracle

select TO_TIMESTAMP_TZ('1965-08-01 00:00:00.0','Dy Mon DD HH:MM:ss TZD YYYY') from dual;

我在varchar列中的数据格式错误,如下所述。

1973-12-12 00:00:00.0
2003-05-14 00:00:00.0
1950-05-01 00:00:00.0
Fri Jul 01 00:00:00 PDT 1977
Sun Feb 01 00:00:00 PST 2015
Wed May 14 00:00:00 PDT 2003

我希望所有日期都采用与以下相同的格式,但无法转换。

Fri Jul 01 00:00:00 PDT 1977
Sun Feb 01 00:00:00 PST 2015
Wed May 14 00:00:00 PDT 2003

添加'Dy Mon DD HH:MM:ss TZD YYYY'格式时,由于TZD而导致异常无效日期格式。

任何人都可以帮我转换这个日期,并通过更新查询保持相同的格式。

1 个答案:

答案 0 :(得分:1)

将日期或时间戳存储为字符串并不是一个好主意。您应该使列成为它们将保存的数据的正确数据类型。除了允许Oracle更高效外,它还可以防止您存储无效数据。

但是,如果您只想将'1965-08-01 00:00:00.0'之类的字符串转换为'Tue Aug 01 00:00:00 PDT 2000',那么它们都是相同的字符串格式,您可以将这些值转换为to_timestamp()的时间戳,指定哪个时间它们用from_tz()表示的区域,然后以您想要的格式将它们转换回字符串。使用您的示例数据构建的演示表t

update t set str = to_char(from_tz(to_timestamp(str, 'YYYY-MM-DD HH24:MI:SS.FF'),
  'America/Los_Angeles'), 'Dy Mon DD HH24:MI:SS TZD YYYY')
where str not like '%PDT%' and str not like '%PST%';

3 rows updated.

select * from t;

STR                        
----------------------------
Wed Dec 12 00:00:00 PST 1973
Wed May 14 00:00:00 PDT 2003
Mon May 01 00:00:00 PDT 1950
Fri Jul 01 00:00:00 PDT 1977
Sun Feb 01 00:00:00 PST 2015
Wed May 14 00:00:00 PDT 2003

 6 rows selected 

您需要应用过滤器,以便忽略目标格式中已有的任何行,因为这些行会出错。在这里,我排除了任何包含PDT或PST的内容。如果您还有其他未显示的格式,则可以使用regexp_like()来查找与特定格式完全匹配的行。

更一般地说,要将任何字符串转换为实际时间戳,您可以创建一个尝试各种格式的函数,并通过捕获异常来返回成功转换的第一个函数。一种简单的蛮力方法可能是:

create or replace function my_to_timestamp_tz(p_str varchar2)
return timestamp with time zone as
begin
  -- try each expected pattern in turn

  begin
    return to_timestamp_tz(p_str, 'Dy Mon DD HH24:MI:SS TZD YYYY');
  exception
    when others then
      null;
  end;

  begin
    -- unspecified TZ; this assumes same as server
    return to_timestamp_tz(p_str, 'YYYY-MM-DD HH24:MI:SS.FF');
  exception
    when others then
      null;
  end;

  -- maybe throw an exception if no conversions worked
  return null;
end;
/

PDT / PST并不总是被识别;我有两个DB,我认为它们构建相同,但一个允许和其他抛出“ORA-01857:不是一个有效的时区”。如果您也看到它,您可以通过将TZD值视为固定字符串并指定它代表的区域来解决它:

  -- if your DB doesn't recognise PDT/PST then force them to a region:
  begin
    return from_tz(to_timestamp_tz(p_str, 'Dy Mon DD HH24:MI:ss "PDT" YYYY'),
      'America/Los_Angeles');
  exception
    when others then
      null;
  end;

  begin
    return from_tz(to_timestamp_tz(p_str, 'Dy Mon DD HH24:MI:ss "PST" YYYY'),
      'America/Los_Angeles');
  exception
    when others then
      null;
  end;

  -- other time zone abbreviations and matching regions if you expect any

使用该功能:

with t (str) as (
  select '1973-12-12 00:00:00.0' from dual
  union all select '2003-05-14 00:00:00.0' from dual
  union all select '1950-05-01 00:00:00.0' from dual
  union all select 'Fri Jul 01 00:00:00 PDT 1977' from dual
  union all select 'Sun Feb 01 00:00:00 PST 2015' from dual
  union all select 'Wed May 14 00:00:00 PDT 2003' from dual
)
select str, my_to_timestamp_tz(str) as converted
from t;

STR                          CONVERTED                                          
---------------------------- ----------------------------------------------------
1973-12-12 00:00:00.0        1973-12-12 00:00:00 Europe/London                   
2003-05-14 00:00:00.0        2003-05-14 00:00:00 Europe/London                   
1950-05-01 00:00:00.0        1950-05-01 00:00:00 Europe/London                   
Fri Jul 01 00:00:00 PDT 1977 1977-07-01 00:00:00 America/Los_Angeles             
Sun Feb 01 00:00:00 PST 2015 2015-02-01 00:00:00 America/Los_Angeles             
Wed May 14 00:00:00 PDT 2003 2003-05-14 00:00:00 America/Los_Angeles             

请注意,前三个假定一个时区,因为我在英国选择伦敦。如果这不能为您提供正确的结果,并且您知道它们总是代表特定的时区,您可以通过更改函数中的最后一个块来指定该区域:

  begin
    -- unspecified TZ; assume from a specific region
    return from_tz(to_timestamp(p_str, 'YYYY-MM-DD HH24:MI:SS.FF'),
      'America/Los_Angeles');
  exception
  ...

然后会得到:

STR                          CONVERTED                                          
---------------------------- ----------------------------------------------------
1973-12-12 00:00:00.0        1973-12-12 00:00:00 America/Los_Angeles             
2003-05-14 00:00:00.0        2003-05-14 00:00:00 America/Los_Angeles             
1950-05-01 00:00:00.0        1950-05-01 00:00:00 America/Los_Angeles             
Fri Jul 01 00:00:00 PDT 1977 1977-07-01 00:00:00 America/Los_Angeles             
Sun Feb 01 00:00:00 PST 2015 2015-02-01 00:00:00 America/Los_Angeles             
Wed May 14 00:00:00 PDT 2003 2003-05-14 00:00:00 America/Los_Angeles             

如果你真的想要,你可以将生成的时间戳转换回字符串,但我真的建议你将它们存储为正确的数据类型