通用的日历格式是 iCalendar,通常扩展名是.ics
iCalendar允许用户通过电子邮件的方式发送“会议请求”或“任务”。收信人使用支持iCalendar邮件客户端,便可以很方便地回应发件人,接受请求或另外提议一个新的会议时间。
维基百科中关于 iCalendar 的描述是: https://zh.wikipedia.org/wiki/ICalendar
具体参考的标准是 RFC 5545
BEGIN:VCALENDAR # 日历开始
PRODID:-//test.us//iCalendar Event//EN # 软件信息
VERSION:2.0 # 遵循的 iCalendar 版本号
CALSCALE:GREGORIAN # 历法:公历
METHOD:PUBLISH # 方法:公开 也可以是 REQUEST 等用于日历间的信息沟通方法
CLASS:PUBLIC # Classification 此属性定义日历组件的访问分类: "PUBLIC" / "PRIVATE" / "CONFIDENTIAL" / iana-token / x-name
BEGIN:VTIMEZONE # Time Zone Component
TZID:Asia/Shanghai # Time Zone Identifier 时区标识符
TZURL:http://tzurl.org/zoneinfo-outlook/Asia/Shanghai # Time Zone URL: 访问 http://tzurl.azureedge.net/zoneinfo-outlook/Asia
X-LIC-LOCATION:Asia/Shanghai
BEGIN:STANDARD
TZOFFSETFROM:+0800
TZOFFSETTO:+0800
TZNAME:CST
DTSTART:19700101T000000
END:STANDARD
END:VTIMEZONE # 时区组件结束
BEGIN:VEVENT # 事件开始
DTSTAMP:20190723T071307Z # 有 Method 属性时表示 实例创建时间,没有时表示最后修订的日期时间
DTSTART;TZID=Asia/Shanghai:20191024T150000 # 开始的日期时间
DTEND;TZID=Asia/Shanghai:20191024T160000 # 结束的日期时间
SUMMARY:我的会议 # 简介 一般是标题
# UID (唯一标识符)
UID:20190723T071307Z-105904298@fe80:0:0:0:e4:cbff:fe80:b24c%ens5
# Time Zone Identifier 时区标识符
TZID:Asia/Shanghai
# 描述
DESCRIPTION:issac 的描述
# 地址
LOCATION:https://cloud.tencent.com/developer/user/6258660
# Alarm Component 报警组件
BEGIN:VALARM
# 触发 alarm (开始之前10分钟提醒)
TRIGGER:-PT10M
# AUDIO\DISPLAY\EMAIL
ACTION:DISPLAY
# In a DISPLAY alarm, the intended alarm effect is for
# the text value of the "DESCRIPTION" property to be displayed to the user.
DESCRIPTION:Reminder
# alarm 结束
END:VALARM
# 事件结束
END:VEVENT
# 日历结束
END:VCALENDAR
# 重复规则 rrule (Recurrence Rule):
RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20190917T133000;INTERVAL=1;BYDAY=TU
重复规则包含多个属性, 每个属性以 NAME = VALUE 对的形式存在, 属性与属性之间用分号区分, 属性之间没有特定的顺序要求,在同一个重复规则中每个属性最多只能出现一次。
1、FREQ
FREQ 属性表示重复规则的类型,可选的 VALUE 有:
2、WKST
WKST 取值范围 MO(周一), TU(周二), WE(周三), TU(周四), FR(周五), SA(周六), SU(周日)。 默认值为 MO。
当一个 WEEKLY 类型的重复规则, INTERVAL 大于 1, 且带有 BYDAY 属性时, 则必须带有 WKST 属性。 当一个 YEARLY 类型的重复规则带有 BYWEEKNO 属性时, 也必须带有 WKST 属性。
3、UNTIL
UNTIL 属性定义了一个日期-时间值,用以限制重复规则。
这个日期-时间值表示这个重复规则的最后一次事件的发生时间。
如果重复规则中未包含 UNTIL 和 COUNT 属性, 则表示该重复规则无限重复。
4、INTERVAL
INTERVAL 属性表示重复规则的间隔, 必须为正整数。 默认值为1。
对应上述不同的 FREQ 值分别表示每一秒,每一分钟, 每一小时, 每一天, 每一周, 每一月, 每一年。s
5、BYDAY
BYDAY 取值范围: MO(周一), TU(周二), WE(周三), TU(周四), FR(周五), SA(周六), SU(周日)。可以有多个值,用逗号分隔。
每个值可以在前面加上一个正整数(+n)或者负整数(-n),用以在 MONTHLY 或者 YEARLY 的重复类型中表示第 n 个周几。 例如,在一个 MONTHLY 类型的重复规则中, +1MO(或者1MO)表示这个月的第1个周一,如果是 -1MO 则表示这个月的最后1个周一。
如果前面没有数字,则表示在这个重复类型中的所有的周几, 比如在一个 MONTHLY 的重复类型中, MO 表示这个月里所有的周一。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import BaseHTTPServer
import httplib2
import urlparse
from apiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
"""Child class of BaseHTTPRequestHandler that only handles GET request."""
# https://developers.google.com/calendar/quickstart/js
clientID = '295995113385-nu8em9928itnt7fa92dc5ab1tv710ojs.apps.googleusercontent.com'
apiKey = 'YFzGsExw6fURfmysfxr1lvpF'
authUrl = 'https://www.googleapis.com/auth/calendar'
flow = OAuth2WebServerFlow(clientID, apiKey, authUrl, redirect_uri='http://localhost:8080/')
def do_GET(self):
"""Handler for GET request."""
print '\n\nNEW REQUEST, Path: %s' % (self.path)
if self.path.startswith('/?fake_user='):
self.handle_initial_url()
# When you redirect to the authorization server below, it redirects back
# to to http://localhost:8080/?code=<some_code> after the user grants access
# permission for your application.
elif self.path.startswith('/?code='):
self.handle_redirected_url()
else:
self.respond_ignore() # Either an error from auth server or bad user entered URL.
def handle_initial_url(self):
"""Handles the initial path."""
parsed = urlparse.urlparse(self.path)
fake_user = urlparse.parse_qs(parsed.query)['fake_user'][0]
self.respond_redirect_to_auth_server(fake_user)
def respond_redirect_to_auth_server(self, fake_user):
"""Respond to the current request by redirecting to the auth server."""
uri = RequestHandler.flow.step1_get_authorize_url()
print 'Redirecting %s to %s' % (fake_user, uri)
self.send_response(301)
self.send_header('Cache-Control', 'no-cache')
self.send_header('Location', uri)
self.end_headers()
def handle_redirected_url(self):
"""Handles the redirection back from the authorization server."""
# The server should have responded with a "code" URL query parameter. This is needed to acquire credentials.
parsed = urlparse.urlparse(self.path)
code = urlparse.parse_qs(parsed.query)['code'][0]
credentials = RequestHandler.flow.step2_exchange(code)
http = httplib2.Http()
http = credentials.authorize(http)
service = build('calendar', 'v3', http=http)
event = {
# 'summary': 'Google I/O 2015',
'summary': 'tttttt',
'location': '800 Howard St., San Francisco, CA 94103',
'description': 'A chance to hear more about Google\'s developer products.',
'start': {
'dateTime': '2019-08-18T09:00:00-07:00',
'timeZone': 'America/Los_Angeles',
},
'end': {
'dateTime': '2019-08-18T17:00:00-07:00',
'timeZone': 'America/Los_Angeles',
},
'recurrence': [
# 'RRULE:FREQ=DAILY;COUNT=2'
# 'RRULE:FREQ=WEEKLY' # 每周重复
# 'RRULE:FREQ=WEEKLY;WKST=SU;UNTIL=20200121T133000;INTERVAL=4;BYDAY=TU' # 报错了
],
'attendees': [
{'email': 'lpage@example.com'},
{'email': 'sbrin@example.com'},
],
'reminders': {
'useDefault': False,
'overrides': [
# {'method': 'email', 'minutes': 24 * 60},
{'method': 'popup', 'minutes': 15},
],
},
}
res = service.events().insert(calendarId='primary', body=event).execute()
parsed = urlparse.urlparse(res.get('htmlLink'))
eidCode = urlparse.parse_qs(parsed.query)['eid'][0]
eventeditUrl = 'https://calendar.google.com/calendar/r/eventedit/' + eidCode
print '>>> eventeditUrl: %s' % (eventeditUrl)
self.send_response(301)
self.send_header('Cache-Control', 'no-cache')
self.send_header('Location', eventeditUrl)
self.end_headers()
def respond_ignore(self):
"""Responds to the current request that has an unknown path."""
self.send_response(200)
self.send_header('Content-type', 'text/plain')
self.send_header('Cache-Control', 'no-cache')
self.end_headers()
self.wfile.write(
'This path is invalid or user denied access:\n%s\n\n' % self.path)
self.wfile.write(
'User entered URL should look like: http://localhost:8080/?fake_user=johndoe')
def main():
try:
server = BaseHTTPServer.HTTPServer(('', 8080), RequestHandler)
print 'Starting server. Use Control+C to stop.'
server.serve_forever()
except KeyboardInterrupt:
print 'Shutting down server.'
server.socket.close()
if __name__ == '__main__':
main()
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。