2014-05-09 48 views
3

我有一个生产问题涉及Oracle 11g电子邮件发送日期。代码如下所示。Oracle电子邮件发送日期有时不正确

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, '[email protected]'); 

    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" <[email protected]>' || 
         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 : 

enter image description here

enter image description here

enter image description here

+0

一个是从您的数据库的日期 - 一个是从电子邮件服务器的日期... – Randy

回答

5

的数据格式在指定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值。根据RFC应该被解释为2014-09-05(9月5日)。您似乎有时会看到,但并非总是如此,这表明MTA正在以不同的方式处理这些问题。基于这三个例子,这可能取决于“错误的”日期是否有效。如果我发送邮件的日期是在Outlook的2014-09-05;但如果我将其更改为05-23-2014 17:43:28,那么它会显示正确的日期 - 大概是因为无效日期2014-23-05已被无声拒绝,并且MTA使用其当前日期。 (或者客户确实;事实上这可能更有可能)。

在第二个示例中,您似乎还有一个转折点,因为它显示的是06/10/2013而不是您可能期望形成的“正确”日期06/11/2013。我认为那是因为你也失去了时区偏移量;实际上你并没有设置一个,但是当你调整时你可能仍然会穿越日期边界。在我的BST时区

Date: Fri 09 May 2014 17:47:52 +01:00 

所以格式化你的日期作为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); 

其中给出。请注意,您必须使用systimestamp而不是sysdate来获取时区信息。并且必须使用英文,因此我已将可选的第三个参数添加到to_char()以确保无论会话区域设置如何。


由于@ShoeLace在评论品脱出来,上面还串不完全匹配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 

一些服务器可能接受没有逗号或冒号,但最好是正确的...

+0

谢谢Alex提供详细的解决方案! –

+0

@Matthew - 没问题;我刚刚添加了一项小改动,以确保它始终以英文发送,因为这可能会导致问题。 –

+0

完美!非常感谢!! –

1

只需添加一些e xtra详细信息来自@ alex-poole的答案。

的STMP日期格式第5条指明的http://www.ietf.org/rfc/rfc0822.txt

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; 

(FYI,ORA -01874:时区小时必须介于-12和14之间)。