假设我想得到什么时候用规则来庆祝生日。然后,除leap日外,YEARLY的频率工作正常。实际上只有每四年一次。
有什么办法可以直接用rrule处理吗?
from datetime import datetime
from dateutil.rrule import rrule, YEARLY
n = 1
print(list(rrule(freq=YEARLY, count=n + 1, dtstart=datetime(1990, 4, 28))))
print(list(rrule(freq=YEARLY, count=n + 1, dtstart=datetime(1992, 2, 29))))
给予
[datetime.datetime(1990, 4, 28, 0, 0), datetime.datetime(1991, 4, 28, 0, 0)]
[datetime.datetime(1992, 2, 29, 0, 0), datetime.datetime(1996, 2, 29, 0, 0)]
leap日甚至没有提到的事实in the docs让我想知道这是否仅仅是个错误。
这可能会有所帮助,但仅限2月28日:
from datetime import datetime
from dateutil.rrule import rrule, YEARLY
n = 5
bday = datetime(1990, 4, 28)
print(list(rrule(freq=YEARLY,
byyearday=bday.timetuple().tm_yday,
count=n + 1,
dtstart=bday)))
bday = datetime(1992, 2, 29)
print(list(rrule(freq=YEARLY,
byyearday=bday.timetuple().tm_yday,
count=n + 1,
dtstart=bday)))
给予
[datetime.datetime(1990, 4, 28, 0, 0), datetime.datetime(1991, 4, 28, 0, 0), datetime.datetime(1992, 4, 27, 0, 0), datetime.datetime(1993, 4, 28, 0, 0), datetime.datetime(1994, 4, 28, 0, 0), datetime.datetime(1995, 4, 28, 0, 0)]
[datetime.datetime(1992, 2, 29, 0, 0), datetime.datetime(1993, 3, 1, 0, 0), datetime.datetime(1994, 3, 1, 0, 0), datetime.datetime(1995, 3, 1, 0, 0), datetime.datetime(1996, 2, 29, 0, 0), datetime.datetime(1997, 3, 1, 0, 0)]
答案 0 :(得分:4)
这是设计使然,实际上在the rrule documentation的注释中被突出提及:
根据RFC第3.3.10节,重复发生日期属于无效日期 时间会被忽略而不是被强迫:
重复规则可能会生成无效的重复实例 日期(例如2月30日)或不存在的本地时间(例如,美国东部时间上午1:30) 将当地时间提前1个小时的一天)。这样 重复发生的实例必须被忽略,并且不能被视为一部分 重复集。
由于1991年2月29日从不存在,因此它是无效日期,将被跳过。
这是过时的RFC 2445的局限性,后来已被RFC 5545取代,而RFC 7529由issue #285更新。 RFC 7529除其他外,将SKIP
参数添加到重复规则,该参数允许您指定OMIT
(默认),BACKWARD
或FORWARD
。 dateutil
早于RFC 7529(甚至RFC 5545),并且仍在更新中。您可以在PR #522上跟踪进度。
{{3}}中已解决此特定问题,但PR仍缺少对一个后备案例的支持,并且尚未合并(截至2018年10月)。
对于一个简单的函数,它返回每年的同一天,又回到每月的最后一天的简单情况,我建议改用relativedelta
(直到发布具有SKIP功能的版本): / p>
from dateutil import relativedelta
from datetime import datetime
def yearly_rule(dtstart, count=None):
n = 0
while count is None or n < count:
yield dtstart + relativedelta.relativedelta(years=n)
n += 1
if __name__ == "__main__":
for dt in yearly_rule(datetime(1992, 2, 29), count=5):
print(dt)
# Prints:
# 1992-02-29 00:00:00
# 1993-02-28 00:00:00
# 1994-02-28 00:00:00
# 1995-02-28 00:00:00
# 1996-02-29 00:00:00
请注意,我在规则中使用的是基准日期时间(dtstart
),而不是在以前的结果中加上1年。这样做的原因是relativedelta
有损,因此在relativedelta(years=1)
上加上datetime(1995, 2, 28)
会得到datetime(1996, 2, 28)
。