iCalendar 简介

iCalendar,简称“iCal”,是“日历数据交换”的标准(RFC 2445),该标准提供了一种公共的数据格式用于存储关于日历方面的信息,比如事件、约定、待办事项等。它不仅允许用户通过电子邮件发送会议或者待办事件等,也允许独立使用,而不局限于某种传输协议。

目前,所有流行日历工具比如:Lotus Notes、Outlook、GMail 和 Apple 的 iCal 都支持 iCalendar 标准,其文件扩展名为 .ical、.ics、.ifb 或者 .icalendar。C&S(Calendaring and Scheduling) 核心对象是一系列日历和行程安排信息。通常情况下,这些日历和行程信息仅仅包含一个 iCalendar 组件(iCalendar 组件分为 Events(VEVENT)、To-do(VTODO)、Journal(VJOURNAL)、Free/busy time (VFREEBUSY)、VTIMEZONE (time zones) 和 VALARM (alarms)),但是多个 iCalendar 组件可以被组织在一起。

C&S 核心对象第一行必须是“BEGIN:VCALENDAR”, 并且最后行必须是“END:VCALENDAR”。在这两行之间主要是由一系列日历属性和一个或者多个 iCalendar 组件组成。

下面看一个例子,它表示发生在 1997 年七月十四日下午五点与 1997 年七月十五日四点之间的事件“Bastille Day Party”。

BEGIN:VCALENDAR          ------ 起始

VERSION:2.0            ------ 版本

PRODID:iCal4j v1.0//EN      ------ 创建该对象的标志符

BEGIN:VEVENT           ------ 事件开始

DTSTART:19970714T170000Z     ------ 事件起始时间

DTEND:19970715T040000Z      ------ 事件结束时间

SUMMARY:Bastille Day Party    ------ 事件概要

END:VEVENT            ------ 事件结束

END:VCALENDAR           ------ 结束

iCalendar 编程基础

iCal4j 简介

iCal4j(产生于 2004 年 4 月,目前是 2.0 版本),是一组读写 iCalendar 数据流的Java API,支持 iCalendar 规范 RFC 2445,主要包括解析器、对象模型以及生成器。

文件读写

任何一种文件格式读写都是最基本的操作,清单 1 向我们演示了这两项基本操作。CalendarBuilder 对象是用来通过输入流解析和构造 iCalendar 模型。值得注意的是, CalendarBuilder 并不是 线程安全的。

清单 1:iCalendar 文件读写示例

public static void readAndWrite(String in, String out)

throws IOException, ParserException, ValidationException {

FileInputStream fin = new FileInputStream(in);

CalendarBuilder builder = new CalendarBuilder();

Calendar calendar = builder.build(fin);

//TODO: 对 iCalendar 数据进行处理

… …

FileOutputStream fout = new FileOutputStream(out);

CalendarOutputter outputter = new CalendarOutputter();

outputter.output(calendar, fout);

}

iCalendar 索引

对组件和属性进行索引之后,我们可以更加有效的查找组件和属性,通常情况下大家用索引去不断地检查某事件(或约定等)是否存在。假定一个场景,您经常性的需要更新一些日历,并且需要检查事件是否已经存在。因为您需要不断地检查日历中的事件,此时对日历中的事件进行索引将是一件有意义的事情。

清单 2:iCalendar 索引

// 创建索引列表

IndexedComponentList indexedEvents = new IndexedComponentList(

myCalendar.getComponents(Component.VEVENT), Property.UID);

// 检查事件

for (Iterator i=inputCalendar.getComponents(Component.VEVENT).iterator(); i.hasNext();){

VEvent event = (VEvent) i.next();

Component existing = indexedEvents.getComponent(event.getUid().getValue());

if (existing == null) {

myCalendar.getComponents().add(event);

}

else if (!event.equals(existing)) {

// 删除已经存在的事件并添加修改后的事件

myCalendar.getComponents().remove(existing);

myCalendar.getComponents().add(event);

}

}

如清单 2 所示,这里请注意,UID 被用来标示唯一的事件。在得到索引组件列表后,我们就可以通过 UID 来检查某一事件是否存在。如果存在的话,修改该事件;否则,添加该事件。

创建日历/事件/会议

