Подтвердить что ты не робот

Какой самый простой способ вычесть месяц из даты в Python?

Если только timedelta аргумент месяца в нем конструктор. Итак, какой самый простой способ сделать это?

EDIT: Я не слишком много думал об этом, как было указано ниже. На самом деле, что я хотел, был в любой день в последний месяц, потому что в конечном итоге я собираюсь захватить только год и месяц. Так что, учитывая объект datetime, какой самый простой способ вернуть любой объект datetime, который выпадает в предыдущем месяце?

4b9b3361

Ответ 1

Попробуйте следующее:

def monthdelta(date, delta):
    m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
    if not m: m = 12
    d = min(date.day, [31,
        29 if y%4==0 and not y%400==0 else 28,31,30,31,30,31,31,30,31,30,31][m-1])
    return date.replace(day=d,month=m, year=y)

>>> for m in range(-12, 12):
    print(monthdelta(datetime.now(), m))


2009-08-06 16:12:27.823000
2009-09-06 16:12:27.855000
2009-10-06 16:12:27.870000
2009-11-06 16:12:27.870000
2009-12-06 16:12:27.870000
2010-01-06 16:12:27.870000
2010-02-06 16:12:27.870000
2010-03-06 16:12:27.886000
2010-04-06 16:12:27.886000
2010-05-06 16:12:27.886000
2010-06-06 16:12:27.886000
2010-07-06 16:12:27.886000
2010-08-06 16:12:27.901000
2010-09-06 16:12:27.901000
2010-10-06 16:12:27.901000
2010-11-06 16:12:27.901000
2010-12-06 16:12:27.901000
2011-01-06 16:12:27.917000
2011-02-06 16:12:27.917000
2011-03-06 16:12:27.917000
2011-04-06 16:12:27.917000
2011-05-06 16:12:27.917000
2011-06-06 16:12:27.933000
2011-07-06 16:12:27.933000
>>> monthdelta(datetime(2010,3,30), -1)
datetime.datetime(2010, 2, 28, 0, 0)
>>> monthdelta(datetime(2008,3,30), -1)
datetime.datetime(2008, 2, 29, 0, 0)

Изменить Исправлено для обработки дня.

Изменить См. также ответ от загадки, в котором указывается более простой расчет для d:

d = min(date.day, calendar.monthrange(y, m)[1])

Ответ 2

Вы можете использовать сторонний dateutil модуль (запись PyPI здесь).

import datetime
import dateutil.relativedelta

d = datetime.datetime.strptime("2013-03-31", "%Y-%m-%d")
d2 = d - dateutil.relativedelta.relativedelta(months=1)
print d2

выход:

2013-02-28 00:00:00

Ответ 3

После того, как исходный вопрос отредактирован на "любой объект даты и времени в предыдущем месяце", вы можете сделать это довольно легко, вычтя 1 день из первого месяца.

from datetime import datetime, timedelta

def a_day_in_previous_month(dt):
   return dt.replace(day=1) - timedelta(days=1)

Ответ 4

Вариант Ответ Дункана (у меня недостаточно репутации для комментариев), в которой используется calendar.monthrange, чтобы значительно упростить вычисление последнего дня месяц:

import calendar
def monthdelta(date, delta):
    m, y = (date.month+delta) % 12, date.year + ((date.month)+delta-1) // 12
    if not m: m = 12
    d = min(date.day, calendar.monthrange(y, m)[1])
    return date.replace(day=d,month=m, year=y)

Информация о месяцем из Получить последний день месяца в Python

Ответ 5

Если только timedelta имел аргумент месяца в нем конструктор. Итак, что Простейший способ сделать это?

Чего вы хотите получить, когда вы вычитаете месяц, скажем, дату, которая составляет 30 марта? Это проблема с добавлением или вычитанием месяцев: месяцы имеют разную длину! В некоторых случаях исключение является подходящим в таких случаях, в других случаях "последний день предыдущего месяца" подходит для использования (но эта поистине сумасшедшая арифметика при вычитании месяца, а затем добавление месяца не в целом нет-операции!), в других же вы хотите сохранить в дополнение к дате некоторые указания на этот факт, например: "Я говорю 28 февраля, но я действительно хотел бы 30 февраля, если бы он существовал", поэтому что добавление или вычитание другого месяца на это может снова изменить ситуацию (и для последнего, очевидно, требуется специальный класс, содержащий данные плюс s/thing else).

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

Ответ 6

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

Например, начиная с любой даты:

>>> import datetime                                                                                                                                                                 
>>> today = datetime.date.today()                                                                                                                                                   
>>> today
datetime.date(2016, 5, 24)

Вычитая дни текущей даты, мы получаем:

