前言
即将进入带学,在杂七杂八的事情搞定之后便是坐等开学了。在学校里很重要的一个东西便是课程表,我大抵看了一下目前市面上的APP,我感觉更像是大学交友平台
有一个“小爱课程表”挺简洁的,而且目前在招纳个人开发者做适配,但是我申请了两回也不知道为什么没消息,而且开发者群里好像三天两头在说产品本身拉跨...
不过此时我把目光投向了“日历”APP,想起许久之前Apple日历还不支持中国节假日的时候(怒)曾在网络上有许多日历订阅的教程,这种日历不仅能由服务端分发,并且还能够及时更新,最重要的是,在临近日程时,日历会在我的手表上提示并常驻锁定屏幕(原生万岁)
那么,开始吧!
协议
首先要弄清楚别人是怎么实现的,搜寻一番,目前广泛使用的是“iCalendar”格式的日程(好像真的和果子有很多关系),然后就是“WebCal”协议,这个协议是“WebDev”系列协议,订阅的功能就基于它。
你可以在 https://icalendar.org/
上找到“iCalendar”格式的相关信息,至于“WebCal”,由客户端处理即可,我们仅需要保证服务端返回正确的“iCalendar”格式文件。
iCalendar格式
我会根据能找到的信息,最大努力翻译出来,但是仍然可能存在错误,烦请指正。
我使用Apple日历导出了一份,以便参考:
BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:飞行计划
X-APPLE-CALENDAR-COLOR:#63DA38
BEGIN:VEVENT
DESCRIPTION:起飞降落时间为航班计划时间,仅供参考。
DTEND;TZID=Asia/Shanghai:20200901T170000
DTSTART;TZID=Asia/Shanghai:20200901T150500
SUMMARY:乘坐HO1072 长沙黄花-上海浦东 当地时间15:05-17:00[航旅纵横]
URL;VALUE=URI:umetrip://OPENAPP
END:VEVENT
END:VCALENDAR
其中有部分在“iCalendar”提供的示例的内容我删去了(如动态时区)该部分内容我看着很头大,但不会影响我们简单的需求。
该文件由“BEGIN:VCALENDAR”和“END:VCALENDAR”两个头包括在一起,详细的文件格式你可以去其官网上找到更多资料。
X-WR-CALNAME 定义该日历的名称
X-APPLE-CALENDAR-COLOR 这个是Apple日历定义图标颜色的
然后是“BEGIN:VEVENT”和“END:VEVENT”段,这将定义一次事件
CREATED 创建日期
DESCRIPTION 备注/描述
SUMMARY 标题
DTEND;TZID 结束日期,其指定了时区
DTSTART;TZID 开始日期,其指定了时区
URL;VALUE 事件的url地址,可以点开
你可以使用Outlook网页版快捷测试自己的日历。
所以我照葫芦画瓢,写了一个:
BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:课程表
X-APPLE-CALENDAR-COLOR:#63DA38
BEGIN:VEVENT
DESCRIPTION:教师:曹华山\n学分:5.0
DTSTART;TZID=Asia/Shanghai:20201003T160000
DTEND;TZID=Asia/Shanghai:20201003T193000
LOCATION:教学楼C404
SUMMARY:网页设计综合实训
URL;VALUE=URI:hello
END:VEVENT
END:VCALENDAR
将其保存到文本文件,并使用ics后缀名,即可尝试导入到Outlook。
你便可以看到添加后的效果,但是其中有些参数不太清楚具体起什么作用,不过只要保证缺失不影响我们的内容即可,剩下的交给日历App本身去处理最佳。

当然在后期也应该多尝试几款日历App,试试他们自带的日程有什么专有格式,能够让日历更漂亮~
有了格式,那么接下来就是尝试转换数据了~
数据转换
首先应当让你学校教务系统提供的课程表变成结构化数据,因为不同学校不一样,这里就用我学校的教务网站做例子。
现状
不禁想要骂人,怎么会有这么离谱的东西?
这个教务系统竟然给人返回一张图片???

