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而导致异常无效日期格式。
任何人都可以帮我转换这个日期,并通过更新查询保持相同的格式。
答案 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
如果你真的想要,你可以将生成的时间戳转换回字符串,但我真的建议你将它们存储为正确的数据类型