根据我对这个等效问题的回答,这是正在实施的 iCalendar RFC 的一个故意功能dateutil
,因为它dateutil
实现了 RFC 2445 并且不支持更新的 RFC 5545 的所有(或大部分)功能。RFC 2445 的相关部分:
重复规则可能会生成具有无效日期(例如,2 月 30 日)或不存在本地时间(例如,当地时间在凌晨 1:00 向前移动一小时的一天的凌晨 1:30)的重复实例。这样的重复实例必须被忽略,并且不能被算作重复集的一部分。
缺少二月是因为2018-02-30
日期无效(实际上是 RFC 中指定的示例)。
需要注意的一点是,此拉取请求实现了您想要的功能,但它(在撰写本文时)当前被阻止等待SKIP
in的支持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
,在这种情况下是等效的。