Oracle 电子邮件发送日期有时不正确
Posted
技术标签:
【中文标题】Oracle 电子邮件发送日期有时不正确【英文标题】:Oracle email send date some times are not correct 【发布时间】:2014-05-09 15:32:39 【问题描述】:我有一个与 Oracle 11g 电子邮件发送日期相关的生产问题。代码如下。
procedure email(p_recip in apex_application_global.vc_arr2,
p_subject in varchar2,
p_message in varchar2) is
c utl_smtp.connection;
msg varchar2(4000);
username varchar2(100) := 'XXX';
password varchar2(100) := '123';
l_encoded_username varchar2(200);
l_encoded_password varchar2(200);
l_recips varchar2(2000);
procedure send_header(name in varchar2, header in varchar2) as
begin
utl_smtp.write_data(c, name || ': ' || header || utl_tcp.crlf);
end;
begin
--Open SMTP connection
l_encoded_username := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(username)));
l_encoded_password := utl_raw.cast_to_varchar2(utl_encode.base64_encode(utl_raw.cast_to_raw(password)));
c := utl_smtp.open_connection('AAA.BBB.local', '25');
utl_smtp.ehlo(c, 'AAA.BBB.local'); --DO NOT USE HELO
utl_smtp.command(c, 'AUTH', 'LOGIN');
utl_smtp.command(c, l_encoded_username);
utl_smtp.command(c, l_encoded_password);
utl_smtp.mail(c, 'XXX@YYY.on.ca');
if ((p_recip is null) or p_recip.count = 0) then
return;
end if;
for i in 1 .. p_recip.count loop
utl_smtp.rcpt(c, p_recip(i));
l_recips := l_recips || p_recip(i) || ','; --mark as Multiple receivers
end loop;
--now remove the trailing comma at the end of l_recips
l_recips := substr(l_recips, 0, length(l_recips) - 1);
utl_smtp.open_data(c);
--prepare mail header
utl_smtp.write_data(c, 'Date: ' ||
to_char(sysdate, 'MM-DD-YYYY HH24:MI:SS') ||
utl_tcp.crlf);
utl_smtp.write_data(c, 'To: ' || l_recips || utl_tcp.crlf);
utl_smtp.write_data(c, 'From: ' ||
'"Company" <MMM@KKK.on.ca>' ||
utl_tcp.crlf);
utl_smtp.write_data(c, 'Subject: ' || p_subject || utl_tcp.crlf ||
utl_tcp.crlf);
--include the message body
utl_smtp.write_data(c, msg);
-- Write message body
utl_smtp.write_data(c, p_message || utl_tcp.crlf);
-- Clean up
utl_smtp.close_data(c);
utl_smtp.quit(c);
exception
when utl_smtp.transient_error or utl_smtp.permanent_error then
begin
utl_smtp.quit(c);
exception
when utl_smtp.transient_error or utl_smtp.permanent_error then
null;
-- When the SMTP server is down or unavailable, we don't have
-- a connection to the server. The QUIT call will raise an
-- exception that we can ignore.
end;
raise_application_error(-20000, 'Failed to send mail due to the following error: ' ||
sqlerrm);
end;
------------------------------------------
Unfortunately, the date on received email "Sent:" some times are wrong.
For example :
【问题讨论】:
一个是您数据库中的日期 - 一个是来自电子邮件服务器的日期... 【参考方案1】:RFC 822 中指定了数据格式(RFC1123 中添加了四位数年份)。您正在以不同的格式发送日期。看起来这有时被接受,有时不被接受;并给出“错误”的结果。文件夹视图中的日期是接收日期,而不是发送日期,因此它实际上没有任何影响 - 只是它与邮件正文中的日期一致,作为有用的交叉引用。
你正在这样做:
utl_smtp.write_data(c, 'Date: ' ||
to_char(sysdate, 'MM-DD-YYYY HH24:MI:SS') ||
utl_tcp.crlf);
所以现在我会得到Date: 05-09-2014 17:43:28
的值。根据应该被解释为2014-09-05
(9 月 5 日)的 RFC。您似乎有时会看到这一点,但并非总是如此,这表明 MTA 的处理方式有所不同。基于这三个示例,这可能取决于“错误”日期是否有效。如果我发送带有该日期的邮件,Outlook 中确实会出现2014-09-05
;但是,如果我将其更改为 05-23-2014 17:43:28
,那么它会出现正确的日期 - 大概是因为无效日期 2014-23-05
被默默拒绝,而 MTA 使用其当前日期。 (或者客户会这样做;事实上这可能更有可能)。
您在第二个示例中似乎有进一步的转折,因为它显示了06/10/2013
,而不是您可能期望的“正确”日期06/11/2013
。我认为那是因为您也失去了时区偏移;事实上,您并没有设置一个,但是当它被调整时,您可能仍然会跨越一个日期边界。
因此,按照 RFC 的要求格式化您的日期:
utl_smtp.write_data(c, 'Date: ' ||
to_char(systimestamp, 'Dy DD Mon YYYY HH24:MI:SS TZH:TZM',
'NLS_DATE_LANGUAGE=ENGLISH') ||
utl_tcp.crlf);
这给出了:
Date: Fri 09 May 2014 17:47:52 +01:00
在我的 BST 时区。请注意,您必须使用systimestamp
而不是sysdate
来获取时区信息。而且它必须是英文的,所以我在to_char()
中添加了可选的第三个参数,以确保无论会话区域设置如何。
@ShoeLace 在 cmets 中指出,上面的字符串仍然与 RFC 822 不完全匹配,它可能应该是:
utl_smtp.write_data(c, 'Date: ' ||
to_char(systimestamp, 'Dy, DD Mon YYYY HH24:MI:SS TZHTZM',
'NLS_DATE_LANGUAGE=ENGLISH') ||
utl_tcp.crlf);
现在给出:
Date: Tue, 04 Aug 2015 10:09:31 +0100
有些服务器可以接受不带逗号或冒号的,但最好是正确的...
【讨论】:
感谢Alex提供详细的解决方案! @Matthew - 没问题;我刚刚添加了一个小改动,以确保它始终以英文发送,因为这也可能导致问题。 rfc0822 说 ' ( ("+" / "-") 4DIGIT ) ;局部微分;小时+分钟。 (HHMM)' 所以我认为时区偏移中不应该有冒号.. ? 并指定日期名称后应该有一个逗号。所以可能更像TO_CHAR(systimestamp, 'Dy "," DD Mon YYYY HH24:MI:SS TZHTZM','NLS_DATE_LANGUAGE=ENGLISH')
@ShoeLace - 优点;现在不确定我当时是否真的测试过这个;如果我做了一些 SMTP 服务器可能比其他服务器更宽容。谢谢。【参考方案2】:
只是为@alex-poole 的答案添加一些额外的细节。
STMP 日期格式在http://www.ietf.org/rfc/rfc0822.txt 的第 5 节中指定:
5.1. SYNTAX
date-time = [ day "," ] date time ; dd mm yy
; hh:mm:ss zzz
day = "Mon" / "Tue" / "Wed" / "Thu"
/ "Fri" / "Sat" / "Sun"
date = 1*2DIGIT month 2DIGIT ; day month year
; e.g. 20 Jun 82
month = "Jan" / "Feb" / "Mar" / "Apr"
/ "May" / "Jun" / "Jul" / "Aug"
/ "Sep" / "Oct" / "Nov" / "Dec"
time = hour zone ; ANSI and Military
hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT]
; 00:00:00 - 23:59:59
zone = "UT" / "GMT" ; Universal Time
; North American : UT
/ "EST" / "EDT" ; Eastern: - 5/ - 4
/ "CST" / "CDT" ; Central: - 6/ - 5
/ "MST" / "MDT" ; Mountain: - 7/ - 6
/ "PST" / "PDT" ; Pacific: - 8/ - 7
/ 1ALPHA ; Military: Z = UT;
; A:-1; (J not used)
; M:-12; N:+1; Y:+12
/ ( ("+" / "-") 4DIGIT ) ; Local differential
; hours+min. (HHMM)
所以你需要正确格式化你的日期
utl_smtp.write_data(c, 'Date: ' ||
to_char(systimestamp, 'Dy "," DD Mon YYYY HH24:MI:SS TZHTZM',
'NLS_DATE_LANGUAGE=ENGLISH') ||
utl_tcp.crlf);
这是一个快速查询,以每小时偏移量显示当前系统时间:
with generater as
(select rownum-13 x from dual connect by level < 28 )
select x, TO_CHAR(systimestamp at time zone x||':00', 'Dy "," DD Mon YYYY HH24:MI:SS TZHTZM','NLS_DATE_LANGUAGE=ENGLISH')
from generater;
(仅供参考,ORA-01874:时区小时必须介于 -12 和 14 之间)。
【讨论】:
以上是关于Oracle 电子邮件发送日期有时不正确的主要内容,如果未能解决你的问题,请参考以下文章