iCalendar 向我们提供了一种公共的数据格式用于存储关于日历方面的信息比如事件、约定、待办事项等,那么如何创建事件/约定/待办事件就是一个大家比较关心的问题,如清单 3。一个日历,有一些基本的属性是必须的,例如 prodid 和 version,但是又只能有一次,一些是可选的,例如 calscale,具体的可以参考 RFC 2445。

清单 3:创建一个事件示例

// 创建一个时区(TimeZone)

TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();

TimeZone timezone = registry.getTimeZone("America/Mexico_City");

VTimeZone tz = timezone.getVTimeZone();

// 起始时间是:2008 年 4 月 1 日 上午 9 点

java.util.Calendar startDate = new GregorianCalendar();

startDate.setTimeZone(timezone);

startDate.set(java.util.Calendar.MONTH, java.util.Calendar.APRIL);

startDate.set(java.util.Calendar.DAY_OF_MONTH, 1);

startDate.set(java.util.Calendar.YEAR, 2008);

startDate.set(java.util.Calendar.HOUR_OF_DAY, 9);

startDate.set(java.util.Calendar.MINUTE, 0);

startDate.set(java.util.Calendar.SECOND, 0);

// 结束时间是:2008 年 4 月 1 日 下午 1 点

java.util.Calendar endDate = new GregorianCalendar();

endDate.setTimeZone(timezone);

endDate.set(java.util.Calendar.MONTH, java.util.Calendar.APRIL);

endDate.set(java.util.Calendar.DAY_OF_MONTH, 1);

endDate.set(java.util.Calendar.YEAR, 2008);

endDate.set(java.util.Calendar.HOUR_OF_DAY, 13);

endDate.set(java.util.Calendar.MINUTE, 0);

endDate.set(java.util.Calendar.SECOND, 0);

// 创建事件

String eventName = "Progress Meeting";

DateTime start = new DateTime(startDate.getTime());

DateTime end = new DateTime(endDate.getTime());

VEvent meeting = new VEvent(start, end, eventName);

// 添加时区信息

meeting.getProperties().add(tz.getTimeZoneId());

// 生成唯一标志符

UidGenerator ug = new UidGenerator("uidGen");

Uid uid = ug.generateUid();

meeting.getProperties().add(uid);

// 添加参加者 .

Attendee dev1 = new Attendee(URI.create("mailto:dev1@mycompany.com"));

dev1.getParameters().add(Role.REQ_PARTICIPANT);

dev1.getParameters().add(new Cn("Developer 1"));

meeting.getProperties().add(dev1);

Attendee dev2 = new Attendee(URI.create("mailto:dev2@mycompany.com"));

dev2.getParameters().add(Role.OPT_PARTICIPANT);

dev2.getParameters().add(new Cn("Developer 2"));

meeting.getProperties().add(dev2);

// 创建日历

net.fortuna.ical4j.model.Calendar icsCalendar = new net.fortuna.ical4j.model.Calendar();

icsCalendar.getProperties().add(new ProdId("-//Events Calendar//iCal4j 1.0//EN"));

icsCalendar.getProperties().add(CalScale.GREGORIAN);

// 添加事件

icsCalendar.getComponents().add(meeting);

TimeZoneRegistry 则表示 net.fortuna.ical4j.model.TimeZone 句柄的一个仓库(Repository)。清单 4 演示了如何获取一个 TimeZoneRegistry,并通过 TimeZoneRegistry 来获取一个时区(TimeZone)。

清单 4:获取时区示例

CalendarBuilder builder = new CalendarBuilder();

Calendar calendar = builder.build(new FileInputStream("mycalendar.ics"));

TimeZoneRegistry registry = builder.getRegistry();

TimeZone tz = registry.getTimeZone("Australia/Melbourne");

添加附件和二进制数据

iCal4j 代码库允许用户添加二进制数据,类 Attach 可以被用来实现二进制数据的添加,如代码清单 5。

代码清单 5:添加二进制数据示例

public static void attachBinaryAttachment(String subject, String out, String binary)

throws IOException, ParserException, ValidationException, ParseException {

FileInputStream bin = new FileInputStream(binary);

ByteArrayOutputStream bout = new ByteArrayOutputStream();

Calendar calendar = new Calendar();

DateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm");

DateTime start = new DateTime(format.parse("11/09/2009 08:00").getTime());

DateTime end = new DateTime(format.parse("11/09/2009 09:00").getTime());

calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));