脑瓜子嗡嗡的,是怕有人学你课程表吗(捂脸)
看了一下请求按钮绑定的事件
function ChkValue()
{
var vXZBJ;
var d='';
var schoolcode="12300";
try{
if(schoolcode == "10533" || schoolcode == "10842" || schoolcode == "10691")//中南大学、三门峡职业技术学院、云南民族大学
vXZBJ=document.all.Sel_BJ.value;
else
vXZBJ=document.all.Sel_XZBJ.value;
if((vXZBJ=="")||(vXZBJ=="Nothing")){
alert('需选定行政班级!');
if(schoolcode == "10533" || schoolcode == "10842" || schoolcode == "10691")//中南大学、三门峡职业技术学院、云南民族大学
document.all.Sel_BJ.focus();
else
document.all.Sel_XZBJ.focus();
return false;
}
else if(document.all.txt_yzm.value=="" && d=="")
{
alert("须录入验证码!");
document.all.txt_yzm.focus();
return false;
}
else{
form.method='post';
form.action='KBFB_ClassSel_rpt.aspx';
form.target='frmRpt';
form.submit();
return true;
}
}catch(e){}
}
阿哲,不知道是哪来的外包系统(好拉跨)...
处理图片肯定不是优先方案,随后我找到这个教务系统提供了按课程和教室查询的方式,返回的是文字..(绝了)
便从这里试着入手吧~
按课程查询
获取整个课程列表后,就可以循环查询了,但是,这个神奇的教务系统,每个验证码只准你查询9次???这算啥??
没啥办法,先硬着头皮弄一些测试数据出来再说。
经过一番折腾,生成了这样的数据:
array(2) {
[0]=>
array(6) {
["name"]=>
string(25) "130601|客户关系管理"
["teacher"]=>
string(6) "韩梅"
["type"]=>
string(19) "专业课/必修课"
["week"]=>
string(4) "1-18"
["day"]=>
string(11) "二[1-2节]"
["location"]=>
string(13) "教学楼A401"
}
[1]=>
array(6) {
["name"]=>
string(28) "130625|商务谈判与礼仪"
["teacher"]=>
string(9) "王晓霞"
["type"]=>
string(19) "专业课/必修课"
["week"]=>
string(4) "1-18"
["day"]=>
string(11) "三[3-4节]"
["location"]=>
string(13) "教学楼C502"
}
}
这是按班级名称分的数组内容(部分课程数据)
同时,已经根据学校提供的校历,作息时间表进行格式化了,所以接下来就是拼凑~
生成数据
按照目标格式拼凑数据,然后将其导入到outlook,便可看到效果:

进行订阅
webcal像webdav一样,本质上还是通过http进行请求,一般来说,你只需要为服务器头加上
Content-type: text/calendar; charset=utf-8
Content-Disposition: attachment; filename="table.ics"
这样日历软件便知道请求得到的内容是日历了。
但是参考iCalendar的规范,每个事件还得有一个“UID”字段,这样便于客户端识别事件以更新。
这个UID虽然有规范,但实际上你只需要做到和其他事件的UID不相同即可,这里我简单的用单个课程的名字,日期和节数进行md5计算。
然后在手机上访问 webcal://会提供ics下载的url
即可

使用outlook添加日历订阅也是一样的操作

Done
已经搞定了基本的操作了,接下来就是要变得易用,这样可以让同学们也能轻松使用~
不过最好是对单个班级的日程提供缓存机制,因为目前是数据量比较小的情况下,多起来的化可能会比较吃亏,或者实现仅返回 前一周,本周,下周和下下周会有更好的性能表现。
目前已部署到 https://ics.statict.cn
目前测试所有iOS设备、Win10设备正常使用。
安卓设备问了一个魅族和一个vivo用户,没有安装第三方日历App,没有任何导入的办法...
[...]https://imrbq.cn/work/timetable.html[...]
V2EX-分享一个自己整的课程表导入日历的工具 | 牛C网(NiuL.Net) 2022-01-06 23:46
wakeup课程表挺好用的,也可以导出日历文件到各种平台的日历APP |´・ω・) ノ
fructose 2022-01-06 23:05
适用!
叮咚 2021-03-02 20:33
|´・ω・) ノ
Mizore 2021-01-23 10:54
我也折腾过导入到原生日历,不知道为什么死活不会提醒(导入后也会显示提醒的那个时间点)。不过我折腾的是Android10下面的。
心灵博客 2020-10-18 22:40