You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP实现Outlook日历添加事件失败技术求助

PHP生成Outlook日历邀请失败问题排查

近2-3天尝试用PHP代码向Outlook日历添加事件,始终失败。收到的邮件格式异常,直接显示HTML代码和iCal内容,无法被识别为日历邀请。以下是相关代码及收到的邮件内容,需排查问题:

我的PHP代码

$domain = 'dopasi.org';
$from_name = 'FromName'; // From       
$from_address = 'fromEmail@gmail.com'; // From Email 
$to_name = 'ToName';        
$to_address = 'toEmail@dopasi.org';
$startTime = '13/11/2023' . ' ' . '11:00';  
$endTime = '13/11/2023' . ' ' . '12:00';  
$subject = 'Meeting Title';   
$description = 'Meeting Description';    
$location = "TestCity";
        
//Create Email Headers
$mime_boundary = "----Meeting Booking----".MD5(TIME());
        
$headers = "From: ".$from_name." <".$from_address.">\n";
$headers .= "Reply-To: ".$from_name." <".$from_address.">\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: multipart/alternative; boundary=\"$mime_boundary\"\n";
$headers .= "Content-class: urn:content-classes:calendarmessage\n";
        
//Create Email Body (HTML)
$message = "--$mime_boundary\r\n";
$message .= "Content-Type: text/html; charset=UTF-8\n";
$message .= "Content-Transfer-Encoding: 8bit\n\n";
$message .= "<html>\n";
$message .= "<body>\n";
$message .= '<p>Dear '.$to_name.',</p>';
$message .= '<p>'.$description.'</p>';
$message .= "</body>\n";
$message .= "</html>\n";
$message .= "--$mime_boundary\r\n";
        
$ical = 'BEGIN:VCALENDAR' . "\r\n" .
        'PRODID:-//Microsoft Corporation//Outlook 10.0 MIMEDIR//EN' . "\r\n" .
        'VERSION:2.0' . "\r\n" .
        'METHOD:REQUEST' . "\r\n" .
        'BEGIN:VTIMEZONE' . "\r\n" .
        'TZID:Asia/Karachi' . "\r\n" .
        'BEGIN:STANDARD' . "\r\n" .
        'DTSTART:20231114T020000' . "\r\n" .
        'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11' . "\r\n" .
        'TZOFFSETFROM:-0400' . "\r\n" .
        'TZOFFSETTO:-0500' . "\r\n" .
        'TZNAME:EST' . "\r\n" .
        'END:STANDARD' . "\r\n" .
        'BEGIN:DAYLIGHT' . "\r\n" .
        'DTSTART:20231114T020000' . "\r\n" .
        'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3' . "\r\n" .
        'TZOFFSETFROM:-0500' . "\r\n" .
        'TZOFFSETTO:-0400' . "\r\n" .
        'TZNAME:EDST' . "\r\n" .
        'END:DAYLIGHT' . "\r\n" .
        'END:VTIMEZONE' . "\r\n" .  
        'BEGIN:VEVENT' . "\r\n" .
        'ORGANIZER;CN="'.$from_name.'":MAILTO:'.$from_address. "\r\n" .
        'ATTENDEE;CN="'.$to_name.'";ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:'.$to_address. "\r\n" .
        'LAST-MODIFIED:' . date("Ymd\TGis") . "\r\n" .
        'UID:'.date("Ymd\TGis", strtotime($startTime)).rand()."@".$domain."\r\n" .
        'DTSTAMP:'.date("Ymd\TGis") . "\r\n" .
        'DTSTART;TZID="Asia/Karachi":'.date("Ymd\THis", strtotime($startTime)) . "\r\n" .
        'DTEND;TZID="Asia/Karachi":'.date("Ymd\THis", strtotime($endTime)) . "\r\n" .
        'TRANSP:OPAQUE'. "\r\n" .
        'SEQUENCE:1'. "\r\n" .
        'SUMMARY:' . $subject . "\r\n" .
        'LOCATION:' . $location . "\r\n" .
        'CLASS:PUBLIC'. "\r\n" .
        'PRIORITY:5'. "\r\n" .
        'BEGIN:VALARM' . "\r\n" .
        'TRIGGER:-PT15M' . "\r\n" .
        'ACTION:DISPLAY' . "\r\n" .
        'DESCRIPTION:Reminder' . "\r\n" .
        'END:VALARM' . "\r\n" .
        'END:VEVENT'. "\r\n" .
        'END:VCALENDAR'. "\r\n";
$message .= 'Content-Type: text/calendar;name="meeting.ics";method=REQUEST'."\n";
$message .= "Content-Transfer-Encoding: 8bit\n\n";
$message .= $ical;
        
$mailsent = mail($to_address, $subject, $message, $headers);
echo ($mailsent) ? 'Success' : 'Failed';

收到的邮件内容

------Meeting Booking----fcf0f13b816a3795030bbfc724382b1d
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: 8bit
<html>
<body>
<p>Dear ToName,</p><p>Meeting Description</p></body> </html> ------Meeting Booking----fcf0f13b816a3795030bbfc724382b1d
Content-Type: text/calendar; name="meeting.ics";method=REQUEST
Content-Transfer-Encoding: 8bit

