PL/SQL 带日期参数的函数 returns 错误输出
PL/SQL function with date parameter returns wrong output
为了使我的主要代码更具可读性,我在函数中包装了一个将日期转换为纪元格式的操作。但是,函数 returns 的值不正确。我做错了什么?
我得到的
函数
create or replace function date_to_epoch(
date_in in date)
return number
is
epoch_out number;
begin
epoch_out := round((to_date(date_in, 'yyyy-mm-dd hh24:mi:ss') - to_date('1970-01-01', 'yyyy-mm-dd'))*24*60*60);
return epoch_out;
end;
我用函数调用:
declare
date_out number;
begin
select date_to_epoch(to_date('2020-01-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')) into date_out from dual;
DBMS_OUTPUT.PUT_LINE('Output:' || date_out);
end;
returns:-62134128000
我期待的
当我在一个简单的 SQL 语句中 运行 相同的操作时,我得到了预期的输出:
select round((to_date('2020-01-01', 'yyyy-mm-dd hh24:mi:ss') - to_date('1970-01-01', 'yyyy-mm-dd'))*24*60*60) from dual;
returns: 1577836800
您的输入参数已经是日期,因此您不应在其上使用 to_date()
。当你这样做时,坏事就会发生。
我会把你的代码写成:
create or replace function date_to_epoch(date_in in date)
return number
is
epoch_out number;
begin
epoch_out := round((date_in - date '1970-01-01')*24*60*60);
return epoch_out;
end;
请注意,您实际上不需要中间变量来存储函数的 return 值。你可以直接做:
create or replace function date_to_epoch(date_in in date)
return number
is
begin
return round((date_in - date '1970-01-01')*24*60*60);
end;
在此demo on DB Fiddle中,代码产生:
Output:1577836800
What am I doing wrong?
TO_DATE
function 有签名:
TO_DATE( date_string, format_model, nls_param )
其中所有参数都应具有字符串数据类型。
您没有将字符串作为第一个参数传递;您传递的是 DATE
数据类型。 Oracle 将尝试提供帮助,并通过使用当前会话的 NLS 日期格式会话参数隐式调用 TO_CHAR
将您传递的 DATE
转换为字符串。
所以,你的函数是有效的:
create or replace function date_to_epoch(
date_in in date
) return number
is
epoch_out number;
begin
epoch_out := round(
( to_date(
TO_CHAR(
date_in,
( SELECT value
FROM NLS_SESSION_PARAMETERS
WHERE parameter = 'NLS_DATE_FORMAT' )
),
'yyyy-mm-dd hh24:mi:ss'
)
-
to_date(
'1970-01-01',
'yyyy-mm-dd'
)
)*24*60*60
);
return epoch_out;
end;
要修复它,您只需将日期输入用作日期,而不是尝试将已经是日期的内容转换为日期:
CREATE FUNCTION date_to_epoch(
date_in IN DATE
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ROUND( ( date_in - DATE '1970-01-01' )*24*60*60 );
END date_to_epoch;
但是,您还需要确保您的日期在 UTC 时区内,如果它们不在计算纪元之前将它们转换为 UTC:
CREATE FUNCTION date_to_epoch(
date_in IN DATE,
timezone IN VARCHAR2 DEFAULT 'UTC'
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ROUND(
( CAST(
FROM_TZ( date_in, timezone ) -- Convert to a timestamp in the correct TZ
AT TIME ZONE 'UTC' -- Change to the UTC TZ
AS DATE -- Cast back to a date
)
- DATE '1970-01-01'
)*24*60*60
);
END date_to_epoch;
/
所以:
SELECT date_to_epoch( DATE '1970-01-01' ) AS utc_epoch,
date_to_epoch( DATE '1970-01-01', 'PST' ) AS pst_epoch
FROM DUAL;
输出:
UTC_EPOCH | PST_EPOCH
--------: | --------:
0 | 28800
db<>fiddle here
为了使我的主要代码更具可读性,我在函数中包装了一个将日期转换为纪元格式的操作。但是,函数 returns 的值不正确。我做错了什么?
我得到的
函数
create or replace function date_to_epoch(
date_in in date)
return number
is
epoch_out number;
begin
epoch_out := round((to_date(date_in, 'yyyy-mm-dd hh24:mi:ss') - to_date('1970-01-01', 'yyyy-mm-dd'))*24*60*60);
return epoch_out;
end;
我用函数调用:
declare
date_out number;
begin
select date_to_epoch(to_date('2020-01-01 00:00:00', 'yyyy-mm-dd hh24:mi:ss')) into date_out from dual;
DBMS_OUTPUT.PUT_LINE('Output:' || date_out);
end;
returns:-62134128000
我期待的
当我在一个简单的 SQL 语句中 运行 相同的操作时,我得到了预期的输出:
select round((to_date('2020-01-01', 'yyyy-mm-dd hh24:mi:ss') - to_date('1970-01-01', 'yyyy-mm-dd'))*24*60*60) from dual;
returns: 1577836800
您的输入参数已经是日期,因此您不应在其上使用 to_date()
。当你这样做时,坏事就会发生。
我会把你的代码写成:
create or replace function date_to_epoch(date_in in date)
return number
is
epoch_out number;
begin
epoch_out := round((date_in - date '1970-01-01')*24*60*60);
return epoch_out;
end;
请注意,您实际上不需要中间变量来存储函数的 return 值。你可以直接做:
create or replace function date_to_epoch(date_in in date)
return number
is
begin
return round((date_in - date '1970-01-01')*24*60*60);
end;
在此demo on DB Fiddle中,代码产生:
Output:1577836800
What am I doing wrong?
TO_DATE
function 有签名:
TO_DATE( date_string, format_model, nls_param )
其中所有参数都应具有字符串数据类型。
您没有将字符串作为第一个参数传递;您传递的是 DATE
数据类型。 Oracle 将尝试提供帮助,并通过使用当前会话的 NLS 日期格式会话参数隐式调用 TO_CHAR
将您传递的 DATE
转换为字符串。
所以,你的函数是有效的:
create or replace function date_to_epoch(
date_in in date
) return number
is
epoch_out number;
begin
epoch_out := round(
( to_date(
TO_CHAR(
date_in,
( SELECT value
FROM NLS_SESSION_PARAMETERS
WHERE parameter = 'NLS_DATE_FORMAT' )
),
'yyyy-mm-dd hh24:mi:ss'
)
-
to_date(
'1970-01-01',
'yyyy-mm-dd'
)
)*24*60*60
);
return epoch_out;
end;
要修复它,您只需将日期输入用作日期,而不是尝试将已经是日期的内容转换为日期:
CREATE FUNCTION date_to_epoch(
date_in IN DATE
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ROUND( ( date_in - DATE '1970-01-01' )*24*60*60 );
END date_to_epoch;
但是,您还需要确保您的日期在 UTC 时区内,如果它们不在计算纪元之前将它们转换为 UTC:
CREATE FUNCTION date_to_epoch(
date_in IN DATE,
timezone IN VARCHAR2 DEFAULT 'UTC'
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ROUND(
( CAST(
FROM_TZ( date_in, timezone ) -- Convert to a timestamp in the correct TZ
AT TIME ZONE 'UTC' -- Change to the UTC TZ
AS DATE -- Cast back to a date
)
- DATE '1970-01-01'
)*24*60*60
);
END date_to_epoch;
/
所以:
SELECT date_to_epoch( DATE '1970-01-01' ) AS utc_epoch,
date_to_epoch( DATE '1970-01-01', 'PST' ) AS pst_epoch
FROM DUAL;
输出:
UTC_EPOCH | PST_EPOCH --------: | --------: 0 | 28800
db<>fiddle here