我正在构建一个日历网站(ASP.NET MVC
)应用程序(类似于简化版的Outlook),我想开始支持定期发生的日历事件(例如每月、每年等)。
目前,我在我的数据库中存储实际日期,但我想弄清楚,在有重复事件的情况下,是否继续存储日期(超过某些明显的截止日期),或者应该存储重复选项并根据需要生成日期。
这让我想到了Outlook、Google Mail等如何处理这个问题,还有其他支持重复日历项目的服务。
对此有什么建议吗?
我正在构建一个日历网站(ASP.NET MVC
)应用程序(类似于简化版的Outlook),我想开始支持定期发生的日历事件(例如每月、每年等)。
目前,我在我的数据库中存储实际日期,但我想弄清楚,在有重复事件的情况下,是否继续存储日期(超过某些明显的截止日期),或者应该存储重复选项并根据需要生成日期。
这让我想到了Outlook、Google Mail等如何处理这个问题,还有其他支持重复日历项目的服务。
对此有什么建议吗?
将数据分为两部分:"规范"数据(重复规则)和 "服务" 数据(生成的日期;除再次生成以外只读)。如果规范数据有更改,就在那时重新生成 "服务" 数据。对于无限期循环,保留一些实例并在用完时生成更多(例如,如果用户查看其 2030 年的日历)。
如果您拥有无限处理器速度,您只需要规范数据 - 但实际上,在每个页面视图中进行所有重复规则的日期/时间处理可能太费时间了…因此,您会在存储方面做一些权衡(和复杂性),以节省重复计算。与所需计算量相比,存储通常非常便宜。如果您仅需要存储事件日期,那么这真的非常便宜 - 您可以轻松使用 4 字节整数来表示日期,然后从中生成完整日期/时间,假设您的重复都是基于日期的。对于基于时间的重复(例如“每三小时”),您可以使用完整的 UTC 瞬间-8 字节将其表示到您可能需要的非常好的分辨率。
但是,您需要小心维护有效性-如果定期会议今天有变化,那不会改变它过去曾经发生的时间...因此,您可能还想拥有关于重复实际发生时间的规范只读数据。显然,您不希望一直保留过去,因此根据您的存储限制,您可能需要“垃圾回收”几年前的事件。
您可能还需要按发生情况添加注释和异常信息(例如“由于公共假期,会议今天不会发生”或“已推迟到下午4点”)。当您更改重复设置时,这变得非常有趣 - 如果您将“每周一”更改为“每周二”,您是否保留异常信息?当您从“每天”更改为“每周”时,如何匹配异常信息?这些问题与存储直接无关,但存储决策将影响您决定实施任何政策的难易程度。
您需要分别处理事件和发生情况。
关于事件: 对于事件,您将需要存储重复规则(可以是类似于rfc5545中指定的rrule,也可以是rfc5545中的rdate中的一组明确日期),还需要存储异常情况(参见rfc5545中的exdate以及可能的rfc2445中的exrule)。您还需要跟踪这些规则的更改: 当rdate、exdate发生在未来且过去的日期应该忽略时,更改没有问题。当rrule更改时会更棘手,因为会影响先前的出现情况。我个人倾向于添加一个特定的属性,用于旧的和新的rrule,以指定它们各自的有效开始和结束日期。 如果事件有限定时间段(比如存在COUNT或UNTIL属性),您应该在表格中存储其开始和结束时间,以便更轻松地查询事件(特别是在寻找预先计算时间窗口之外的发生情况时,它可以帮助减少需要重新计算的事件数)。
关于发生情况: 对于发生情况,您应该在预定义的窗口范围内存储实例(例如+/-6个月或12个月,并定期计算),并记录此内容以允许重新计算,如果您的用户想要查看更远的未来(出于性能问题)。您还应该考虑计算索引(RECURRENCE-ID),以便更轻松地找到下一个发生情况。 在后端方面,您还应该跟踪tzid更改,询问用户是否需要更新安排在给定tzid上的事件(如果它是保持当前时区还是需要更新),同样,您可以问一下夏令时期间发生的事件是“永远不会发生”还是“发生两次”的情况(关于此主题的更多信息可在这里)。
注意: 您可能希望在重复规则方面支持超出rfc5545定义的内容,并添加对宗教重复规则的支持(请参见USNO日历介绍或在打印版中,请参见E. Reingol和N. Dershowitz的《日历计算》(第三版))。
既然您询问现有的实现,您可以轻松检查sunbird(sqlite)或Apple开源日历和联系人服务器的数据库模式,存在于caldav服务器的现有开源项目的更完整列表(这可能是您要寻找的子集)在此处可用。
我需要构建一个与调度相关的系统,而我们已经完成了。以下是我们所拥有的:
对于日程安排来说,情况可能非常棘手,因为您必须记住,在任何时间点,日程安排都可能会改变。此外,某个项目可能会在您的应用程序未运行时到期,当它再次启动时,您需要知道如何识别过期的项目。
另外,我们确保保留跟踪实际日程安排的表格。原因是那些是系统中最复杂的表格集合,我们希望能够重复使用它们,以便用于需要调度的不同事项,例如发送管理电子邮件、发送通知以及服务器维护(如清理日志文件)。
我肯定会选择您的第二个选项。使用不同的重复选项,将其单独存储并在运行时计算。存储所有那些日期将是一大堆不必要的数据。
这里有一个很好的答案可以补充您的问题。
用于存储重复事件的数据结构?
另外,顺便说一下。我已经开始将所有内容存储为UTC时间,这样如果您需要使用多个时区,就有了一个共同的基准线。
我在几年前制作的一个Web应用程序中遇到了类似的问题(现在可能有更好的方法:))。 我想包括一个调度程序,具有所有重复事件的功能,处理时间、天数、周数、月数、年数和异常情况,以便我可以有如下规则:
1)每天上午10点,除了星期三
2)每2小时最多进行4次迭代
3)每个月的第一个星期一
等等。
存储重复日期/时间是可能的,但不灵活。 每个事件的每次迭代都会改变“最大值”。 你要往前看多远?
最后,我编写了一个自定义调度类,可以从字符串中读取和写入。 这是存储在数据库中的字符串,然后可以调用简单的函数来查找下一个发生的时间。
when()
函数的输入应该是“现在”,它可以返回下一个事件日期。鉴于此,您可以通过将给定日期反馈到when()
中或者让该函数返回整个序列来计算尽可能多的事件。 - Moo-Juice你需要确保存储其中一些。用户可能会编辑其中一个事件,而不触及其他事件(你可能在某些日历中遇到过这个问题:“您想编辑所有重复事件还是仅此事件?”例如 Windows Mobile)。
你也可能希望存储过去的事件,并在用户删除重复事件时不将其删除。
存储所有其他事件或生成它们是实现细节。如果可能的话,我更喜欢生成它们。
无论如何,你都需要在每个事件中存储一些重复事件的ID,以及一些标志告诉你该事件是否稍后被修改。或者在更复杂的方法中,为每个事件属性设置一个标志,告诉你它是默认值(来自重复事件)还是为此特定实例修改的。当用户决定编辑重复事件时,你将需要这个。