项目有个需求,做个类似outlook的周期性会议的功能,如下图所示:
-
方案一:
由于使用的是java平台,刚开始想通过成熟的调度框架
quartz
来实现,但是无法通过一个trigger实现一个复杂任务的调度。如:每2个月的第四周的周末,次行10次后结束quartz提供来四种计划:
SimpleScheduleBuilder
CronScheduleBuilder
DailyTimeIntervalScheduleBuilder
CalendarIntervalScheduleBuilder
没种计划都有各自特定的场景。cronExpress也不是万能的,比如表示【每2个月的第四周的周末】是无法通过表达式:0 0 0 ?*/2 SAT#4,SUN#4
,对于周来讲这样的写法会抛出异常,不运行逗号隔开多个Support for specifying multiple "nth" days is not implemented.
-
方案二
网上找了好久的方案,通过这篇文章日历设计之重复事件规则设计,知道了有个icalendar的协议RFC2445协议 (更加详细的中文解释在https://www.jianshu.com/p/8f8572292c58)。
于是,我需要找基于
java
平台的支持RFC2445协议
,找到了2个。1. lib-recur
github地址:https://github.com/dmfs/lib-recur \
验证地址:http://recurrence-expansion-service.appspot.com/
验证代码:RecurrenceRule rule = new RecurrenceRule("FREQ=MONTHLY;INTERVAL=1;BYDAY=2SA,2SU;COUNT=10"); DateTime start = DateTime.parse("20180528T063302Z");//new DateTime(2018, Calendar.MAY /* 0-based month numbers! */,28, 14, 33, 02); RecurrenceRuleIterator it = rule.iterator(start); // it.skip(0); int maxInstances = 20; // limit instances for rules that recur forever while (it.hasNext() && (!rule.isInfinite() || maxInstances-- > 0)) { DateTime nextInstance = it.nextDateTime(); System.out.println(nextInstance.toString()); // do something with nextInstance }
2. ical4j
github地址:https://github.com/ical4j/ical4j
代码演示:
@Test public void testIcal4j() throws ParseException, IOException { DtStart dtstart = new DtStart("20180528T063302Z"); DtStart endDate = new DtStart("20200925T063302Z"); RRule rule = new RRule("FREQ=MONTHLY;INTERVAL=1;BYDAY=2SA,2SU;COUNT=10"); VEvent sessionEvent = new VEvent(dtstart.getDate(), "hahahhehe"); sessionEvent.getProperties().add(rule); // sessionEvent.getProperties().add(dtstart); sessionEvent.getProperties().add(new Uid("2322332323323323")); sessionEvent.getProperties().add(new Location("南京堵路")); // 提醒,提前10分钟 VAlarm valarm = new VAlarm(new Dur(0, 0, -10, 0)); valarm.getProperties().add(new Summary("Event Alarm")); valarm.getProperties().add(Action.DISPLAY); valarm.getProperties().add(new Description("Progress Meeting at 9:30am")); sessionEvent.getAlarms().add(valarm); net.fortuna.ical4j.model.Calendar calendar = new net.fortuna.ical4j.model.Calendar(); calendar.getProperties().add(new ProdId("-//Ben Fortuna//iCal4j 1.0//EN")); calendar.getProperties().add(Version.VERSION_2_0); calendar.getProperties().add(CalScale.GREGORIAN); calendar.getComponents().add(sessionEvent); calendar.validate(); // FileOutputStream fout = new FileOutputStream("/Users/rick/jkxyx205/log/2.ics"); // CalendarOutputter outputter = new CalendarOutputter(); // outputter.output(calendar, fout); // PeriodList periodList = sessionEvent.getConsumedTime(dtstart.getDate(), endDate.getDate()); // // for(Period period : periodList) { // System.out.println(period.get); // } DateList list = rule.getRecur().getDates(dtstart.getDate(), dtstart.getDate(), endDate.getDate(), Value.DATE_TIME, 10); for(Date date : list) { System.out.println(date.toLocaleString()); } }
可以通过该库输出标准格式文件.ics,可参考文章ical4j 实现ICS文件的生成和解析
为了提高性能,需要在客户端处理相关协议,基于
javascript
平台的支持RFC2445协议前端框架rrule
github地址:https://github.com/jakubroztocil/rrule \
验证地址:http://jakubroztocil.github.io/rrule/
更完整的rrule验证:https://www.textmagic.com/free-tools/rrule-generator
技术难点
-
- 用户在打开应用时, 当页面定位在某个月(周, 日)视图上时, 怎么样从这个用户中所有的周期事件中快速计算出所有的当前视图事件? 不可能每次请求一次, 全部事件计算一次.
-
- 日历的主要功能是提醒. 怎么样准时, 无误的把每分钟要提醒的短信, 邮件发出去, 这又是个量变到质变的难点, 如果当前数据库有几千万的周期事件?