C#生成iCalendar(.ics)文件 C#如何创建日历事件文件

来源:这里教程网 时间:2026-02-21 17:41:32 作者:

StringBuilder
拼接标准 iCalendar 格式最直接

iCalendar(.ics)本质是纯文本,RFC 5545 规范定义了严格的字段顺序、换行和转义规则。C# 中不推荐用

XmlSerializer
或 JSON 库生成,也不必引入重型库(如 Ical.Net)——除非你需处理重复事件、时区转换或日历订阅等复杂逻辑。对单个会议、提醒类事件,
StringBuilder
手动拼接更可控、无依赖、不易出编码问题。

关键点:

DTSTART
DTEND
必须用 UTC 时间(末尾加
Z
)或带偏移的本地时间(如
+0800
),不能只写“20240520T140000”而不声明时区
每行不能超过 75 字节,超长需软换行(行末加
\r\n 
,注意空格)——但现代日历客户端(Outlook、Apple Calendar)大多容忍,可暂不处理
UID
必须全局唯一,建议用
Guid.NewGuid().ToString()
生成,避免重复导入时被忽略
必须以
BEGIN:VCALENDAR
开头、
END:VCALENDAR
结尾,中间嵌套
BEGIN:VEVENT
/
END:VEVENT

System.Text.Encodings.Web
处理中文标题和描述的 URL 编码陷阱

如果事件标题含中文(如“项目评审会”),直接写入 .ics 文件会导致 Outlook 打开乱码或解析失败。iCalendar 要求非 ASCII 字符必须用

ENCODING=BASE64
+
CHARSET=UTF-8
声明,不能靠文件保存编码“蒙混过关”。

正确做法是用

System.Text.Encodings.Web
WebEncoders.Base64Encode
编码字节:

string summary = "项目评审会";
byte[] utf8Bytes = Encoding.UTF8.GetBytes(summary);
string encoded = WebEncoders.Base64Encode(utf8Bytes);
// 写入 .ics 时:
// SUMMARY;ENCODING=BASE64;CHARSET=UTF-8:UEBqZWN057yW56iL5L+h5oGv

常见错误:

Convert.ToBase64String(Encoding.UTF8.GetBytes(...))
—— 结果一样,但
WebEncoders
是 .NET Core 3.0+ 官方推荐,语义更明确
漏写
CHARSET=UTF-8
,部分安卓日历会默认当 ISO-8859-1 解码
DESCRIPTION
LOCATION
忘记同样处理

File.WriteAllText
保存时必须指定
Encoding.UTF8

.ics 文件不是“随便保存就能用”的文本。如果调用

File.WriteAllText(path, content)
不传编码参数,.NET 默认用系统 ANSI(Windows 上常为 GBK),中文字段立刻变乱码,且 Apple Calendar 会直接拒绝导入。

务必显式指定 UTF-8:

File.WriteAllText(filePath, icsContent, Encoding.UTF8);

验证方法:用 VS Code 或 Notepad++ 打开生成的 .ics 文件,右下角确认编码显示为 “UTF-8”,而非 “UTF-8 with BOM” 或 “GBK”。iCalendar 规范禁止 BOM,BOM 会导致某些客户端解析失败。

其他注意事项:

路径中不要含中文或空格(尤其部署到 Linux 服务器时),用
Path.GetInvalidFileNameChars()
过滤
文件扩展名必须是
.ics
,不能是
.txt
.ical
,否则 macOS 双击不会关联日历应用
HTTP 下载时,响应头应设
Content-Type: text/calendar; charset=utf-8

Outlook 和 iOS 对
DTSTAMP
SEQUENCE
的宽松与严格

简单事件可省略

SEQUENCE
,但
DTSTAMP
(时间戳)必须存在,且应设为当前 UTC 时间。Outlook 允许缺省,iOS 则可能拒绝导入无
DTSTAMP
的文件。

若后续需更新同一事件(比如改时间),必须同时更新:

DTSTAMP
:设为更新时刻的 UTC 时间
SEQUENCE
:从 0 开始递增(每次修改 +1)
UID
:保持不变

否则 iOS 会当作新事件重复添加,Outlook 可能无法识别更新意图。测试时可用同一

UID
连续生成两个版本,导入后观察是否覆盖而非新增。

容易被忽略的是:所有时间字段(

DTSTART
DTEND
DTSTAMP
)格式必须统一——要么全用 UTC(结尾
Z
),要么全用本地时间+偏移(如
20240520T140000+0800
)。混用会导致 iOS 解析出错,且错误不报具体提示,只静默失败。

相关推荐