【问题标题】:dateutils rrule returns dates that 2 months apartdateutils rrule 返回相隔 2 个月的日期
【发布时间】:2016-11-14 15:53:24
【问题描述】:

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

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

【问题讨论】:

    标签: python date datetime python-dateutil rrule


    【解决方案1】:

    您遇到的问题来自 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 中查看this bug report 关于此问题。

    如果你只想要一个月的最后一天,你应该使用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
    

    【讨论】:

    • 仅供参考,有RFC 7529,它(除其他外)通过一种模式扩展RRULE,如果一天不存在,则回退到一个月的最后一天。规则FREQ=MONTHLY;RSCALE=GREGORIAN;SKIP=BACKWARD;BYMONTHDAY=30 可以做到这一点。在 2 月,这将导致 28 日(或闰年的 29 日)。不幸的是,目前还没有太多支持。
    • @Marten 真棒,不知道。我将在 dateutil 中阅读和实施。
    • @Paul 在 dateutil 中这个问题有什么进展吗?
    • @IvanVirabyan 尚未完成,但存在一些实现工作:github.com/dateutil/dateutil/issues/285github.com/dateutil/dateutil/pull/522
    【解决方案2】:

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-01
    • 2018-09-12
    • 2018-02-01
    • 2020-02-24
    • 2011-12-05
    • 1970-01-01
    • 2019-03-23
    相关资源
    最近更新 更多