当存在 BYMONTH、BYWEEKNO、BYYEARDAY 中的一个以上时,YEARLY RRULE 解释

YEARLY RRULE interpretation when more than one of BYMONTH, BYWEEKNO, BYYEARDAY are present

我目前正在为 RFC 5545 的解释细节而苦苦挣扎。Section 3.3.10 声明基本上,所有 BYxxx 字段都显示 expanding 行为,除了BYDAY,它有特定的规则,具体取决于 BYxxx 个字段实际存在。

但是,我不清楚对于指定 多于一个 RRULE 到底应该发生什么 BYMONTHBYWEEKNO, BYYEARDAY}。在我看来,第 3.3.10 节中的 table 允许解释我必须分别 展开 每个规则部分,即构建三个结果集的并集来自

  1. 展开BYMONTHBYMONTHDAY
  2. 展开BYWEEKNO
  3. 展开BYYEARDAY

一直尊重解释 BYDAY 行为的注释 2。结果可能有点反直觉(当然,用户不太可能指定此类规则)。但是至少有一种实现 claims to do it this way:

For the YEARLY frequency, the BYMONTH, BYWEEKNO, and BYYEARDAY rules are expanded separately from each other if specified.

其他实现,例如 http://recurrence-expansion-service.appspot.com/ 或 Google Calendar,如果存在多个规则部分,则似乎使用 limiting 策略:

  1. 展开BYMONTH/BYMONTHDAY
  2. 然后使用BYWEEKNOBYYEARDAY
  3. 进行限制

有效地创建交集而不是并集。该方法似乎与第 3.3.10 节规定的规则部分的申请顺序有些一致:

If multiple BYxxx rule parts are specified, then after evaluating the specified FREQ and INTERVAL rule parts, the BYxxx rule parts are applied to the current set of evaluated occurrences in the following order: BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR, BYMINUTE, BYSECOND and BYSETPOS; then COUNT and UNTIL are evaluated.

但是,将此解释为规则部分是 limiting 而不是 expanding 在我看来与expand/limit table 显示在 page 44 of the standard

因此我的问题是:

RFC5545 是否实际上明确指定如何解释 BYMONTHBYWEEKNOBYYEARDAY 中的多个同时存在?如果是这样,它在哪里说明? 如果标准在这方面实际上不清楚,是否有处理这种情况的“事实上的标准”首选方法?

Does RFC5545 actually unambiguously specify how the simultaneous presence of more than one of BYMONTH, BYWEEKNO and BYYEARDAY is to be interpreted?

嗯,是的,在您在问题中引用的摘录中:它们按顺序应用于当前评估的事件集

术语当前评估出现的集合是句子的关键部分。例如,让我们采用 BYMONTH 和 BYYEARDAY 的规则。

  1. 首先计算 BYMONTH,生成一组出现次数(在 YEARLY 频率上扩展,即集合中每年可以出现多次)
  2. 然后 BYYEARDAY 仅应用于那些出现,即与 BYMONTH 匹配的出现。不考虑与 BYMONTH 不匹配的每一年日期,因为根据定义,它不是步骤 1 中计算的当前事件集的一部分。

expand/limit table 在这种情况下是无关紧要的。 RFC 说得很清楚:

"The table below summarizes the dependency of BYxxx rule part expand or limit behavior on the FREQ rule part value."

table总结了BYxxx规则部分对其他BYxxx规则部分的行为。

因此它无疑是一个十字路口。这样想:如果结果是并集,就没有办法写出 "every day of the 5th week of the year that are in February" (FREQ=YEARLY;BYWEEKNO=5;BYMONTH=2).

这样的规则

为了实现 union,RFC 定义了一个 recurrence set,它简单地组合了多个 RRULE、RDATE 和 EXDATE(尽管对于出于某些原因,RFC 指出 RRULE 不应 出现不止一次)。

And if the standard is actually unclear in this regard, is there a “de-facto standard” preferred way of dealing with this situation?

我是 php-rrule which is a port of Python dateutil 的维护者,这就是这两个版本处理这种情况的方式。据我所知,Javascript 和 Ruby 库也是这样工作的。我不知道那里的所有其他库,但 RFC 无论如何都非常清楚,正如我希望我在上面展示的那样。

编辑:回答您对 BYDAY 的评论

table的注释2确实比较混乱,这里稍微说明一下。 BYDAY 之所以特殊,是因为它有两种语法:简单的一种使用日期名称(例如 MOTU 等),而 "complicated" 一种使用名称当天的 集合中的位置。例如 1MO 表示 "the first Monday" 和 -1MO "the last Monday"。然而问题是"the first/last Monday of what?".

  • 当使用 YEARLY 频率和 BYDAY=1MO,2MO 时,它是 "the first and the second Monday of the year",当使用 MONTHLY 时它是 "the first and the second Monday of the month" 等等,这非常简单。
  • 然而,当使用 YEARLY 频率 BYMONTH=2,3 时,规则实际上意味着 "every February and March" 所以问题是:你应该得到每个第一个和2 月和 3 月的第二个星期一(展开),或每年 的第一个和第二个星期一 发生在二月和三月的年份 - 即空集,因为两者都在一月(限制)。答案是通过实际将其视为 "MONTHLY" 频率(即使它是 YEARLY)来执行 "special expand",因此 BYDAY=1MO,2MO 的含义更改为“二月的每个第一个和第二个星期一AND 三月的第一个和第二个星期一`。

引用 RFC 的相关部分

The numeric value in a BYDAY rule part with the FREQ rule part set to YEARLY corresponds to an offset within the month when the BYMONTH rule part is present, and corresponds to an offset within the year when the BYWEEKNO or BYMONTH rule parts are present.

老实说,我不确定为什么 BYDAY 有这种特殊情况,但无论如何它应该如何表现是很清楚的。