Как обрабатывать сумму и умножение временных интервалов? - PullRequest
0 голосов
/ 12 апреля 2019

Мне нужно вычислить deadline (datetime) после добавления N (int) interval s (представлено в относительном значении, потому что это могут быть месяцы или годы, а также секунды или даты).Я могу сделать это, просто умножив interval на N и суммировав его до start_date (datetime).В то же время мне нужно сделать это в несколько шагов, например, вычислить 5-й срок, 6-й ..., поэтому я просто добавляю interval к start_date для N раз.В некоторых случаях эти два метода дают разные результаты.

Предположим, start_date = datetime (год = 2019, месяц = ​​1, день = 2), interval = относительная дельта (месяцы = 1, дни = 2) и N = 16. От одноготочки зрения, оба метода верны, потому что interval*16 = относительная дельта (годы = + 1, месяцы = + 4, дни = + 32), start_date+16*interval = 2019-01-01 + 1 год + 4 месяца + 32дни = 2020/05/1 + 32 дня = 2020-06-02 (потому что в мае 31 день).В то же время, когда мы добавляем их один за другим, это приводит к 2020/05/1 + 1 месяц + 2 дня = 2020/06/02

Проблема связана с «переполнением дней месяца», ноЯ не могу понять, как справиться с этим.Всегда использовать сумму вместо умножения?Но небезопасно (представьте 9999999-й срок с интервалом = 1 день и 1 секунда)

Шаги для воспроизведения:

def test_relative_sum_mult_with_date():
    start = datetime(year=2019, month=1, day=1)
    interval = relativedelta(months=1, days=2)
    check_up_to = 100
    for i in range(check_up_to):
        multiplied = start + i*interval
        summed = start
        for j in range(i):
            summed += interval
        print('i=%s, i*interval=%s, diff(multiplied-summed)=%s, multiplied=%s, summed=%s' %
              (i, i*interval, multiplied-summed, multiplied, summed))
        assert multiplied == summed

Трассировка:

i*interval=relativedelta(), diff(multiplied-summed)=0:00:00, multiplied=2019-01-01 00:00:00, summed=2019-01-01 00:00:00
i=1, i*interval=relativedelta(months=+1, days=+2), diff(multiplied-summed)=0:00:00, multiplied=2019-02-03 00:00:00, summed=2019-02-03 00:00:00
i=2, i*interval=relativedelta(months=+2, days=+4), diff(multiplied-summed)=0:00:00, multiplied=2019-03-05 00:00:00, summed=2019-03-05 00:00:00
i=3, i*interval=relativedelta(months=+3, days=+6), diff(multiplied-summed)=0:00:00, multiplied=2019-04-07 00:00:00, summed=2019-04-07 00:00:00
i=4, i*interval=relativedelta(months=+4, days=+8), diff(multiplied-summed)=0:00:00, multiplied=2019-05-09 00:00:00, summed=2019-05-09 00:00:00
i=5, i*interval=relativedelta(months=+5, days=+10), diff(multiplied-summed)=0:00:00, multiplied=2019-06-11 00:00:00, summed=2019-06-11 00:00:00
i=6, i*interval=relativedelta(months=+6, days=+12), diff(multiplied-summed)=0:00:00, multiplied=2019-07-13 00:00:00, summed=2019-07-13 00:00:00
i=7, i*interval=relativedelta(months=+7, days=+14), diff(multiplied-summed)=0:00:00, multiplied=2019-08-15 00:00:00, summed=2019-08-15 00:00:00
i=8, i*interval=relativedelta(months=+8, days=+16), diff(multiplied-summed)=0:00:00, multiplied=2019-09-17 00:00:00, summed=2019-09-17 00:00:00
i=9, i*interval=relativedelta(months=+9, days=+18), diff(multiplied-summed)=0:00:00, multiplied=2019-10-19 00:00:00, summed=2019-10-19 00:00:00
i=10, i*interval=relativedelta(months=+10, days=+20), diff(multiplied-summed)=0:00:00, multiplied=2019-11-21 00:00:00, summed=2019-11-21 00:00:00
i=11, i*interval=relativedelta(months=+11, days=+22), diff(multiplied-summed)=0:00:00, multiplied=2019-12-23 00:00:00, summed=2019-12-23 00:00:00
i=12, i*interval=relativedelta(years=+1, days=+24), diff(multiplied-summed)=0:00:00, multiplied=2020-01-25 00:00:00, summed=2020-01-25 00:00:00
i=13, i*interval=relativedelta(years=+1, months=+1, days=+26), diff(multiplied-summed)=0:00:00, multiplied=2020-02-27 00:00:00, summed=2020-02-27 00:00:00
i=14, i*interval=relativedelta(years=+1, months=+2, days=+28), diff(multiplied-summed)=0:00:00, multiplied=2020-03-29 00:00:00, summed=2020-03-29 00:00:00
i=15, i*interval=relativedelta(years=+1, months=+3, days=+30), diff(multiplied-summed)=0:00:00, multiplied=2020-05-01 00:00:00, summed=2020-05-01 00:00:00
i=16, i*interval=relativedelta(years=+1, months=+4, days=+32), diff(multiplied-summed)=-1 day, 0:00:00, multiplied=2020-06-02 00:00:00, summed=2020-06-03 00:00:00

datetime.datetime(2020, 6, 2, 0, 0, 0) != datetime.datetime(2020, 6, 3, 0, 0, 0)

Expected :datetime.datetime(2020, 6, 3, 0, 0, 0)
Actual   :datetime.datetime(2020, 6, 2, 0, 0, 0)

Версии: Python 3.6 python-dateutil == 2.8.0

1 Ответ

1 голос
/ 15 апреля 2019

Позвольте мне привести ваш пример более простым способом:

start = datetime(year=2018, month=3, day=29)
interval = relativedelta(months=1, days=2)
d1 = start + interval * 2            # 2018-06-02
d2 = start + interval + interval     # 2018-06-03
print(d1, d2)

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

...