295

I need to increment the month of a datetime value

next_month = datetime.datetime(mydate.year, mydate.month+1, 1)

when the month is 12, it becomes 13 and raises error "month must be in 1..12". (I expected the year would increment)

I wanted to use timedelta, but it doesn't take month argument. There is relativedelta python package, but i don't want to install it just only for this. Also there is a solution using strtotime.

time = strtotime(str(mydate));
next_month = date("Y-m-d", strtotime("+1 month", time));

I don't want to convert from datetime to str then to time, and then to datetime; therefore, it's still a library too

Does anyone have any good and simple solution just like using timedelta?

1
  • 5
    This is a shortcoming of python's datetime which must be able to do datetime.timedelta(months=6). Java's LocalDate.plusMonths() increments the month and then ensures a valid date by decrementing the day field to the last valid date in the month. Python should do the same ootb without having to resort to external modules, but until it's fixed the java code can be easily ported — in fact it's roughly the same as the code in the accepted answer. Commented Dec 17, 2016 at 21:10

21 Answers 21

616

This is short and sweet method to add a month to a date using dateutil's relativedelta.

from datetime import datetime
from dateutil.relativedelta import relativedelta
    
date_after_month = datetime.today()+ relativedelta(months=1)
print('Today: ',datetime.today().strftime('%d/%m/%Y'))
print('After Month:', date_after_month.strftime('%d/%m/%Y'))
Today:  01/03/2013

After Month: 01/04/2013

A word of warning: relativedelta(months=1) and relativedelta(month=1) have different meanings. Passing month=1 will replace the month in original date to January whereas passing months=1 will add one month to original date.

Note: this requires the python-dateutil module. Install with this command line:

pip install --user python-dateutil

Explanation : Add month value in python

Sign up to request clarification or add additional context in comments.

9 Comments

This requires the python-dateutil module, though/
What is the difference between timedelta and relativedelta in the dateutil package?
Does dateutil play nicely with pytz-aware datetimes (dt localized with pytz tzinfo)?
@Fomalhaut It's months, not just month. What a way to make a library vulnerable to buggy behaviour.
months and month save me for more than 3 hours of debugging
|
193

Edit - based on your comment of dates being needed to be rounded down if there are fewer days in the next month, here is a solution:

import datetime
import calendar

def add_months(sourcedate, months):
    month = sourcedate.month - 1 + months
    year = sourcedate.year + month // 12
    month = month % 12 + 1
    day = min(sourcedate.day, calendar.monthrange(year,month)[1])
    return datetime.date(year, month, day)

In use:

>>> somedate = datetime.date.today()
>>> somedate
datetime.date(2010, 11, 9)
>>> add_months(somedate,1)
datetime.date(2010, 12, 9)
>>> add_months(somedate,23)
datetime.date(2012, 10, 9)
>>> otherdate = datetime.date(2010,10,31)
>>> add_months(otherdate,1)
datetime.date(2010, 11, 30)

Also, if you're not worried about hours, minutes and seconds you could use date rather than datetime. If you are worried about hours, minutes and seconds you need to modify my code to use datetime and copy hours, minutes and seconds from the source to the result.

18 Comments

if it was the 31st October, then it would be the 30st November
but i think the month may overflow here --> month = (sourcedate.month - 1 + months) % 12 + 1. Don't you think it's better to solve year after calculating the month?
Not sure I understand your comment. The modulus prevents the month from overflowing and in the code it doesn't matter which order year and month are calculated.
I think for this instance add_months(date, 23), the year wouldn't increment more than 1. Anyway it solved my problem, perhaps it will solve others' such problems. Thanks a lot
It's not wrong, but it's not the simplest solution either. Please, take note of "Atul Arvind" answer below.
|
52

Here's my salt :

current = datetime.datetime(mydate.year, mydate.month, 1)
next_month = datetime.datetime(mydate.year + int(mydate.month / 12), ((mydate.month % 12) + 1), 1)

Quick and easy :)

3 Comments

This doesn't solve the problem: it takes you to the first day of the next month, not the 'same day'. Of course you could change the "1" to mydate.day, but then you risk an error when you go e.g. from Jan 31 to Feb.
That doesn't fully solve the OP's problem, but it does work for my case.
next_month = datetime(year+1 if month == 12 else year, 1 if month == 12 else month)
21

since no one suggested any solution, here is how i solved so far

year, month= divmod(mydate.month+1, 12)
if month == 0: 
      month = 12
      year = year -1
next_month = datetime.datetime(mydate.year + year, month, 1)

2 Comments