calendar.getProperties().add(Version.VERSION_2_0);

calendar.getProperties().add(CalScale.GREGORIAN);

VEvent event = new VEvent(start, end, subject);

event.getProperties().add(new Uid(new UidGenerator("iCal4j")

.generateUid().getValue()));

for (int i = bin.read(); i >= 0;) {

bout.write(i);

i = bin.read();

}

ParameterList params = new ParameterList();

params.add(Encoding.BASE64);

params.add(Value.BINARY);

Attach attach = new Attach(params, bout.toByteArray());

event.getProperties().add(attach);

calendar.getComponents().add(event);

// 验证

calendar.validate();

FileOutputStream fout = new FileOutputStream(out);

CalendarOutputter outputter = new CalendarOutputter();

outputter.output(calendar, fout);

}

程序输出结果为:

BEGIN:VCALENDAR

PRODID:-//Ben Fortuna//iCal4j 1.0//EN

VERSION:2.0

CALSCALE:GREGORIAN

BEGIN:VEVENT

DTSTAMP:20091119T124443Z

DTSTART:20091109T080000

DTEND:20091109T090000

SUMMARY:iCal4j Attach Binary Example

UID:20091119T124443Z-iCal4j@192.168.1.100

ATTACH;ENCODING=BASE64;VALUE=BINARY:VGhp333cyBhIENsaWVudCBmb3IgRS1CdXNpb

mVzcyBJbWFnZQ==

END:VEVENT

END:VCALENDAR

循环事件

iCalendar 规范支持循环事件,即事件不止一次发生,通常使用一系列的日期(RDATE)或者一个循环规则(RRULE)。现在列举一个简单的 RRULE 例子。

事件每间隔一月,在每个月的 29 日发生。

RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=29

注意:非闰年二月份只有 28 天,因此这条规则将表示事件在上一年的 12 月份和 4 月之间将有一个跳跃。

下面分别讲解一下程序如何生成一系列的日期(RDATE)和一个循环规则(RRULE)。清单 6 讲解了如何生成一个日期列表。

清单 6:创建循环事件(使用 RDate)示例

public static void addRDate(String subject, String out)

throws IOException, ParserException, ValidationException,

URISyntaxException, ParseException {

Calendar calendar = new Calendar();

calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));

calendar.getProperties().add(Version.VERSION_2_0);

calendar.getProperties().add(CalScale.GREGORIAN);

PeriodList periodList = new PeriodList();

ParameterList paraList = new ParameterList();

DateFormat format = new SimpleDateFormat("MM/dd/yyyy hh:mm");

DateTime startDate1 = new DateTime(format.parse(("11/09/2009 08:00")));

DateTime startDate2 = new DateTime(format.parse(("11/10/2009 09:00")));

DateTime endDate1 = new DateTime(format.parse(("11/09/2009 09:00")));

DateTime endDate2 = new DateTime(format.parse(("11/10/2009 11:00")));

periodList.add(new Period(startDate1, endDate1));

periodList.add(new Period(startDate2, endDate2));

VEvent event = new VEvent(startDate1, endDate1, subject);

event.getProperties().add(new Uid(new UidGenerator("iCal4j").

generateUid().getValue()));

paraList.add(ParameterFactoryImpl.getInstance().createParameter(

Value.PERIOD.getName(), Value.PERIOD.getValue()));

RDate rdate = new RDate(paraList,periodList);

event.getProperties().add(rdate);

calendar.getComponents().add(event);

// 验证

calendar.validate();

FileOutputStream fout = new FileOutputStream(out);

CalendarOutputter outputter = new CalendarOutputter();

outputter.output(calendar, fout);

}

程序输出结果为:

BEGIN:VCALENDAR

PRODID:-//Ben Fortuna//iCal4j 1.0//EN

VERSION:2.0

CALSCALE:GREGORIAN

BEGIN:VEVENT

DTSTAMP:20091119T100938Z

DTSTART:20091109T000000Z

DTEND:20091109T010000Z

SUMMARY:iCal4j RDate Example

UID:20091119T100938Z-iCal4j@IBM-3C6595B4005.cn.ibm.com

RDATE;VALUE=PERIOD:20091109T000000Z/20091109T010000Z,20091110T010000Z/200 91110T030000Z

END:VEVENT

END:VCALENDAR

