2016-07-12 15 views
1

Ich bin neu in Python und auch dateutil Modul. Ich bin vorbei die folgenden Argumente:dateutils Rulle gibt Daten, die 2 Monate auseinander

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

Hier die zu konvertierende Zeitangabe ist datetime.datetime(2012, 10, 31, 0, 0)

Enddatum ab jetzt ist.

Wenn ich:

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

ich die Daten für jeden anderen Monat oder 2 Monate auseinander zu bekommen. Das Ergebnis ist:

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

Ich bin mir nicht sicher, was ich falsch mache. Kann jemand bitte hier auf den Fehler hinweisen?

Antwort

2

Das Problem, auf das Sie stoßen, kommt von der Tatsache, dass datetime.datetime(2012, 10, 31, 0, 0) der 31. des Monats ist, und nicht alle Monate einen 31. haben. Da das rrule Modul ist eine Implementierung von RFC 2445. Per RFC 3.3.10:

Recurrence Regeln kann Wiederholung Instanzen mit einem ungültigen Datum (zB 30. Februar) erzeugen oder nicht vorhandene lokale Zeit (zB 01.30 an einem Tag, an dem die Ortszeit um 1 Uhr morgens um eine Stunde nach vorne verschoben wird). Solche Wiederholungsinstanzen MÜSSEN ignoriert werden und dürfen NICHT als Teil des Wiederholungssatzes gezählt werden.

Da Sie eine Monatsregel haben, die den 31. eines Monats generiert, werden alle Monate mit 30 oder weniger Tagen übersprungen. Sie können this bug report in dateutil zu diesem Problem sehen.

Wenn Sie nur den letzten Tag des Monats wollen, sollten Sie das bymonthday=-1 Argument:

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

Leider, ich glaube nicht, dass es eine RFC-kompatible Art und Weise eine einfache RRULE zu erzeugen, die nur fällt zurück zum Ende des Monats wenn und nur wenn es notwendig ist (zB was machst du mit dem 30. Januar - du brauchst einen Fallback für Februar, aber du willst nicht bymonthday=-2 verwenden weil das dir den 27. Februar geben wird, etc).

Alternativ kann für eine einfache monatliche Regel wie diese, ist eine bessere Option wahrscheinlich nur relativedelta zu verwenden, die tut fallen zurück zum Ende des Monats:

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

Bitte beachte, dass ich verwenden speziell cdate = dtstart + ii * rd , tun Sie nicht wollen nur ein „laufendes tally“ halten, so dass auf den kürzesten Monat Pin wird die tally gesehen hat:

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 

Re sult:

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 
+3

FYI, gibt es [RFC 7529] (https://tools.ietf.org/html/rfc7529), die (unter anderem) erstreckt 'RRULE' von einem Modus, der zurück zu den Fällen Letzter Tag eines Monats, wenn ein Tag nicht existiert. Die Regel 'FREQ = MONTHLY; RSCALE = GREGORIAN; SKIP = BACKWARD; BYMONTHDAY = 30' würde das tun. Im Februar würde dies zum 28. (oder 29. in Schaltjahren) führen. Leider gibt es dafür noch nicht viel Unterstützung. – Marten

+0

@Marten Super, wusste nicht darüber. Ich lese und implementiere in Dateutil. – Paul

+0

Danke Paul. Upvoted Ihre Antwort. – DrBug