Python dateutil - 与BYMONTHDAY选择最近的一天的bug

时间:2018-02-08 07:48:49

标签: python-dateutil rrule

我正在使用python的dateutil模块来解析我日历中的重复规则。以下问题产生了一个问题:

来自dateutil.rrule import 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 ))

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

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

这是dateutil模块中的错误,我该如何解决?或者我做错了什么?

1 个答案:

答案 0 :(得分:2)

根据我的回答on this equivalent question,这是dateutil正在实施的iCalendar RFC的故意特征,因为dateutil实现了RFC 2445并且不支持所有(或大多数)更新的RFC 5545的功能.RFC 2445的相关部分:

  

重复规则可以生成具有无效日期(例如,2月30日)或不存在的本地时间的重现实例(例如,在当地时间在凌晨1:00向前移动一小时的那天的1:30 AM)。必须忽略此类重复实例,并且不得将其视为重复集的一部分。

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

需要注意的一点是,this pull request实现了您想要的功能,但是(在撰写本文时)当前已阻止等待SKIPBYWEEKNO的支持。合并之后,您将能够修改您的RRULE:

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

在此之前,您最好的选择可能是使用BYMONTHDAY=28,然后在结果中添加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在月末有#34;倒退"你正在寻找的行为。为了100%安全,您可以选择bymonthday=1,在这种情况下它是等效的。