在构建日历应用程序时,我应该在数据库中存储日期或重复规则吗?

我正在构建一个日历网站( ASP.NET MVC )应用程序(想想outlook的简单版本),我想开始支持重复出现的日历事件(每月,每年等)

现在我在我的实际日期存储,但我想知道是否,重复,是否有意义继续存储日期(有一些明显的截止),或者我应该存储重现选项并在运行中生成日期。

它让我思考Outlook,谷歌邮件等是如何做这个或任何其他支持重复日历项目的服​​务。

对此有什么建议吗?

将您的数据分为两部分:“规范”数据(重复规则)和“服务”(生成日期;除了重新生成之外的只读数据)。 如果规范数据发生变化,则在该点重新生成“服务”数据。 对于无限重复,保留一定数量的实例并在用完时生成更多实例(例如,如果用户查看其2020年的日历)。

如果你有无限的处理器速度,你只需要规范数据 – 但实际上,在每个页面视图上对所有重复规则进行所有日期/时间处理可能太耗时…所以你交易关闭一些存储(和复杂性)以保存重复计算。 与大量事件所需的计算相比,存储通常非常便宜。 如果您需要存储事件的日期,这非常便宜 – 您可以轻松地使用4字节整数来表示日期,然后从中生成完整的日期/时间,假设您的重复都是基于日期的。 对于基于时间的重现(例如“每三个小时”),您可以使用完整的UTC时刻 – 只要您可能需要,8个字节将代表相当精确的分辨率。

您需要注意保持有效性 – 如果定期会议今天发生变化,那么在过去发生变化时不会发生变化…因此您可能希望获得有关何时实际发生重复的规范只读数据。 显然你不会希望永远保持过去,所以你可能希望“垃圾收集”事件超过几年,这取决于你的存储限制。

您可能还需要能够在每次出现的基础上添加注释和例外(例如,“由于公共假期而未在今天开会”或“移至下午4点”)。 当你改变复发时,这变得非常有趣 – 如果你将“每个星期一”改为“每个星期二”,你是否保留了例外情况? 当你从“每天”改为“每周”时,你如何匹配exception? 这些不是直接关于存储的问题 – 但存储决策将影响实施您决定的任何策略是多么容易。

我必须构建一个与调度一起工作的系统,我们做了两个。 这就是我们所拥有的

  • 一组跟踪计划的表。
  • 一个表,跟踪以前的计划实例(实际发生时)
  • 一个跟踪上一个和下一个实例的表(当下一个项目将根据上次发生时)你不需要这个表,但是我们使用它,因为否则你会经常计算一个项目是否应该现在正在发生

通过调度,事情变得非常棘手,因为您必须记住,在任何时间点,时间表都可能发生变化。 此外,当您的应用程序未运行时,项目可能到期,并且当它再次启动时,您需要知道如何识别过期项目。

此外,我们确保跟踪实际时间表的表格是独立的。 这样做的原因是那些是系统中最复杂的表集合,我们希望能够重用它们,以便它们可以用于需要调度的不同事物。 例如发送管理员电子邮件,发送通知以及清理日志文件等服务器维护。

您需要单独处理事件和事件。

事件明智:对于事件,您需要存储复发规则(可以是rfc5545指定的规则,但也可以是rfc5545中的rdate等明确的日期集),但也有exception(参见rfc5545的exdate和rfc2445中的exrule) 。 您还需要跟踪这些规则的变化:rdate,exdate的变化在将来发生时没有问题,在过去的日期会被忽略。 由于影响之前的出现,rrule的变化更加棘手。 我个人的偏好是为旧的和新的rrule添加一个特定的属性,以指定它们各自的开始和结束日期。

如果事件的时间跨度有限(比如COUNT或UNTIL属性),则应将其开始和结束存储在表中,以便更容易查询事件(特别是在预先计算的时间窗口之外查找事件时(见下文),可以帮助减少重新计算的事件数量)。

发生故障:对于出现的情况,您应该将实例存储在现有的预定义窗口内(例如+/- 6个月或12个月并定期计算)并保留此记录以允许重新计算,如果您的用户希望进一步查看未来(表演问题)。 您还应该考虑计算索引(RECURRENCE-ID)以帮助更容易地找到下一个匹配项。

在后端更少,但在前端更多,你还应该跟踪tzid更改,以询问用户是否在某个给定的tzid上安排的事件,如果它要保持在当前时区,则需要更新(想想萨摩亚岛上有人安排在2011年12月30日星期五举行会议,然后国家决定这一天不存在),同样地,你可以问一下在夏令时期间发生的事件是否意味着“从不发生“或”发生两次“(更多关于此主题)

注意:您可能需要考虑在rfc5545中根据recurence规则定义的支持,并且还要添加对宗教重复规则的支持( 请参阅USNO日历介绍或来自E. Reingol和N的印刷“Calendrical Calculations”(第三版) 。德肖威茨)

由于您询问现有实现,您可以轻松检查sunbird(sqlite)或Apple开源日历和联系服务器的数据库模式,这是caldav服务器的现有开源项目的更完整列表(可能是其中的一部分)你在寻找)可以在这里 )

我肯定会使用你的第二个选择。 使用不同的重复选项,单独存储并动态计算。 存储所有这些日期将是一大堆不必要的数据。

这是一个很好的答案来赞美你的问题。
存储重复事件的数据结构?

另外,作为旁注。 我已经开始将所有内容存储为UTC时间,以便在需要使用多个时区时有一个共同的基线。

您需要确保存储其中一些。 用户可能会编辑其中一个事件,而其他事件保持不变(您可能会在某些日历中遇到问题:“您要编辑所有定期事件还是仅编辑此事件?”,即Windows Mobile)。

您还可能希望存储过去的事件,而不是在用户删除定期事件时将其删除。

如果您存储所有其他人或生成它们是一个实现细节。 如果可能的话,我更愿意生成它们。

在任何情况下,您都希望每个事件都存储一些周期性事件的ID,另外还有一些标志告诉您事件是否稍后被修改。 或者在更复杂的方法中,每个事件属性的标志告诉,如果它是默认值(来自重复事件)或者是否为此特定实例修改了它。 当用户决定编辑定期事件时,您将需要此function。

我在几年前制作的网络应用程序中遇到了类似的问题(现在可能有更好的方法:))。 我想要包含一个调度程序,它具有周期性事件,处理时间,日期,周,月,年和exception的所有function,因此我可以使用以下规则:

1)每天上午10点,星期三除外

2)每2小时,每天最多4次迭代

3)每个月的第一个星期一

等等..

存储重复的日期/时间是可能的,但不灵活。 当“最大值”出现时,事件的每次迭代都会发生变化。 你看多远了?

最后,我编写了一个自定义调度类,可以读取和写入字符串。 这是存储在数据库中的字符串,然后可以调用一个简单的函数来查找下一次出现的时间。

请注意,大多数回复倾向于保存生成的数据。 但请确保考虑您的用例。

回到过去,我的服务器受到io的限制,很多cpu什么都不做。 现在你有ssd(如果你能买得起,否则你的左手有一个旧的旋转高清),但请注意核心数量也增加了。

这些计算的好处在于,您可以轻松地将它们拆分并将它们分配给您的多个核心,甚至是本地网络中的一些廉价服务器。 通常比设置nosql集群或以完整的数据库集群方式便宜。

替代方案也可能是缓存,只需缓存日历视图,无需每次更改时都进行所有计算。

但正如所说,这取决于你的用例。 如果您有时间而不是做出决定,请不要只遵循上述答案,而是自己计算。