What is the easiest way to subtract a month from a date in Python?

If only the timedelta argument of the month is a constructor in it. So what is the easiest way to do this?

EDIT: I haven't thought too much about this, as mentioned below. In fact, what I wanted was any day in the last month, because in the end Iโ€™m going to capture only a year and a month. So, given the datetime object, what is the easiest way to return any datetime object that drops in the previous month ?

+68
python
Aug 6 2018-10-06
source share
15 answers

Try the following:

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) 

Edit Fixed to handle the day.

Edit See also the answer from the riddle, which indicates a simpler calculation for d :

 d = min(date.day, calendar.monthrange(y, m)[1]) 
+44
Aug 6 '10 at 15:13
source share

You can use a third-party dateutil module (PyPI entry here ).

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

exit:

 2013-02-28 00:00:00 
+138
Jan 22 '13 at 13:16
source share

After the original question has been edited to "any date and time object in the previous month," you can do this quite easily by subtracting 1 day from the first month.

 from datetime import datetime, timedelta def a_day_in_previous_month(dt): return dt.replace(day=1) - timedelta(days=1) 
+41
Aug 22 '11 at 20:50
source share

Option Duncan's answer (I don't have enough reputation for comment), which uses calendar.monthrange to greatly simplify the calculation of the last day of the month:

 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) 

Information about the month of Get the last day of the month in Python

+21
Mar 16 '14 at 21:17
source share

If only timedelta had a month argument in it constructor. So what is the easiest way to do this?

What do you want to get when you subtract a month, say, a date that is March 30? This is a problem with adding or subtracting months: months have different lengths! In some cases, the exception is appropriate in such cases, in other cases the โ€œlast day of the previous monthโ€ is suitable for use (but this is really crazy arithmetic when subtracting the month, and then adding the month is not a generally no-operation!), In others you want save in addition to the date some indications of this fact, for example: โ€œI say February 28, but I really would like February 30 if it existedโ€, therefore, adding or subtracting another month for this can change the situation again (and for the last obviously a special class is required containing the data plus s / thing else).

There can be no real solution acceptable for all applications, and you did not tell us what your specific application needs for the semantics of this miserable operation, so it will not help much more.

+14
Aug 6 2018-10-06T00:
source share

If you want only one day in the last month, the simplest thing you can do is subtract the number of days from the current date, which will give you the last day of the previous month.

For example, starting from any date:

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

Subtracting the days of the current date, we get:

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

This is enough for your simplified need any day in the last month.

But now that you have it, you can also get any day in the month, including the day you started (i.e., more or less the same as subtracting the month):

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

Of course, you need to be careful from the 31st to the 30-day month or days absent in February (and take care of leap years), but this is also easy to do:

 >>> 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) 
+2
May 24 '16 at 22:14
source share

Here are some code to do just that. I have not tried it myself ...

 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 
0
Aug 6 '10 at 15:14
source share

Given the (year, month) tuple where the month goes from 1 to 12, try the following:

 >>> 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) 

It is estimated that 12 months each year.

0
Aug 6 2018-10-06T00:
source share
 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) 
0
Jul 29 '15 at 2:23
source share

I used the following code to return n months from a specific date:

 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 

For example: entry date = 12/28/2015 Calculate 6 months of the previous date.

I) CALIATION MONTH: This step will give you start_date on 06/30/2015.
Note that after the step of calculating the month, you will receive the last day of the required month.

II) CALCULATED DAY: The condition if (start_date.day> your_date.day) checks if the day with input_date is present in the required month. This handles the condition when the input date is 31 (or 30) and the required month has less than 31 (or 30 days). He also handles the case of a leap year (for February). After this step, you will get the result as 06/28/2015

If this condition is not met, start_date remains the last date of the previous month. Therefore, if you give 12/31/2015 as the input date and want 6 months of the previous date, it will give you 06/30/2015

0
Feb 23 '16 at 10:42 on
source share

You can use the function below to get the date before / after X month.

 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)
0
Dec 07 '17 at 0:11
source share

I think this answer is quite readable:

 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 
0
Apr 10 '18 at 21:21
source share

I use this for state fiscal years, where Q4 starts on October 1st. Notice, I convert the date to quarters and cancel it.

 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) 
0
Feb 11 '19 at 18:33
source share

The vectorized panda solution is very simple:

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

0
Jun 11 '19 at 19:56 on
source share
 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) 

A simple solution, no special libraries needed.

-2
Sep 21 '18 at 8:39
source share



All Articles