Правило ли сломано на високосные дни? - PullRequest
0 голосов
/ 03 октября 2018

Предположим, я хотел узнать, когда праздновать дни рождения с помощью rule.Тогда частота ГОДА работает нормально, кроме високосных дней.Там это на самом деле только каждые 4 года.

Есть ли способ справиться с этим напрямую с помощью rurule?

from datetime import datetime
from dateutil.rrule import rrule, YEARLY

n = 1
print(list(rrule(freq=YEARLY, count=n + 1, dtstart=datetime(1990, 4, 28))))
print(list(rrule(freq=YEARLY, count=n + 1, dtstart=datetime(1992, 2, 29))))

дает

[datetime.datetime(1990, 4, 28, 0, 0), datetime.datetime(1991, 4, 28, 0, 0)]
[datetime.datetime(1992, 2, 29, 0, 0), datetime.datetime(1996, 2, 29, 0, 0)]

Тот факт, чтовисокосные дни даже не упоминаются в документах заставляет задуматься, может ли это быть просто ошибкой.

до года

Это может помочь, но только для 28 февраля:

from datetime import datetime
from dateutil.rrule import rrule, YEARLY

n = 5

bday = datetime(1990, 4, 28)
print(list(rrule(freq=YEARLY,
                 byyearday=bday.timetuple().tm_yday,
                 count=n + 1,
                 dtstart=bday)))

bday = datetime(1992, 2, 29)
print(list(rrule(freq=YEARLY,
                 byyearday=bday.timetuple().tm_yday,
                 count=n + 1,
                 dtstart=bday)))

т

[datetime.datetime(1990, 4, 28, 0, 0), datetime.datetime(1991, 4, 28, 0, 0), datetime.datetime(1992, 4, 27, 0, 0), datetime.datetime(1993, 4, 28, 0, 0), datetime.datetime(1994, 4, 28, 0, 0), datetime.datetime(1995, 4, 28, 0, 0)]
[datetime.datetime(1992, 2, 29, 0, 0), datetime.datetime(1993, 3, 1, 0, 0), datetime.datetime(1994, 3, 1, 0, 0), datetime.datetime(1995, 3, 1, 0, 0), datetime.datetime(1996, 2, 29, 0, 0), datetime.datetime(1997, 3, 1, 0, 0)]

1 Ответ

0 голосов
/ 03 октября 2018

Это сделано специально и фактически упоминается в документации о правилах в примечании, которое гласит:

В соответствии с разделом 3.3.10 RFC, случаи повторения случаются наневерные даты и время игнорируются, а не приводятся по принуждению:

Правила повторения могут генерировать случаи повторения с недопустимой датой (например, 30 февраля) или несуществующим местным временем (например, 1:30 утра в день, когдаместное время сдвигается вперед на час в 1:00 утра).Такие экземпляры повторения ДОЛЖНЫ игнорироваться и НЕ ДОЛЖНЫ учитываться как часть набора повторений.

Поскольку 29 февраля 1991 года никогда не существовало, это недопустимая дата, и она пропускается.

Это ограничение устаревшего RFC 2445 , который позже был заменен RFC 5545 , который обновляется RFC 7529 .RFC 7529, помимо прочего, добавляет параметр SKIP к правилам повторения, который позволяет указывать OMIT (по умолчанию), BACKWARD или FORWARD.dateutil предшествует RFC 7529 (и даже RFC 5545) и все еще находится в процессе обновления.Вы можете отследить ход выполнения проблемы # 285 .

Эта конкретная проблема решена в PR # 522 , но в PR по-прежнему отсутствует поддержка одного резервного варианта ине было (по состоянию на октябрь 2018 г.) объединено.

Для простого случая функции, которая возвращает один и тот же день каждого года, возвращаясь к последнему дню месяца, я рекомендую вместо этого использовать relativedelta(пока не будет выпущена версия с функцией SKIP):

from dateutil import relativedelta
from datetime import datetime

def yearly_rule(dtstart, count=None):
    n = 0
    while count is None or n < count:
        yield dtstart + relativedelta.relativedelta(years=n)
        n += 1

if __name__ == "__main__":
    for dt in yearly_rule(datetime(1992, 2, 29), count=5):
        print(dt)

    # Prints:
    # 1992-02-29 00:00:00
    # 1993-02-28 00:00:00
    # 1994-02-28 00:00:00
    # 1995-02-28 00:00:00
    # 1996-02-29 00:00:00

Обратите внимание, что я использую base datetime (dtstart) в моем правиле, а не добавляю 1 год к предыдущемурезультат.Причина этого в том, что relativedelta с потерями, поэтому добавление relativedelta(years=1) к datetime(1995, 2, 28) даст datetime(1996, 2, 28).

...