Какой самый простой способ вычесть месяц из даты в Python?
Если только timedelta аргумент месяца в нем конструктор. Итак, какой самый простой способ сделать это? EDIT: Я не слишком много думал об этом, как было указано ниже. На самом деле, что я хотел, был в любой день в последний месяц, потому что в конечном итоге я собираюсь захватить только год и месяц. Так что, учитывая объект datetime, какой самый простой способ вернуть любой объект datetime, который выпадает в предыдущем месяце?
11 ответов
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])
Если я правильно ((date.month+delta-1) % 12)+1 работает и означает, что if not m: m = 12 можно удалить
Вы можете использовать сторонний 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
Я запутался с параметром month и months в dateutil. Вычитание с параметром month не работает, но months работают In [23]: created_datetime__lt — relativedelta(months=1) Out[23]: datetime.datetime(2016, 11, 29, 0, 0, tzinfo=
@SiminJie: Я думаю, что использование months будет иметь значение 1 месяц. Используя month вы установите месяц на 1 (например, январь), независимо от месяца ввода.
После того, как исходный вопрос изменится на «любой объект datetime в предыдущем месяце», вы можете сделать это довольно легко, вычитая 1 день с первого месяца.
from datetime import datetime, timedelta def a_day_in_previous_month(dt): return datetime(dt.year, dt.month, 1) - timedelta(days=1)
Вариант Ответ Дункана (у меня недостаточно репутации для комментариев), в которой используется 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)
Если только timedelta имел аргумент месяца в нем конструктор. Итак, что Простейший способ сделать это?
Чего вы хотите получить, когда вы вычитаете месяц, скажем, дату, которая составляет 30 марта? Это проблема с добавлением или вычитанием месяцев: месяцы имеют разную длину! В некоторых случаях исключение является подходящим в таких случаях, в других случаях «последний день предыдущего месяца» подходит для использования (но эта поистине сумасшедшая арифметика при вычитании месяца, а затем добавление месяца не в целом нет-операции!), в других же вы хотите сохранить в дополнение к дате некоторые указания на этот факт, например: «Я говорю 28 февраля, но я действительно хотел бы 30 февраля, если бы он существовал», поэтому что добавление или вычитание другого месяца на это может снова изменить ситуацию (и для последнего, очевидно, требуется специальный класс, содержащий данные плюс s/thing else).
Не может быть никакого реального решения, приемлемого для всех приложений, и вы не сказали нам, что нужно вашему конкретному приложению для семантики этой жалкой операции, поэтому здесь не намного больше поможет.
Вы абсолютно правы. Просто шутил, что у Timedelta есть аргумент для этого. Интересно, применима ли та же логика для отсутствия аргументов в течение нескольких часов или минут. Технически день не совсем 24 часа.
PHP обрабатывает это , добавляя 2 дня к 28 февраля, и это идет ко 2 марта. Есть соглашения для этого, позвольте мне найти их очень быстро
Это становится еще интереснее, если вы рассчитываете даты для обмена валюты: например, вам может понадобиться найти дату, скажем, через 3 или 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)
Вы можете использовать приведенную ниже функцию для получения даты до/после 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)
Я использовал следующий код для возврата 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
Как правильно вычесть месяц из datetime.now()?
В начале стоит подумать о:
Что делать если исходная дата 31 число?
Что делать с февралём?
Что делать с високосными годами?
from datetime import date from dateutil.relativedelta import relativedelta one_month = date.today() + relativedelta(months=-1)
>>> from datetime import date >>> from dateutil.relativedelta import relativedelta >>> one_month = date.today() + relativedelta(months=-1) >>> one_month datetime.date(2019, 10, 16)
from datetime import datetime, timedelta datetime.now() - timedelta(days=30)
import calendar calendar.monthrange(year, month)
>>> calendar.monthrange(2019, 2)[1] 28 >>> calendar.monthrange(2019, 1)[1] 31
Владимир Куц, Тогда если отнять месяц, а потом прибавить, на место не вернемся. Т.е. пусть автор объяснит, для чего ему на самом деле это надо.
Andy_U, так можно разную логику для вычетения и прибавления написать. Просто, как вы и сказали, нужно наверняка знать какую проблему нужно решить.
antonksa, и правда! Взглянул сейчас в календарь — есть такое. Спасибо, что подсказал. В следующий раз буду умнее и буду целиком делать проекты вопрошающих здесь и решать все их проблемы, а не только показывать направление, в котором можно двигаться.