我的应用需要处理日历信息(包括单一事件、循环事件等)。为了更容易地与其他应用程序进行接口,我认为基于iCalendar格式(字段、关系、约束)直接创建我的数据库模式是一个好主意,这样我可以通过ORM获得与iCalendar兼容的对象,并在需要时轻松地公开它们。
我知道RFC可用,但由于我目前不使用其中的所有附加信息,所以它有点复杂。
请问是否有人能指向一个更简单的来源,以便基于iCal标准创建数据库模式(即iCal条目的字段/字段名及其关系列表)?
谢谢!
我已经完成了这个任务(仅适用于VEvents,不支持TODO事项或日志条目等)。 我的实现如下所示(删除了与问题无关的列):
-- One table for each event. An event may have multiple rRules.
Create Table [vEvent]
(vEventID Integer Identity(1, 1) Not Null
Constraint [vEvent.pk]
Primary Key
Clustered
,title nVarChar(200) Not Null);
-- One table for rRules.
-- My application does NOT support the "bySetPos" rule, so that is not included.
Create Table [rRule]
(rRuleID Integer Identity(1, 1) Not Null
Constraint [rRule.pk]
Primary Key
Clustered
,vEventID Integer Not Null
Constraint [fk.vEvent.rRules]
Foreign Key
References [vEvent] (vEventID)
On Update Cascade
On Delete Cascade
,[class] varChar( 12) Not Null Default('public')
,[created] DateTime Not Null Default(getUTCDate())
,[description] nVarChar(max) Null
,[dtStart] DateTime Not Null
,[dtEnd] DateTime Null
,[duration] varChar( 20) Null
,[geoLat] Float Null
,[geoLng] Float Null
,[lastModified] DateTime Not Null Default(getUTCDate())
,[location] nVarChar(max) Null
,[organizerCN] nVarChar( 50) Null
,[organizerMailTo] nVarChar( 100) Null
,[seq] Integer Not Null Default(0)
,[status] varChar( 9) Not Null Default('confirmed')
,[summary] nVarChar( 75) Null
,[transparent] Bit Not Null Default(0)
,[freq] varChar( 8) Not Null Default('daily')
,[until] DateTime Null
,[count] Integer Null
,[interval] Integer Not Null Default(1)
,[bySecond] varChar( 170) Null
,[byMinute] varChar( 170) Null
,[byHour] varChar( 61) Null
,[byDay] varChar( 35) Null
,[byMonthDay] varChar( 200) Null
,[byYearDay] varChar(3078) Null
,[byWeekNo] varChar( 353) Null
,[byMonth] varChar( 29) Null
,[wkSt] Char ( 2) Null Default('mo'));
-- Class must be one of "Confidential", "Private", or "Public"
Alter Table [rRule]
Add Constraint [rRule.ck.Class]
Check ([class] In ('confidential', 'private', 'public'));
-- Start date must come before End date
Alter Table [rRule]
Add Constraint [rRule.ck.dtStart]
Check ([dtEnd] Is Null Or [dtStart] <= [dtEnd]);
-- dtEnd and duration may not both be present
Alter Table [rRule]
Add Constraint [rRule.ck.duration]
Check (Not ([dtEnd] Is Not Null And [duration] Is Not Null));
-- Check valid values for [freq]. Note that 'single' is NOT in the RFC;
-- it is an optimization for my particular iCalendar calculation engine.
-- I use it as a clue that this pattern has only a single date (dtStart),
-- and there is no need to perform extra calculations on it.
Alter Table [rRule]
Add Constraint [rRule.ck.freq]
Check ([freq] In
('yearly'
,'monthly'
,'weekly'
,'daily'
,'hourly'
,'minutely'
,'secondly'
,'single')); -- Single is NOT part of the spec!
-- If there is a latitude, there must be a longitude, and vice versa.
Alter Table [rRule]
Add Constraint [rRule.ck.geo]
Check (([geoLat] Is Null And [geoLng] Is Null)
Or ([geoLat] Is Not Null And [geoLng] Is Not Null));
-- Interval must be positive.
Alter Table [rRule]
Add Constraint [rRule.ck.interval]
Check ([interval] > 0);
-- Status has a set of defined values.
Alter Table [rRule]
Add Constraint [rRule.ck.status]
Check ([status] In ('cancelled', 'confirmed', 'tentative'));
-- Until and Count may not coexist in the same rule.
Alter Table [rRule]
Add Constraint [rRule.ck.until and count]
Check (Not ([until] Is Not Null And [count] Is Not Null));
-- One table for exceptions to rRules. In my application, this covers both
-- exDate and rDate. I do NOT support extended rule logic here; The RFC says
-- you should support the same sort of date calculations here as are supported
-- in rRules: exceptions can recur, etc. I don't do that; mine is simply a
-- set of dates that are either "exceptions" (dates which don't appear, even
-- if the rule otherwise says they should) or "extras" (dates which do appear,
-- even if the rule otherwise wouldn't include them). This has proved
-- sufficient for my application, and something that can be exported into a
-- valid iCalendar file--even if I can't import an iCalendar file that makes
-- use of recurring rules for exceptions to recurring rules.
Create Table [exDate]
(exDateID Integer Identity(1, 1) Not Null
Constraint [exDate.pk]
Primary Key
Clustered
,rRuleID Integer Not Null
Constraint [fk.rRule.exDates]
Foreign Key
References [rRule] (rRuleID)
On Update Cascade
On Delete Cascade
,[date] DateTime Not Null
,[type] varChar(6) Not Null); -- Type = "exDate" or "rDate" for me; YMMV.
Select * From dbo.getDatesByVEventID(@id, @startDate, @endDate)
Select * From dbo.getEventsByDateRange(@startDate, @endDate, @maxCount)
实现以上内容非常有趣!
开源的 Mozilla 日历应用 Sunbird 是基于 sqlite 数据库。我刚刚下载并解压了他们的源代码,其中包含了 .sql
文件。
文件 mozilla\calendar\providers\storage\schema-7.sql
是 Sunbird 使用的模式,用于生成有效的 iCal 文件,因此它不会太糟糕。
我不需要他的解决方案中的很多列。我只需要能帮助我处理事件和重复事件的列。这是iCalendar规范的问题,而不是Chris的问题。我的解决方案仅考虑重复规则在日历限制和序列限制方面。
某些列——最重要的是dtStart和dtEnd——属于VEVENT,而不属于RRULE,但Chris将它们放在了RRULE中。这让我感到困惑。 VEVENT:https://www.rfc-editor.org/rfc/rfc5545#section-3.6.1 RRULE:https://www.rfc-editor.org/rfc/rfc5545#section-3.3.10
我还需要找出如何包含可能具有各种模式的时间表。例如,一个事件可能每周五从下午6点到晚上9点发生,但也可能在五一节全天发生。这需要对dtStart和dtEnd具有灵活性。因此,我创建了一个包含表“SCHEDULE”,它与EVENTS维护多对多关系,而EVENTS与RRULES具有包含关系。
from app import db
from sqlalchemy import CheckConstraint
from sqlalchemy.ext.associationproxy import association_proxy
class Schedule(db.Model):
id = db.Column(db.Integer, primary_key=True)
subtypes_relation = db.relationship('Event', secondary=schedule_event_association,
backref=db.backref('Schedule', lazy='dynamic'))
schedule_event_association = db.Table(
'schedule_event_association',
db.Column('schedule_id', db.Integer, db.ForeignKey('schedule.id')),
db.Column('event_id', db.Integer, db.ForeignKey('event.id')))
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
dt_start = db.Column(db.DateTime) # start time
dt_end = db.Column(db.DateTime) # end time
tz_id = db.Column(db.String) # Time Zone
recurrence_rule = db.Column('RecurrenceRule_id', db.Integer, db.ForeignKey('RecurrenceRule.id'))
# Start date must come before End date
CheckConstraint('dtEnd is NULL OR dtStart <= dtEnd', name='Valid: Time Period')
class RecurrenceRule(db.Model):
id = db.Column(db.Integer, primary_key=True)
# Frequency Type
freq = db.Column(db.String(8), nullable=False, default='weekly') # type of recurrence
# Calendar-Based Rules
byDay = db.Column(db.String(35)) # List of Day of the Week
# "mo,tu,we" for weekly
# "+2MO, -1MO" = second monday, last monday for yearly or monthly
byMonthDay = db.Column(db.String(200)) # List of Day of the Month
# +1,-1"
# Only for Monthly or Yearly
byYearDay = db.Column(db.String(3078)) # List Day of the Year
#"+1, -1"
# Only for yearly
# Take care with leap years
byWeekNo = db.Column(db.String(353)) # Which week of Mon`enter code here`th
# "+5, -3" for fifth and third-to-last
# Only for yearly
byMonth = db.Column(db.String(29)) # Month of year.
# Sequence-Based Rules
until = db.Column(db.DateTime) # last day of occurence
count = db.Column(db.Integer) # number of occurences
interval = db.Column(db.Integer, nullable=False, default=1) # interval between recurrences
bysetpos = db.Column(db.String()) # Specifies specific instances of recurrence
# Valid Values
CheckConstraint(freq in ('yearly', 'monthly', 'weekly', 'daily', 'single'),
name='Valid: Frequency Value')
CheckConstraint(interval > 0, name='Valid: Positive Interval')
CheckConstraint(byDay is not None and freq in ('daily', 'yearly', 'monthly'))
CheckConstraint(byWeekNo is not None and freq in ('yearly', 'monthly'))
CheckConstraint(byYearDay is not None and freq == 'yearly')
# Until and Count may not coexist in the same rule.
CheckConstraint(not (until is not None and count is not None),
name='Valid: Not Both Until and Count')
你可以尝试使用这个文档来处理苹果日历。你可以直接将字段复制到数据库表中。 http://developer.apple.com/library/mac/#DOCUMENTATION/AppleApplications/Reference/SyncServicesSchemaRef/Articles/Calendars.html
如果你使用Java,ical4j提供了模式和接口之间的粘合剂。如果你不使用Java,生成事件和结构的源代码算法将为实现提供很好的帮助。
iCal是一款由苹果公司开发的应用程序,遵循当前已知的Icalendar标准(前身为Vcalendar)。我认为维基百科entry上提供了您需要的所有信息,并以简单易懂的格式呈现,但如果需要更多指导,请随时提出!