>>> last_day_previous_month = today - datetime.timedelta(days=today.day)
>>> last_day_previous_month
datetime.date(2016, 4, 30)

Этого достаточно для вашей упрощенной потребности в любой день за последний месяц.

Но теперь, когда у вас есть это, вы также можете получить любой день в месяце, в том числе в тот же день, когда вы начали (т.е. более или менее то же, что и вычитание месяца):

>>> same_day_last_month = last_day_previous_month.replace(day=today.day)
>>> same_day_last_month
datetime.date(2016, 4, 24)

Конечно, вам нужно быть внимательным с 31-м по 30-дневному месяцу или дням, отсутствующим в феврале (и позаботиться о високосных годах), но это также легко сделать:

>>> a_date = datetime.date(2016, 3, 31)                                                                                                                                             
>>> last_day_previous_month = a_date - datetime.timedelta(days=a_date.day)
>>> a_date_minus_month = (
...     last_day_previous_month.replace(day=a_date.day)
...     if a_date.day < last_day_previous_month.day
...     else last_day_previous_month
... )
>>> a_date_minus_month
datetime.date(2016, 2, 29)

Ответ 7

Вот несколько code, чтобы сделать именно это. Не пробовал самостоятельно...

def add_one_month(t):
    """Return a `datetime.date` or `datetime.datetime` (as given) that is
    one month earlier.

    Note that the resultant day of the month might change if the following
    month has fewer days:

        >>> add_one_month(datetime.date(2010, 1, 31))
        datetime.date(2010, 2, 28)
    """
    import datetime
    one_day = datetime.timedelta(days=1)
    one_month_later = t + one_day
    while one_month_later.month == t.month:  # advance to start of next month
        one_month_later += one_day
    target_month = one_month_later.month
    while one_month_later.day < t.day:  # advance to appropriate day
        one_month_later += one_day
        if one_month_later.month != target_month:  # gone too far
            one_month_later -= one_day
            break
    return one_month_later

def subtract_one_month(t):
    """Return a `datetime.date` or `datetime.datetime` (as given) that is
    one month later.

    Note that the resultant day of the month might change if the following
    month has fewer days:

        >>> subtract_one_month(datetime.date(2010, 3, 31))
        datetime.date(2010, 2, 28)
    """
    import datetime
    one_day = datetime.timedelta(days=1)
    one_month_earlier = t - one_day
    while one_month_earlier.month == t.month or one_month_earlier.day > t.day:
        one_month_earlier -= one_day
    return one_month_earlier

Ответ 8

Учитывая (год, месяц) кортеж, где месяц идет от 1 до 12, попробуйте следующее:

>>> from datetime import datetime
>>> today = datetime.today()
>>> today
datetime.datetime(2010, 8, 6, 10, 15, 21, 310000)
>>> thismonth = today.year, today.month
>>> thismonth
(2010, 8)
>>> lastmonth = lambda (yr,mo): [(y,m+1) for y,m in (divmod((yr*12+mo-2), 12),)][0]
>>> lastmonth(thismonth)
(2010, 7)
>>> lastmonth( (2010,1) )
(2009, 12)

Предполагается, что 12 месяцев каждый год.

Ответ 9

def month_sub(year, month, sub_month):
    result_month = 0
    result_year = 0
    if month > (sub_month % 12):
        result_month = month - (sub_month % 12)
        result_year = year - (sub_month / 12)
    else:
        result_month = 12 - (sub_month % 12) + month
        result_year = year - (sub_month / 12 + 1)
    return (result_year, result_month)

>>> month_sub(2015, 7, 1)    
(2015, 6)
>>> month_sub(2015, 7, -1)
(2015, 8)
>>> month_sub(2015, 7, 13)
(2014, 6)
>>> month_sub(2015, 7, -14)
(2016, 9)

Ответ 10

Я использовал следующий код для возврата n месяцев с определенной даты:

your_date =  datetime.strptime(input_date, "%Y-%m-%d")  #to convert date(2016-01-01) to timestamp
start_date=your_date    #start from current date

#Calculate Month
for i in range(0,n):    #n = number of months you need to go back
    start_date=start_date.replace(day=1)    #1st day of current month
    start_date=start_date-timedelta(days=1) #last day of previous month

#Calculate Day
if(start_date.day>your_date.day):   
    start_date=start_date.replace(day=your_date.day)            

print start_date

Например: дата ввода = 28/12/2015 Рассчитайте 6 месяцев предыдущей даты.

I) КАЛИЯЦИОННЫЙ МЕСЯЦ: Этот шаг даст вам start_date как 30/06/2015.
Примечание, что после шага вычисления месяца вы получите последний день требуемого месяца.

