4

我是 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)]

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

截止日期为现在。

当我使用:

disclosure_dates = [dt for dt in rrule(MONTHLY, dtstart=disclosure_start_date, until=disclosure_end_date)]

我每隔一个月或两个月得到日期。结果是:

>>> 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)]

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

4

2 回答 2

8

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

重复规则可能会生成具有无效日期(例如,2 月 30 日)或不存在本地时间(例如,本地时间在凌晨 1:00 向前移动一小时的一天的凌晨 1:30)的重复实例。这样的重复实例必须被忽略,并且不能被算作重复集的一部分。

由于您有一个生成每月 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)]

不幸的是,我不认为有一种符合 RFC 的方式来生成一个简单的 RRULE,它只会在必要时回退到月底(例如,你在 1 月 30 日做什么 - 你需要回退2 月,但您不想使用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)]

请注意,我专门使用了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

结果:

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
于 2016-07-24T18:21:39.267 回答
0

"datetime.datetime(2022, 1, 3, 0, 0)"

于 2022-01-18T20:22:17.517 回答