首页 > 解决方案 > 没有 dtend 作为 rrule 参数?

问题描述

我试图dateutil.rrule在给定的datetime.

尝试 1:

我的第一次尝试是:

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

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

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

[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 日的最后一个星期五(基于今天)?

标签: pythondatetimepython-dateutilrrule

解决方案


假设 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

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

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


推荐阅读