BEGIN:VCALENDAR
PRODID:-//Microsoft Corporation//Outlook 10.0 MIMEDIR//EN
VERSION:2.0
METHOD:REQUEST
BEGIN:VTIMEZONE
TZID: Asia/Karachi
BEGIN:STANDARD
DTSTART:20231114T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=1SU;BYMONTH=11
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
TZNAME:EST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20231114T020000
RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SU;BYMONTH=3
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
TZNAME:EDST
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
ORGANIZER;CN="fromName":MAILTO:fromEmail@gmail.com
ATTENDEE;CN="toName";ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:toEmail#gmail.com
LAST-MODIFIED:20231114T112000
UID:19700101T500002143262541@mydomain.com
DTSTAMP:20231114T112000
DTSTART;TZID="Asia/Karachi":19700101T050000
DTEND;TZID="Asia/Karachi":19700101T050000
TRANSP:OPAQUE
SEQUENCE:1
SUMMARY: Meeting Title
LOCATION: TestCity
CLASS:PUBLIC
PRIORITY:5
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION: Reminder
END:VALARM
END:VEVENT
END:VCALENDAR

问题排查与修复方案

核心问题点

  1. MIME类型错误:使用multipart/alternative类型,日历邀请应使用multipart/mixed——前者用于提供内容的不同表现形式(如HTML和纯文本),后者用于包含附件类独立内容。
  2. 日期解析失败:startTime采用日/月/年格式,strtotime无法正确识别,导致事件时间被解析为Unix纪元起始时间(1970-01-01)。
  3. MIME边界格式错误:
    • 部分换行符混用\n和\r\n,导致邮件结构解析异常
    • 邮件末尾未添加闭合边界(--$mime_boundary--)
  4. 时区配置错误:
    • VTIMEZONE中DAYLIGHT和STANDARD的DTSTART重复,且时区名称(EST/EDST)与Asia/Karachi不匹配
    • Asia/Karachi时区无夏令时,无需配置DAYLIGHT部分

修复后的代码

$domain = 'dopasi.org';
$from_name = 'FromName';
$from_address = 'fromEmail@gmail.com';
$to_name = 'ToName';
$to_address = 'toEmail@dopasi.org';
// 使用年/月/日格式确保strtotime正确解析
$startTime = '2023-11-13 11:00';  
$endTime = '2023-11-13 12:00';  
$subject = 'Meeting Title';
$description = 'Meeting Description';
$location = "TestCity";

$mime_boundary = "----Meeting Booking----" . MD5(TIME());

// 更换为multipart/mixed类型
$headers = "From: $from_name <$from_address>\r\n";
$headers .= "Reply-To: $from_name <$from_address>\r\n";
$headers .= "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/mixed; boundary=\"$mime_boundary\"\r\n";
$headers .= "Content-class: urn:content-classes:calendarmessage\r\n";

$message = "--$mime_boundary\r\n";
$message .= "Content-Type: text/html; charset=UTF-8\r\n";
$message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
$message .= "<html>\r\n<body>\r\n";
$message .= "<p>Dear $to_name,</p>\r\n";
$message .= "<p>$description</p>\r\n";
$message .= "</body>\r\n</html>\r\n";

$message .= "--$mime_boundary\r\n";
$message .= "Content-Type: text/calendar; name=\"meeting.ics\"; method=REQUEST\r\n";
$message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";

// Asia/Karachi无夏令时,简化时区配置
$ical = "BEGIN:VCALENDAR\r\n";
$ical .= "PRODID:-//Microsoft Corporation//Outlook 10.0 MIMEDIR//EN\r\n";
$ical .= "VERSION:2.0\r\n";
$ical .= "METHOD:REQUEST\r\n";
$ical .= "BEGIN:VTIMEZONE\r\n";
$ical .= "TZID:Asia/Karachi\r\n";
$ical .= "BEGIN:STANDARD\r\n";
$ical .= "DTSTART:19700101T000000\r\n";
$ical .= "TZOFFSETFROM:+0500\r\n";
$ical .= "TZOFFSETTO:+0500\r\n";
$ical .= "TZNAME:PKT\r\n";
$ical .= "END:STANDARD\r\n";
$ical .= "END:VTIMEZONE\r\n";
$ical .= "BEGIN:VEVENT\r\n";
$ical .= "ORGANIZER;CN=\"$from_name\":MAILTO:$from_address\r\n";
$ical .= "ATTENDEE;CN=\"$to_name\";ROLE=REQ-PARTICIPANT;RSVP=TRUE:MAILTO:$to_address\r\n";
$ical .= "LAST-MODIFIED:" . date("Ymd\TGis") . "\r\n";
$ical .= "UID:" . date("Ymd\TGis", strtotime($startTime)) . rand() . "@$domain\r\n";
$ical .= "DTSTAMP:" . date("Ymd\TGis") . "\r\n";
$ical .= "DTSTART;TZID=\"Asia/Karachi\":" . date("Ymd\THis", strtotime($startTime)) . "\r\n";
$ical .= "DTEND;TZID=\"Asia/Karachi\":" . date("Ymd\THis", strtotime($endTime)) . "\r\n";
$ical .= "TRANSP:OPAQUE\r\n";
$ical .= "SEQUENCE:1\r\n";
$ical .= "SUMMARY:$subject\r\n";
$ical .= "LOCATION:$location\r\n";
$ical .= "CLASS:PUBLIC\r\n";
$ical .= "PRIORITY:5\r\n";
$ical .= "BEGIN:VALARM\r\n";
$ical .= "TRIGGER:-PT15M\r\n";
$ical .= "ACTION:DISPLAY\r\n";
$ical .= "DESCRIPTION:Reminder\r\n";
$ical .= "END:VALARM\r\n";
$ical .= "END:VEVENT\r\n";
$ical .= "END:VCALENDAR\r\n";

$message .= $ical;
// 添加闭合边界
$message .= "--$mime_boundary--\r\n";

$mailsent = mail($to_address, $subject, $message, $headers);
echo $mailsent ? 'Success' : 'Failed';

内容的提问来源于stack exchange,提问作者Khurshid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 10:32:33