2

我正在使用 python 的 dateutil 模块来解析我日历中的重复规则。以下规则出现问题:

从 dateutil.rrule 导入 rrulestr

def test():
    rrule = 'FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=30;UNTIL=20180331T2359'
    dtstart = datetime.datetime(2018, 1, 1, 18, 0)
    dates = list(rrulestr(rrule + ';UNTIL=', dtstart = dtstart ))

这导致以下输出(缺少二月):

datetime: 2018-01-30 18:00:00
datetime: 2018-03-30 18:00:00

这是dateutil模块中的错误,我应该如何修复它?还是我做错了什么?

4

1 回答 1

2

根据我对这个等效问题的回答,这是正在实施的 iCalendar RFC 的一个故意功能dateutil,因为它dateutil 实现了 RFC 2445 并且不支持更新的 RFC 5545 的所有(或大部分)功能。RFC 2445 的相关部分:

重复规则可能会生成具有无效日期(例如,2 月 30 日)或不存在本地时间(例如,当地时间在凌晨 1:00 向前移动一小时的一天的凌晨 1:30)的重复实例。这样的重复实例必须被忽略,并且不能被算作重复集的一部分。

缺少二月是因为2018-02-30日期无效(实际上是 RFC 中指定的示例)。

需要注意的一点是,此拉取请求实现了您想要的功能,但它(在撰写本文时)当前被阻止等待SKIPin的支持BYWEEKNO。合并后,您将能够修改您的 RRULE:

rrule = ('FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=30;UNTIL=20180331T2359;'+
         'SKIP=BACKWARD;RSCALE=GREGORIAN')

在此之前,您最好的选择可能是使用 aBYMONTHDAY=28然后将 a 添加relativedelta(day=30)到结果中,例如:

from dateutil.rrule import rrule, MONTHLY
from dateutil.relativedelta import relativedelta

def end_of_month(dtstart, until):
    rr = rrule(freq=MONTHLY, interval=1, bymonthday=28,
               dtstart=dtstart, until=until)

    for dt in rr:
        yield dt + relativedelta(day=30)

这是有效的,因为 28 日存在于所有月份(因此rrule将始终生成它)并且relativedelta具有您正在寻找的“月底倒退”行为。为了 100% 安全,您可以选择bymonthday=1,在这种情况下是等效的。

于 2018-02-08T08:32:44.810 回答