Although, this doesn't solve the problem: it takes you to the first day of the next month, not the 'same day'.
@MatthewSchinckel You could just replace the 1 at the end by the current day, but then you would need to check for 31/30 days (not talking about February)
17
from datetime import timedelta
try:
    next = (x.replace(day=1) + timedelta(days=31)).replace(day=x.day)
except ValueError:  # January 31 will return last day of February.
    next = (x + timedelta(days=31)).replace(day=1) - timedelta(days=1)

If you simply want the first day of the next month:

next = (x.replace(day=1) + timedelta(days=31)).replace(day=1)

2 Comments

If I'm not mistaken, the second example would fail on January 21st, and send you right into March.
x.replace(day=1) switches Jan 21 to Jan 1 before adding 31 days.
14

Use the monthdelta package, it works just like timedelta but for calendar months rather than days/hours/etc.

Here's an example:

from monthdelta import MonthDelta

def prev_month(date):
    """Back one month and preserve day if possible"""
    return date + MonthDelta(-1)

Compare that to the DIY approach:

def prev_month(date):
    """Back one month and preserve day if possible"""
   day_of_month = date.day
   if day_of_month != 1:
           date = date.replace(day=1)
   date -= datetime.timedelta(days=1)
   while True:
           try:
                   date = date.replace(day=day_of_month)
                   return date
           except ValueError:
                   day_of_month -= 1               

3 Comments

This is the simplest solution.
If I have to install a different package, I'd rather take python-dateutil
I needed to use monthdelta.monthdelta(-1).
11

To calculate the current, previous and next month:

import datetime
this_month = datetime.date.today().month
last_month = datetime.date.today().month - 1 or 12
next_month = (datetime.date.today().month + 1) % 12 or 12

1 Comment

Much shorter next_month = datetime.date.today().month % 12 + 1
7

What about this one? (doesn't require any extra libraries)

from datetime import date, timedelta
from calendar import monthrange

today = date.today()
month_later = date(today.year, today.month, monthrange(today.year, today.month)[1]) + timedelta(1)

Comments

6

Perhaps add the number of days in the current month using calendar.monthrange()?

import calendar, datetime

def increment_month(when):
    days = calendar.monthrange(when.year, when.month)[1]
    return when + datetime.timedelta(days=days)

now = datetime.datetime.now()
print 'It is now %s' % now
print 'In a month, it will be %s' % increment_month(now)

1 Comment

increment_month(datetime.date(2011,1,31)) -> datetime.date(2011, 3, 3), which rounds the wrong way.
6

I was looking to solve the related problem of finding the date for the first of the following month, regardless of the day in the given date. This does not find the same day 1 month later.

So, if all you want is to put in December 12, 2014 (or any day in December) and get back January 1, 2015, try this:

import datetime

def get_next_month(date):
    month = (date.month % 12) + 1
    year = date.year + (date.month + 1 > 12)
    return datetime.datetime(year, month, 1)

Comments

5

Simplest solution is to go at the end of the month (we always know that months have at least 28 days) and add enough days to move to the next moth:

>>> from datetime import datetime, timedelta
>>> today = datetime.today()
>>> today
datetime.datetime(2014, 4, 30, 11, 47, 27, 811253)
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
datetime.datetime(2014, 5, 30, 11, 47, 27, 811253)

Also works between years:

>>> dec31
datetime.datetime(2015, 12, 31, 11, 47, 27, 811253)
>>> today = dec31
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)

Just keep in mind that it is not guaranteed that the next month will have the same day, for example when moving from 31 Jan to 31 Feb it will fail:

>>> today
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
>>> (today.replace(day=28) + timedelta(days=10)).replace(day=today.day)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: day is out of range for month

So this is a valid solution if you need to move to the first day of the next month, as you always know that the next month has day 1 (.replace(day=1)). Otherwise, to move to the last available day, you might want to use:

>>> today
datetime.datetime(2016, 1, 31, 11, 47, 27, 811253)
>>> next_month = (today.replace(day=28) + timedelta(days=10))
>>> import calendar
>>> next_month.replace(day=min(today.day, 
                               calendar.monthrange(next_month.year, next_month.month)[1]))
datetime.datetime(2016, 2, 29, 11, 47, 27, 811253)

Comments

4

Similar in ideal to Dave Webb's solution, but without all of that tricky modulo arithmetic:

import datetime, calendar

def increment_month(date):
    # Go to first of this month, and add 32 days to get to the next month
    next_month = date.replace(day=1) + datetime.timedelta(32)
    # Get the day of month that corresponds
    day = min(date.day, calendar.monthrange(next_month.year, next_month.month)[1])
    return next_month.replace(day=day)