清单 7 演示了如何用 RRule 创建一个循环事件,该事件从 2009 年 11 月 9 日开始,间隔一周,持续 4 次。

代码清单 7: 创建循环事件(使用 RRule)示例

public static void addRRule(String subject, String out)

throws IOException, ParserException, ValidationException,

URISyntaxException, ParseException {

Calendar calendar = new Calendar();

DateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm");

DateTime start = new DateTime(format.parse("11/09/2009 08:00").getTime());

DateTime end = new DateTime(format.parse("11/09/2009 09:00").getTime());

calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));

calendar.getProperties().add(Version.VERSION_2_0);

calendar.getProperties().add(CalScale.GREGORIAN);

VEvent event = new VEvent(start, end, subject);

event.getProperties().add(new Uid(new UidGenerator("iCal4j").generateUid()

.getValue()));

Recur recur = new Recur(Recur.WEEKLY, 4);

recur.setInterval(2);

RRule rule = new RRule(recur);

event.getProperties().add(rule);

calendar.getComponents().add(event);

// 验证

calendar.validate();

FileOutputStream fout = new FileOutputStream(out);

CalendarOutputter outputter = new CalendarOutputter();

outputter.output(calendar, fout);

}

程序输出结果为:

BEGIN:VCALENDAR

PRODID:-//Ben Fortuna//iCal4j 1.0//EN

VERSION:2.0

CALSCALE:GREGORIAN

BEGIN:VEVENT

DTSTAMP:20091119T094724Z

DTSTART:20091109T080000

DTEND:20091109T090000

SUMMARY:iCal4j RRule Usage Example

UID:20091119T094724Z-iCal4j@IBM-3C6595B4005.cn.ibm.com

RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=4

END:VEVENT

END:VCALENDAR

扩展

通常情况下我们把没有在 RFC2445 中没有定义的组件(components)/属性(properties)/参数(parameters)都称为非标准的或者扩展的对象。 iCalendar 允许用户扩展组件/属性/参数,但是名字必须以 X- 开头以兼容 iCalendar 标准(除非使能 ical4j.parsing.relaxed),这些对象分别用 XComponent, XProperty and XParameter 来表示。

下面的例子演示了如何扩展一个事件的属性 ical4j_extension_sample,见清单 8。

清单 8: 扩展事件属性示例

public static void extension(String subject, String out)

throws IOException, ParserException, ValidationException, URISyntaxException

, ParseException {

Calendar calendar = new Calendar();

DateFormat format = new SimpleDateFormat("MM/dd/yyyy HH:mm");

DateTime start = new DateTime(format.parse("11/09/2009 08:00").getTime());

DateTime end = new DateTime(format.parse("11/09/2009 09:00").getTime());

calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN"));

calendar.getProperties().add(Version.VERSION_2_0);

calendar.getProperties().add(CalScale.GREGORIAN);

VEvent event = new VEvent(start, end, subject);

event.getProperties().add(new Uid(new UidGenerator("iCal4j")

.generateUid().getValue()));

// 注意:扩展的属性必须以 X- 开头

Property xProperty = PropertyFactoryImpl.getInstance()

.createProperty("X-iCal4j-extension");

xProperty.setValue("ical4j_extension_sample");

event.getProperties().add(xProperty);

calendar.getComponents().add(event);

// 验正

calendar.validate();

FileOutputStream fout = new FileOutputStream(out);

CalendarOutputter outputter = new CalendarOutputter();

outputter.output(calendar, fout);

}

程序输出结果为:

BEGIN:VCALENDAR

PRODID:-//Ben Fortuna//iCal4j 1.0//EN

VERSION:2.0

CALSCALE:GREGORIAN

BEGIN:VEVENT

DTSTAMP:20091119T093429Z

DTSTART:20091109T080000

DTEND:20091109T090000

SUMMARY:iCal4j Extension Example

UID:20091119T093429Z-iCal4j@IBM-3C6595B4005.cn.ibm.com

X-iCal4j-extension:ical4j_extension_sample

END:VEVENT

END:VCALENDAR

iCal4j Connector 介绍

iCal4j Connector 是 iCal4j 代码库的一个扩展,它提供连接后端 Calendar/vCard 服务器支持(支持的 HTTP 方法包括 GET, HEAD, POST, PUT, DELETE, TRACE, COPY, MOVE),服务器包括 CalDAV、与 CardDAV 相兼容的服务器和 JCR(Java Content Repository)。

