没有 dtend 作为规则参数?

No dtend as rrule parameters?

我试图用 dateutil.rrule 找到给定 datetime 之前的第一个匹配项。

尝试 1:

我的第一次尝试是:

dtstart = datetime(2010, 1, 1, 0, 00)
myRule = rrule(freq=WEEKLY, dtstart=dtstart)
result = myRule.before(dtstart)

它不起作用,变量结果将等于 None

如果我是对的,当您执行 rrule 的方法 before 时,您将不幸地搜索传递给参数 dtstartdatetime 之后。 所以基本上,我以前的代码可以表示为那种时间线:

[A区][TARGET][B区][dtstart][C区]

并且在那条时间线中,只有 [zone C] 被解析,所以我的目标从未被发现。

尝试 2:

我的第二次尝试有点效果,但真的很丑...目的是将 [dtstart] 移动到 [TARGET] 之前,以获得如下所示的时间轴:

[A区][dtstart][B区][TARGET][C区]

为此,我必须找出一个 dtstart 肯定是在目标之前而不是太远以避免性能问题。所以[zone B]必须存在,但必须越短越好。

seekdt = datetime(2010, 1, 1, 0, 00)
startdt = seekdt - timedelta(days=7) # While my rrule.freq is WEEKLY and the interval is 1 (default), I'm sure that my startdt will be before my target if I shift it by 7 days
myRule = rrule(freq=WEEKLY, dtstart=startdt)
result = muRule.before(seekdt)

正如我所说,该解决方案真的很难看...如果我有更复杂的规则或规则集,也很难定义最佳转换。

最好的解决方案,但实际上并不存在......:'( :

如果 rrule 可以采用 dtend 而不是 dtstart,那就完美了。我可以做类似的事情 :

dtstart = datetime(2010, 1, 1, 0, 00)
myRule = rrule(freq=WEEKLY, dtend=dt)
result = muRule.before(dt)

问题:

如果没有任何简单的功能,接缝对我来说很奇怪。有什么优雅的方法可以达到我的目标吗?我的尝试 2 是最好的解决方案吗?

换句话说(同样有问题但作为练习):

如何找到 2 月的最后一个星期五,即 13 日(基于今天)?

假设 dateutils.rrule 没有提供开箱即用的功能,我认为您自己的解决方案已经很接近了,但还不够完善。您需要以正确的时间间隔倒退并计算目标日期之前的最后结果。如果您的规则未完全指定并且结果继承了原始日期的某些属性,则“正确的时间间隔”很重要。

例如,如果您的规则是 MONTHLY,您必须确保在当月或前一个月的同一天结束。这很棘手,因为前一个月甚至可能没有那一天,例如从 7 月 31 日开始倒退 1 个月会给你一个不正确的结果——在这种情况下你必须一直回到 5 月 31 日。另一个例子:从 2 月 29 日开始倒退 YEARLY 规则 - 你必须倒退 4(有时甚至 8)年。

此外,您必须确保跳过规则中指定的间隔数。对于每两周一次的规则(例如 FREQ=WEEKLY;INTERVAL=2),您必须及时返回 2 周,否则您的结果将出现在错误的一周。

另一个需要注意的陷阱是区间可能为空。要采用您的示例,您可能需要倒退数周才能找到匹配 FREQ=WEEKLY;BYDAY=FR;BYMONTHDAY=13 的日期。你必须为此做好准备并继续向后走,直到找到一个非空的。

我对 dateutil.rrule 不熟悉,所以这里有一些伪代码应该可以为您指明方向:

rule = … // your rule
target = … // the pivot date
dtstart = target

DO 
  DO
    SWITCH(rule.freq)
       CASE YEARLY ->
            dtstart = same month, same day of month rule.interval years before old dtstart
            BREAK
       CASE MONTHLY:
            dtstart = same day of month rule.interval months before old dtstart
            BREAK
       CASE WEEKLY:
            dtstart = 7 * rule.interval days before old dtstart
            BREAK
       CASE DAILY:
            dtstart = rule.interval days before old dtstart
            BREAK
  UNTIL dtstart is a valid date

  rule.dtstart = dtstart
  candidate = rule.before(target)
UNTIL candidate is a date

// candidate is your result

请注意,规则的指定方式很可能不会产生单一结果。因此,如果您的代码接受用户输入,您应该为此做好准备并避免无限循环。

另一种处理继承属性的方法是在某些字段不存在时调整规则。例如,对于没有 BYMONTHDAYMONTHLY 规则,您可以将 rule.bymonthday 设置为目标日期的月日。但是您必须设置的字段因 FREQ 而异,因此有其自身的缺陷。