ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python-规则打破了leap年?

2019-10-25 00:59:25  阅读:301  来源: 互联网

标签:python-dateutil rrule python


假设我想得到什么时候用规则来庆祝生日.然后,除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的事实使我想知道这是否仅仅是一个bug.

年复一日

这可能会有所帮助,但仅适用于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)]

解决方法:

这是设计使然,实际上在the rrule documentation中的注释中被突出提及:

Per RFC section 3.3.10, recurrence instances falling on invalid dates
and times are ignored rather than coerced:

Recurrence rules may generate recurrence instances with an invalid
date (e.g., February 30) or nonexistent local time (e.g., 1:30 AM on a
day where the local time is moved forward by an hour at 1:00 AM). Such
recurrence instances MUST be ignored and MUST NOT be counted as part
of the recurrence set.

由于1991年2月29日从不存在,因此它是无效日期,将被跳过.

这是过时的RFC 2445的限制,之后的版本由RFC 5545取代,而RFC 5545RFC 7529更新.RFC 7529除其他外,将SKIP参数添加到重复规则中,该规则允许您指定OMIT(默认),BACKWARD或FORWARD . dateutil早于RFC 7529(甚至RFC 5545),并且仍在更新中.您可以在issue #285上跟踪进度.

该特殊问题已在PR #522中得到解决,但PR仍然缺少对一个后备案例的支持,并且尚未合并(截至2018年10月).

对于简单的函数返回年份返回到每月的最后一天的简单情况,我建议改为使用relativedelta(直到发布具有SKIP功能的版本):

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是有损的,因此在datetime(1995,2,28)中加上relativedelta(years = 1)会得到datetime(1996,2,28).

标签:python-dateutil,rrule,python
来源: https://codeday.me/bug/20191025/1924732.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有