II) РАСЧЕТНЫЙ ДЕНЬ: Условие if (start_date.day > your_date.day) проверяет, присутствует ли день с input_date в требуемом месяце. Это обрабатывает условие, когда дата ввода составляет 31 (или 30), а требуемый месяц имеет менее 31 (или 30 дней). Он также обрабатывает случай високосного года (для февраля). После этого шага вы получите результат как 28/06/2015

Если это условие не выполняется, start_date остается последней датой предыдущего месяца. Поэтому, если вы дадите 31/12/2015 в качестве даты ввода и хотите 6 месяцев предыдущей даты, она даст вам 30/06/2015

Ответ 11

Вы можете использовать приведенную ниже функцию для получения даты до/после X месяца.

from datetime import date

def next_month(given_date, month):
    yyyy = int(((given_date.year * 12 + given_date.month) + month)/12)
    mm = int(((given_date.year * 12 + given_date.month) + month)%12)

    if mm == 0:
        yyyy -= 1
        mm = 12

    return given_date.replace(year=yyyy, month=mm)


if __name__ == "__main__":
    today = date.today()
    print(today)

    for mm in [-12, -1, 0, 1, 2, 12, 20 ]:
        next_date = next_month(today, mm)
        print(next_date)

Ответ 12

Я думаю, что этот ответ вполне читабелен:

def month_delta(dt, delta):
    year_delta, month = divmod(dt.month + delta, 12)

    if month == 0:
        # convert a 0 to december
        month = 12
        if delta < 0:
            # if moving backwards, then it december of last year
            year_delta -= 1

    year = dt.year + year_delta

    return dt.replace(month=month, year=year)

for delta in range(-20, 21):
    print(delta, "->", month_delta(datetime(2011, 1, 1), delta))

-20 -> 2009-05-01 00:00:00
-19 -> 2009-06-01 00:00:00
-18 -> 2009-07-01 00:00:00
-17 -> 2009-08-01 00:00:00
-16 -> 2009-09-01 00:00:00
-15 -> 2009-10-01 00:00:00
-14 -> 2009-11-01 00:00:00
-13 -> 2009-12-01 00:00:00
-12 -> 2010-01-01 00:00:00
-11 -> 2010-02-01 00:00:00
-10 -> 2010-03-01 00:00:00
-9 -> 2010-04-01 00:00:00
-8 -> 2010-05-01 00:00:00
-7 -> 2010-06-01 00:00:00
-6 -> 2010-07-01 00:00:00
-5 -> 2010-08-01 00:00:00
-4 -> 2010-09-01 00:00:00
-3 -> 2010-10-01 00:00:00
-2 -> 2010-11-01 00:00:00
-1 -> 2010-12-01 00:00:00
0 -> 2011-01-01 00:00:00
1 -> 2011-02-01 00:00:00
2 -> 2011-03-01 00:00:00
3 -> 2011-04-01 00:00:00
4 -> 2011-05-01 00:00:00
5 -> 2011-06-01 00:00:00
6 -> 2011-07-01 00:00:00
7 -> 2011-08-01 00:00:00
8 -> 2011-09-01 00:00:00
9 -> 2011-10-01 00:00:00
10 -> 2011-11-01 00:00:00
11 -> 2012-12-01 00:00:00
12 -> 2012-01-01 00:00:00
13 -> 2012-02-01 00:00:00
14 -> 2012-03-01 00:00:00
15 -> 2012-04-01 00:00:00
16 -> 2012-05-01 00:00:00
17 -> 2012-06-01 00:00:00
18 -> 2012-07-01 00:00:00
19 -> 2012-08-01 00:00:00
20 -> 2012-09-01 00:00:00

Ответ 13

Я использую это для государственных финансовых лет, где Q4 начинается 1 октября. Заметьте, я конвертирую дату в кварталы и отменяю ее.

import pandas as pd

df['Date'] = '1/1/2020'
df['Date'] = pd.to_datetime(df['Date'])              #returns 2020-01-01
df['NewDate'] = df.Date - pd.DateOffset(months=3)    #returns 2019-10-01 <---- answer

# For fun, change it to FY Quarter '2019Q4'
df['NewDate'] = df['NewDate'].dt.year.astype(str) + 'Q' + df['NewDate'].dt.quarter.astype(str)

# Convert '2019Q4' back to 2019-10-01
df['NewDate'] = pd.to_datetime(df.NewDate)

Ответ 14

Векторизованное решение для панд очень просто:

df['date'] - pd.DateOffset(months=1)

Ответ 15

import datetime
date_str = '08/01/2018'
format_str = '%d/%m/%Y'
datetime_obj = datetime.datetime.strptime(date_str, format_str)   
datetime_obj.replace(month=datetime_obj.month-1)

Простое решение, нет необходимости в специальных библиотеках.