dateutils rrule devuelve fechas de 2 meses de diferencia

Soy nuevo en Python y también en el módulo dateutil . Estoy pasando los siguientes argumentos:

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

Aquí disclosure_start_date = 2012-10-31 00:00:00 que se convirtió a datetime es datetime.datetime(2012, 10, 31, 0, 0)

La fecha de finalización es a partir de ahora.

Cuando uso:

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

Obtengo las fechas para cada otro mes o 2 meses de diferencia. El resultado es:

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

    No estoy seguro de lo que estoy haciendo mal. ¿Alguien por favor puede señalar el error aquí?

    El problema con el que se enfrenta proviene del hecho de que datetime.datetime(2012, 10, 31, 0, 0) es el 31 del mes y no todos los meses tienen un 31st. Dado que el módulo rrule es una implementación de RFC 2445. Según RFC 3.3.10:

    Las reglas de recurrencia pueden generar instancias de recurrencia con una fecha no válida (por ejemplo, el 30 de febrero) o una hora local inexistente (por ejemplo, 1:30 AM en un día en que la hora local se adelanta una hora a la 1:00 AM). Tales instancias de recurrencia DEBEN ser ignoradas y NO DEBEN contabilizarse como parte del conjunto de recurrencias.

    Ya que tiene una regla mensual que genera el 31 de un mes, saltará todos los meses con 30 días o menos. Puede ver este informe de error en dateutil sobre este problema.

    Si solo desea el último día del mes, debe usar el argumento 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)] 

    Desafortunadamente, no creo que exista una manera compatible con RFC de generar un RRULE simple que solo retroceda al final del mes si es necesario (por ejemplo, ¿qué hace con el 30 de enero? Necesita un respaldo) para febrero, pero no quieres usar bymonthday=-2 porque eso te dará el 27 de febrero, etc.).

    Alternativamente, para una regla mensual simple como esta, una mejor opción es probablemente usar simplemente relativedelta , que se remonta al final del mes:

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

    Tenga en cuenta que específicamente utilicé cdate = dtstart + ii * rd , no desea simplemente mantener un “conteo en ejecución”, ya que quedará vinculado al mes más corto que haya visto el conteo:

     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 

    Resultado:

     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