java 解析 ical_转载iCalendar 编程基础:了解和使用 iCal4j相关推荐

  1. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  2. java学习笔记14--多线程编程基础1

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...

  3. java面向对象程序_面向对象编程基础(java)

    面向对象编程基础 1.1 面向对象概述 在程序开发初期,大家使用的是结构化开发语言,也就是面向过程(opp),但随着市场需求剧增,软件的规模也越来越大,结构化语言的弊端也暴露出来. 开发周期无休止的拖 ...

  4. java 同一个package import_【编程基础】Java 中的Package和Import

    开始要逐步去熟悉最基础的语法了,幸好之前是有底子的,所以理解也不难,  import Package 有点类似C语言中的#include [头文件] 为什么要用包(Package) 当一个大型程序交由 ...

  5. java swt 菜鸟教程_编程基础学习JS的入门教程

    将JavaScript 插入网页的方法 使用 插入JavaScript 与在网页中插入CSS的方式相似.使用下面的代码可以在网页中插入JavaScript: ... 其中的...就是代码的内容.Jav ...

  6. Java解析excel的通用方法--基础版

    提出问题: 通过销售地图项目和目前的评分系统的项目都需要用到解析excel,并且每次因为excel中列名的不同和对应的实体类的不同,每一次都需要重新写一个解析excel的方法,代码之长很复杂也很麻烦写 ...

  7. java双等号和equals_JAVA编程基础篇:hashCode的特性和作用

    hashCode作用可以归结为:加速查找速度 hashCoed 的特性 (1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode经常用于确定对象的存 ...

  8. java反射头文件_编程基础知识——C++能不能支持Java和ObjC的反射?

    C++能不能支持Java和ObjC的反射? 要回答这个问题.首先我们要清楚什么是反射.什么是反射? 教科书的解释我就不说了,(^o^)事实上我也记不得.实际开发应用的反射就是在没有某个类型的头文件或者 ...

  9. java 解析 ical_使用Java编写.ics iCal文件

    我正在尝试使用java实现我自己的iCal创建者,由于某种原因,我无法识别我的.ics文件.我想知道我做错了什么,我可以得到与维基百科的示例完全一样的输出. .ics文件和程序生成后的文件之间有什么区 ...

最新文章

  1. 蝴蝶扇了一下翅膀,混沌就诞生了
  2. Java NIO学习系列五:I/O模型
  3. 前端工程化系列好文摘要
  4. python方法定义..._解析Python类中的方法定义
  5. [转载] java中的经典问题:传值与传引用
  6. Java笔记-通过注解和插件自动生成get/set和toString方法,使代码结构清晰
  7. C语言运算符优先级和结合性
  8. 【UNIX环境高级编程】UNIX基础知识
  9. 软件工程 第4版张海藩 pdf_2019年第4期软件工程造价师培训课程圆满结束
  10. 线性调频信号的脉冲压缩(匹配滤波)
  11. Python 如何随机生成姓名?
  12. 基于新浪微博的男女性择偶观数据分析(下)
  13. 南京大学Sweeper(2000)原创:地理系在哈佛的灭亡与计量革命
  14. 洛谷P4147 玉蟾宫(单调栈解决)
  15. 为什么python文件会闪退_困扰已久的问题--python文件打开方式?为什么打开py文件会闪退!...
  16. 模拟电路50(开关电容滤波器)
  17. 请检查ftp文件服务器是否开启,查看服务器是否开启ftp服务
  18. 51单片机12864坐标轴显示,并实时刷新数proteus实现
  19. 数据库管理工具heidiSQL的基本使用
  20. 粒子特效-Xffect

热门文章

  1. 软考高级信息系统项目管理师系列之四十九:量化的项目管理
  2. 原生js输出html5,原生JS+HTML5实现的可调节写字板功能示例
  3. iOS 本地动态验证码生成
  4. 机器人还原魔方时间再次刷新记录 这一次只用了0.38秒!
  5. 遇到的一些bug合集
  6. Pixologic ZBrush 中文版
  7. 工作中PTO和OOO是什么意思?
  8. cloudmersive OCR识别聊天记录图片转文字
  9. 推荐提升效率的4个Windows 10任务栏快捷键
  10. iwatch可以用计算机吗,Apple Watch能接电脑么?连接方法教程