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

Posted

技术标签:

【中文标题】在构建日历应用程序时,我应该在我的数据库中存储日期或重复规则吗?【英文标题】:Should I store dates or recurrence rules in my database when building a calendar app? 【发布时间】:2011-05-13 11:55:50 【问题描述】:

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

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

这让我想到了 Outlook、谷歌邮件等如何做到这一点或任何其他支持定期日历项目的服​​务。

对此有什么建议吗?

【问题讨论】:

【参考方案1】:

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

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

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

另一种选择也可能是缓存,只需缓存您的日历视图,无需在每次没有任何变化时都进行所有计算。

但正如所说,这取决于您的用例。不要只是按照上面的答案去做,如果你有时间的话,你自己计算一下,然后再做决定。

【讨论】:

【参考方案2】:

您需要分别处理事件和事件。

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

如果事件的时间跨度有限(比如存在 COUNT 或 UNTIL 属性),则应将其开始和结束存储在表中,以便更轻松地查询事件(尤其是在查找超出预先计算的时间窗口的事件时(见下文) ),它可以帮助减少要重做计算的事件数量。

明智的发生: 对于发生的情况,您应该将实例存储在当前的预定义窗口内(例如 +/- 6 个月或 12 个月并定期计算)并保留记录,以便在您的用户希望在未来进一步查看时重新计算(例如性能问题)。您还应该考虑计算索引 (RECURRENCE-ID) 以帮助更轻松地查找下一个事件。

后端少一些,前端多一些,您还应该跟踪 tzid 更改,以询问用户是否安排在给定 tzid 上的事件是否打算停留在它的当前时区需要更新(想想萨摩亚岛的某个人在该国决定这一天不存在之前于 2011 年 12 月 30 日星期五安排了一次会议),同样您可以询问在夏令时发生的事件是否意味着“永远不会发生”或“发生两次”(更多关于这个话题here)

注意: 您可能需要考虑超出 rfc5545 中定义的重复规则的支持,并添加对宗教重复规则的支持(see USNO introduction to calendars 或打印“日历计算”(第三版)来自 E. Reingol 和 N. Dershowitz)。

由于您询问现有实现,您可以轻松检查 sunbird (sqlite) 或 Apple open source Calendar and Contacts Server 的数据库架构,这是 caldav 服务器现有开源项目的更完整列表(这可能是您的一个子集)正在寻找)可用here)

【讨论】:

【参考方案3】:

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

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

如果您存储所有其他内容或生成它们,则属于实现细节。如果可能的话,我更愿意生成它们。

在任何情况下,您都希望在每个事件中存储一些重复事件的 ID,加上一些标志,告诉您该事件是否在以后被修改。或者在更复杂的方法中,每个事件属性的标志告诉它是默认值(来自重复事件)还是针对此特定实例进行了修改。当用户决定编辑重复事件时,您将需要它。

【讨论】:

【参考方案4】:

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

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

不过,您需要小心保持有效性 - 如果定期会议今天发生变化,那么过去发生 时它不会改变......所以您可能希望拥有有关实际发生重复时间的规范只读数据。显然,您不希望它永远保留过去,因此您可能希望“垃圾收集”超过几年的事件,具体取决于您的存储限制。

您可能还需要能够逐次添加注释和例外情况(例如,“由于公共假期,今天没有举行会议”或“移至下午 4 点”)。当您更改重复时,这变得真的有趣 - 如果您将“每个星期一”更改为“每个星期二”,您是否保留例外情况?当您从“每天”变为“每周”时,您甚至如何匹配异常?这些不是直接与存储有关的问题 - 但存储决策将影响实施您决定的任何策略的难易程度。

【讨论】:

甜蜜,这是我第一次阅读/注意到 Jon Skeet 为我自己的目的的答案 :-) 请原谅题外话。【参考方案5】:

我必须建立一个可以处理日程安排的系统,而我们两者都做到了。这就是我们所拥有的

一组跟踪计划的表格。 一个表,用于跟踪计划的先前实例(它们实际发生的时间) 一个跟踪最后一个实例和下一个实例的表(下一个项目何时根据上一次发生)您不需要此表,但我们使用了它,否则您将不断计算是否一个项目现在应该正在发生

使用日程安排,事情会变得非常棘手,因为您必须记住,日程安排随时可能发生变化。此外,当您的应用程序未运行时,某个项目可能已到期,当它再次启动时,您需要知道如何识别过期项目。

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

【讨论】:

【参考方案6】:

几年前我在一个网络应用程序中遇到了类似的问题(现在可能有更好的方法:))。我想包含一个调度程序,它具有重复事件的所有功能,处理时间、天、周、月、年和异常,这样我就可以有如下规则:

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

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

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

等等。

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

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

【讨论】:

但我需要的不仅仅是下一次出现 @ooo,但编写良好的日程安排类可以做到这一点。给定当前日期,它可以计算下一次出现,即无限期之后的那个。 when() 函数的输入应该是“现在”,它可以返回下一个发生日期。鉴于您可以通过将返回的日期反馈给when() 或通过该函数返回整个序列来计算任意数量。【参考方案7】:

我肯定会使用您的第二个选项。使用不同的重复选项,单独存储并即时计算。存储所有这些日期将是大量不必要的数据。

这是一个很好的回答来补充您的问题。Data structure for storing recurring events?

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

【讨论】:

如果您为每个有重复的用户的每个重复存储单独的日期,它可能会很快变成大量数据。 +1 有利于存储重复。考虑用户移动重复事件实例的用例:她可以选择将移动应用于所有事件、所有后续事件、仅此事件等。

以上是关于在构建日历应用程序时,我应该在我的数据库中存储日期或重复规则吗?的主要内容,如果未能解决你的问题,请参考以下文章

Swift FSCalendar 在表格视图的日历显示事件中选择日期

如何使用 laravel 在 jquery fullcalendar 中存储拖动的事件

Tapku 日历上标记的日期错误

如何在我的完整日历议程中显示今天的日期

谷歌日历图表日期弹出(工具提示)问题

使 Span 打开和关闭 In-Line Bootstrap 日期选择器日历