Добавьте один месяц к заданной дате (с округлением на следующий день) с помощью Python

Я хотел бы добавить один месяц к заданной дате

import datetime
dt = datetime.datetime(year=2014, month=5, day=2)

поэтому я должен получить

datetime.datetime(year=2014, month=6, day=2)

но с

dt = datetime.datetime(year=2015, month=1, day=31)

я должен получить

datetime.datetime(year=2015, month=3, day=1)

потому что нет 2015-02-31 (и я хочу, чтобы мой результат был круглым через день после)

В некоторых месяцах 31 день, в других 30, в некоторых 29, в некоторых 28!

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

Я заметил, что у панд есть интересная концепция DateOffset

http://pandas.pydata.org/pandas-docs/stable/timeseries.html#dateoffset-objects

но я не нашел смещения Month, только MonthBegin или MonthEnd

Я также вижу этот пост Как рассчитать дату через шесть месяцев от текущей даты с помощью модуля Python datetime?

поэтому я попробовал dateutil.relativedelta, но

from dateutil.relativedelta import relativedelta
datetime.datetime(year=2015, month=1, day=31)+relativedelta(months=1)

возвращается

datetime.datetime(2015, 2, 28, 0, 0)

поэтому результат был округлен за один день до.

Есть ли (чистый) способ округлить день after?

редактировать: я привел пример с добавлением одного месяца, но я также хочу иметь возможность добавить, например: 2 года и 6 месяцев (используя relativedelta(years=2, months=6))


person scls    schedule 28.01.2015    source источник
comment
Хотели бы вы когда-нибудь добавить, например, 2 года, 6 месяцев и 5 дней? Можете ли вы быть более конкретным в вашем вопросе?   -  person Ffisegydd    schedule 28.01.2015
comment
Мне просто нужно добавить годы и месяцы к дате и времени (но я хочу, чтобы результат был круглым на следующий день, а не за день до этого, как это делает relativedelta)   -  person scls    schedule 28.01.2015
comment
Я отредактировал свой ответ ниже, чтобы он принимал аргумент relativedelta. Это позволит вам использовать любое время, которое вы хотите. Обратите внимание, что это не сработает, если вы включите дни, часы и т. д.   -  person Ffisegydd    schedule 28.01.2015


Ответы (4)


Вы можете использовать dateutil.relativedelta.relativedelta и вручную проверить файл datetime.day, если исходный день больше нового дня, добавьте день.

Функция ниже принимает объект datetime и объект relativedelta. Обратите внимание, что приведенный ниже код работает только в течение многих лет и месяцев, я не верю, что он будет работать, если вы будете использовать что-то меньшее (дни, часы и т. д.). Вы можете легко модифицировать эту функцию, чтобы она принимала years и months в качестве аргументов, а затем создавала relativedelta внутри самой функции.

from datetime import datetime
from dateutil.relativedelta import relativedelta

def add_time(d, rd):
    day = relativedelta(days=+1)

    out = d + rd
    if d.day > out.day:
        out = out + day

    return out    

# Check that it "rolls over"
print(add_time(datetime(year=2015, month=1, day=29), relativedelta(years=+4, months=+1))) # 2019-03-01 00:00:00
print(add_time(datetime(year=2015, month=3, day=31), relativedelta(years=+0, months=+2))) # 2015-05-01 00:00:00

# Check that it handles "normal" scenarios
print(add_time(datetime(year=2015, month=6, day=19), relativedelta(months=+1))) # 2015-07-19 00:00:00
print(add_time(datetime(year=2015, month=6, day=30), relativedelta(years=+2, months=+1))) # 2017-07-30 00:00:00

# Check across years
print(add_time(datetime(year=2015, month=12, day=25), relativedelta(months=+1))) # 2016-01-25 00:00:00

# Check leap years
print(add_time(datetime(year=2016, month=1, day=29), relativedelta(years=+4, months=+1))) # 2020-02-29 00:00:00
person Ffisegydd    schedule 28.01.2015

Кажется, это работает. Довольно чисто, но не красиво:

def add_month(now):
    try:
        then = (now + relativedelta(months=1)).replace(day=now.day)
    except ValueError:
        then = (now + relativedelta(months=2)).replace(day=1)
    return then

for now in [datetime(2015, 1, 20), datetime(2015, 1, 31), datetime(2015, 2, 28)]:
    print now, add_month(now)

печатает:

2015-01-20 00:00:00 2015-02-20 00:00:00
2015-01-31 00:00:00 2015-03-01 00:00:00
2015-02-28 00:00:00 2015-03-28 00:00:00

Он добавляет один месяц и пытается заменить день исходным днем. Если это удается, это не особый случай. В случае неудачи (ValueError) мы должны добавить еще месяц и перейти к его первому дню.

person eumiro    schedule 28.01.2015

Быстрое решение:

import datetime
import calendar
dt = datetime.datetime(year=2014, month=5, day=2)
d = calendar.monthrange(dt.year,dt.month+1)[1] 
print dt+datetime.timedelta(days=d+1)

Выход для первого входа (year=2014, month=5, day=2):

2014-06-02 00:00:00

Выход для второго входа (year=2015, month=1, day=31):

2015-03-01 00:00:00
person venpa    schedule 28.01.2015

Используйте следующий метод:

def datetime_offset_by_months(datetime_origin, n=1):
    """
    datetime offset by months
    :param datetime_origin: the original datetime
    :param n: count of months
    :return: after offset datetime
    """
    # create a shortcut object for one day
    one_day = datetime.timedelta(days=1)

    # first use div and mod to determine year cycle
    q, r = divmod(datetime_origin.month + n, 12)

    # create a datetime_offset
    # to be the last day of the target month
    datetime_offset = datetime.datetime(
        datetime_origin.year + q, r + 1, 1) - one_day

    '''
       if input date is the last day of this month
       then the output date should also be the last
       day of the target month, although the day
       www.iplaypy.com
       may be different.
       for example:
       datetime_origin = 8.31
       datetime_offset = 9.30
    '''

    if datetime_origin.month != (datetime_origin + one_day).month:
        return datetime_offset

    '''
        if datetime_origin day is bigger than last day of
        target month, then, use datetime_offset
        for example:
        datetime_origin = 10.31
        datetime_offset = 11.30
    '''

    if datetime_origin.day >= datetime_offset.day:
        return datetime_offset

    '''
     then, here, we just replace datetime_offset's day
     with the same of datetime_origin, that's ok.
    '''

    return datetime_offset.replace(day= datetime_origin.day)

его использование:

import datetime
now_date = datetime.datetime.now()

off_set_datetime = datetime_offset_by_months(now_date, -2)
print(off_set_datetime)
person aircraft    schedule 15.05.2018