dateutils rrule返回相隔2个月的日期

DrB*_*Bug 2 python datetime date rrule python-dateutil

我是Python和dateutil模块的新手.我传递了以下论点:

disclosure_start_date = resultsDict['fd_disclosure_start_date']
disclosure_end_date = datetime.datetime.now()
disclosure_dates = [dt for dt in rrule(MONTHLY, dtstart=disclosure_start_date, until=disclosure_end_date)]
Run Code Online (Sandbox Code Playgroud)

这里 disclosure_start_date = 2012-10-31 00:00:00转换为datetime是datetime.datetime(2012, 10, 31, 0, 0)

截止日期截至目前.

我用的时候:

disclosure_dates = [dt for dt in rrule(MONTHLY, dtstart=disclosure_start_date, until=disclosure_end_date)]
Run Code Online (Sandbox Code Playgroud)

我得到每隔一个月或相隔2个月的日期.结果是:

>>> list(disclosure_dates)
[datetime.datetime(2012, 10, 31, 0, 0), 
 datetime.datetime(2012, 12, 31, 0, 0), 
 datetime.datetime(2013, 1, 31, 0, 0), 
 datetime.datetime(2013, 3, 31, 0, 0), 
 datetime.datetime(2013, 5, 31, 0, 0), 
 datetime.datetime(2013, 7, 31, 0, 0), 
 datetime.datetime(2013, 8, 31, 0, 0), 
 datetime.datetime(2013, 10, 31, 0, 0), 
 datetime.datetime(2013, 12, 31, 0, 0), 
 datetime.datetime(2014, 1, 31, 0, 0), 
 datetime.datetime(2014, 3, 31, 0, 0), 
 datetime.datetime(2014, 5, 31, 0, 0), 
 datetime.datetime(2014, 7, 31, 0, 0), 
 datetime.datetime(2014, 8, 31, 0, 0), 
 datetime.datetime(2014, 10, 31, 0, 0), 
 datetime.datetime(2014, 12, 31, 0, 0), 
 datetime.datetime(2015, 1, 31, 0, 0), 
 datetime.datetime(2015, 3, 31, 0, 0), 
 datetime.datetime(2015, 5, 31, 0, 0), 
 datetime.datetime(2015, 7, 31, 0, 0), 
 datetime.datetime(2015, 8, 31, 0, 0), 
 datetime.datetime(2015, 10, 31, 0, 0), 
 datetime.datetime(2015, 12, 31, 0, 0), 
 datetime.datetime(2016, 1, 31, 0, 0), 
 datetime.datetime(2016, 3, 31, 0, 0), 
 datetime.datetime(2016, 5, 31, 0, 0)]
Run Code Online (Sandbox Code Playgroud)

我不确定我做错了什么.有人可以指出这里的错误吗?

Pau*_*aul 5

你遇到的问题来自于datetime.datetime(2012, 10, 31, 0, 0)本月31日的事实,并非所有月份都有31日.由于该rrule模块是RFC 2445的实现.根据RFC 3.3.10:

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

由于您的月度规则会生成一个月中的第31个月,因此它将跳过30天或更少天的所有月份.您可以在此问题中看到此错误报告dateutil.

如果您只想要一个月的最后一天,则应使用以下bymonthday=-1参数:

from dateutil.rrule import rrule, MONTHLY
from datetime import datetime

disclosure_start_date = datetime(2012, 10, 31, 0, 0)

rr = rrule(freq=MONTHLY, dtstart=disclosure_start_date, bymonthday=-1)
# >>>rr.between(datetime(2013, 1, 1), datetime(2013, 5, 1))
# [datetime.datetime(2013, 1, 31, 0, 0),
#  datetime.datetime(2013, 2, 28, 0, 0),
#  datetime.datetime(2013, 3, 31, 0, 0),
#  datetime.datetime(2013, 4, 30, 0, 0)]
Run Code Online (Sandbox Code Playgroud)

不幸的是,我不认为有一种符合RFC的方式来生成一个简单的RRULE,只要它是必要的(例如,你在1月30日做什么 - 你需要回退)二月,但你不想使用,bymonthday=-2因为这将给你2月27日,等).

或者,对于像这样的简单月度规则,可能只是使用更好的选项relativedelta,这可以追溯到月末:

from dateutil.relativedelta import relativedelta
from datetime import datetime

def disclosure_dates(dtstart, rd, dtend=None):
    ii = 0
    while True:
        cdate = dtstart + ii*rd
        ii += 1

        yield cdate
        if dtend is not None and cdate >= dtend:
            break


dtstart = datetime(2013, 1, 31, 0, 0)
rd = relativedelta(months=1)
rr = disclosure_dates(dtstart, rd, dtend=datetime(2013, 5, 1))

# >>> list(rr)
# [datetime.datetime(2013, 1, 31, 0, 0),
#  datetime.datetime(2013, 2, 28, 0, 0),
#  datetime.datetime(2013, 3, 31, 0, 0),
#  datetime.datetime(2013, 4, 30, 0, 0),
#  datetime.datetime(2013, 5, 31, 0, 0)]
Run Code Online (Sandbox Code Playgroud)

请注意,我专门用cdate = dtstart + ii * rd,你希望只是保持"流水账",因为这将引脚以最短一个月的讯号已经看到:

dt_base = datetime(2013, 1, 31)
dt = dt_base
for ii in range(5):
    cdt = dt_base + ii*rd
    print('{} | {}'.format(dt, cdt))
    dt += rd
Run Code Online (Sandbox Code Playgroud)

结果:

2013-01-31 00:00:00 | 2013-01-31 00:00:00
2013-02-28 00:00:00 | 2013-02-28 00:00:00
2013-03-28 00:00:00 | 2013-03-31 00:00:00
2013-04-28 00:00:00 | 2013-04-30 00:00:00
2013-05-28 00:00:00 | 2013-05-31 00:00:00
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,有[RFC 7529](https://tools.ietf.org/html/rfc7529),其中(以及其他内容)通过一种模式扩展"RRULE",如果一天可以回退到一个月的最后一天不存在.规则`FREQ = MONTHLY; RSCALE = GREGORIAN; SKIP = BACKWARD; BYMONTHDAY = 30`会做到这一点.2月份这将导致第28次(或闰年第29次).不幸的是,目前还没有太多的支持. (3认同)