1 Comment

This is about twice as slow as Dave's solution.
4

This implementation might have some value for someone who is working with billing.

If you are working with billing, you probably want to get "the same date next month (if possible)" as opposed to "add 1/12 of one year".

What is so confusing about this is you actually need take into account two values if you are doing this continuously. Otherwise for any dates past the 27th, you'll keep losing a few days until you end up at the 27th after leap year.

The values you need to account for:

  • The value you want to add a month to
  • The day you started with

This way if you get bumped from the 31st down to the 30th when you add one month, you'll get bumped back up to the 31st for the next month that has that day.

This is how I did it:

def closest_date_next_month(year, month, day):
    month = month + 1
    if month == 13:
        month = 1
        year  = year + 1


    condition = True
    while condition:
        try:
            return datetime.datetime(year, month, day)
        except ValueError:
            day = day-1
        condition = day > 26

    raise Exception('Problem getting date next month')

paid_until = closest_date_next_month(
                 last_paid_until.year, 
                 last_paid_until.month, 
                 original_purchase_date.day)  # The trick is here, I'm using the original date, that I started adding from, not the last one

Comments

3

Well with some tweaks and use of timedelta here we go:

from datetime import datetime, timedelta


def inc_date(origin_date):
    day = origin_date.day
    month = origin_date.month
    year = origin_date.year
    if origin_date.month == 12:
        delta = datetime(year + 1, 1, day) - origin_date
    else:
        delta = datetime(year, month + 1, day) - origin_date
    return origin_date + delta

final_date = inc_date(datetime.today())
print final_date.date()

4 Comments

incerement by 4 weeks (28 days)? Months have different days, haven't they?
ah good point. i'll drop this and think about it more.
Don't think this will work when the next month has fewer days, e.g. adding one month to 31st October or 31st January.
damn, you're right. this is why dealing with dates is so tough.
1
def add_month(d,n=1): return type(d)(d.year+(d.month+n-1)/12, (d.month+n-1)%12+1, 1)

Comments

0

A solution without the use of calendar:

def add_month_year(date, years=0, months=0):
    year, month = date.year + years, date.month + months + 1
    dyear, month = divmod(month - 1, 12)
    rdate = datetime.date(year + dyear, month + 1, 1) - datetime.timedelta(1)
    return rdate.replace(day = min(rdate.day, date.day))

Comments

0

This is what I came up with

from calendar  import monthrange

def same_day_months_after(start_date, months=1):
    target_year = start_date.year + ((start_date.month + months) / 12)
    target_month = (start_date.month + months) % 12
    num_days_target_month = monthrange(target_year, target_month)[1]
    return start_date.replace(year=target_year, month=target_month, 
        day=min(start_date.day, num_days_target_month))

Comments

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

def month_add(year, month, add_month):
    return month_sub(year, month, -add_month)

>>> month_add(2015, 7, 1)                        
(2015, 8)
>>> month_add(2015, 7, 20)
(2017, 3)
>>> month_add(2015, 7, 12)
(2016, 7)
>>> month_add(2015, 7, 24)
(2017, 7)
>>> month_add(2015, 7, -2)
(2015, 5)
>>> month_add(2015, 7, -12)
(2014, 7)
>>> month_add(2015, 7, -13)
(2014, 6)

Comments

-1

Just Use This:

import datetime
today = datetime.datetime.today()
nextMonthDatetime = today + datetime.timedelta(days=(today.max.day - today.day)+1)

2 Comments

max.day seems to always return 31 <code> ipdb> start_date datetime.datetime(2015, 2, 14, 0, 0) ipdb> start_date.max.day 31 </code>
Sry, this won't work, because "today.max.day == 2999-12-31" (or smth like this).
-3

example using the time object:

start_time = time.gmtime(time.time())    # start now

#increment one month
start_time = time.gmtime(time.mktime([start_time.tm_year, start_time.tm_mon+1, start_time.tm_mday, start_time.tm_hour, start_time.tm_min, start_time.tm_sec, 0, 0, 0]))

1 Comment

Well, that's a PHP-like solution if I've ever seen one
-5

My very simple solution, which doesn't require any additional modules:

def addmonth(date):
    if date.day < 20:
        date2 = date+timedelta(32)
    else :
        date2 = date+timedelta(25)
    date2.replace(date2.year, date2.month, day)
    return date2

1 Comment

im sorry, this answer adds no value. the question is alraedy solved, and as far as i can see, this answer is wrong.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.