587

I am using the datetime Python module. I am looking to calculate the date 6 months from the current date. Could someone give me a little help doing this?

The reason I want to generate a date 6 months from the current date is to produce a review date. If the user enters data into the system it will have a review date of 6 months from the date they entered the data.

Wolf
  • 9,679
  • 7
  • 62
  • 108
RailsSon
  • 19,897
  • 31
  • 82
  • 105
  • 6
    You will have to be more specific : when is six months from march 31th? And from august 30th? – kmkaplan Feb 13 '09 at 15:21
  • 5
    Yes the edit helps: it means you can aproximate 6 months to 183 days with no ill effect. So adding 183 days to today will do the trick. – kmkaplan Feb 13 '09 at 16:42
  • 28
    The above comments strike me as silly. The concept of "adding six months" is quite clear -- take the month component and add `6` to it, with support for rolling over the year (and cycling the month back to `1`) if we go past December. This happens to be exactly what `relativedelta` does and is in fact what every programming language with support for this sort of concept does. – Kirk Woll Jul 18 '12 at 18:30
  • 9
    @KirkWoll: I am sure it is quite clear. But still different for whoever speaks. Python: `date(2015, 3, 31) + relativedelta(months = 6)` gives `datetime.date(2015, 9, 30)`. Perl: `DateTime->new(year=>2000, month=>3, day=>31)->add(months=>6)` gives `2000-10-01T00:00:00`. Php: `date_create('2000-03-31', new DateTimeZone('UTC'))->add(new DateInterval('P6M'))` gives 2000-10-01. Pick your poison. – kmkaplan Feb 10 '15 at 10:12
  • 2
    @kmkaplan The problem with adding 183 days or similar approaches is that for certain dates they produce an answer which doesn't match ANYONE's definition of what 'adding six months' should be. – jwg Mar 04 '15 at 09:43
  • related: [How to convert years to seconds](http://stackoverflow.com/a/32658742/4279) – jfs Jan 29 '16 at 08:30
  • 5
    ... adding 182 seems more pragmatic for generating a *review date*: it keeps the day of the week intact. – Wolf Mar 27 '19 at 11:35
  • @Wolf Maybe… It turns out `6*365/12` does give `182` because of integer division ;-) – kmkaplan Apr 26 '20 at 09:16

48 Answers48

1340

I found this solution to be good. (This uses the python-dateutil extension)

from datetime import date
from dateutil.relativedelta import relativedelta

six_months = date.today() + relativedelta(months=+6)

The advantage of this approach is that it takes care of issues with 28, 30, 31 days etc. This becomes very useful in handling business rules and scenarios (say invoice generation etc.)

$ date(2010,12,31)+relativedelta(months=+1)
  datetime.date(2011, 1, 31)

$ date(2010,12,31)+relativedelta(months=+2)
  datetime.date(2011, 2, 28)
Paul
  • 10,381
  • 13
  • 48
  • 86
Mahendra
  • 13,575
  • 2
  • 16
  • 4
  • 7
    the +6 is an indicator that it could be -6, same thing applies to days and years as well :) – securecurve Feb 20 '13 at 11:04
  • that's weird, I'm using python 2.7.1 and from dateutil.relativedelta import relativedelta won't work – sliders_alpha Mar 22 '13 at 09:05
  • 6
    @sliders_alpha You need to install the python-dateutil package (pip install python-dateutil) – poiuytrez Nov 19 '13 at 15:29
  • 23
    This is the correct solution. If the OP would have asked 6 years instead of 6 months, then the approved answer would fail miserably. So one should keep in mind that the answers are more valuable the more generalized they are. – Daniel F May 24 '14 at 20:34
  • 12
    Kindly note that the `relativedelta` function also takes in `month` as an argument, which basically replaces / sets / fixes month in the passed date, which is very different than adding more months. Just a forewarning for folks wondering if the function is broken because they forgot the extra *s* in *months*. – shad0w_wa1k3r Jul 30 '19 at 09:19
  • @shad0w_wa1k3r this comment should be part of the answer- I just spent a frustrating 15 minutes wondering why it wasn't working – user2682863 Aug 17 '19 at 21:34
  • 10
    I'm curious: does anyone know why this isn't included by default? Timedelta seems packaged with `datetime` by default. I actually assumed I could pass "months" into timedelta. – dTanMan Nov 12 '19 at 03:14
  • 5
    @dTanMan Since it is not clear what should be the result of adding one month to date `(2020, 1, 30)`. Any attempt to solve it is not perfect and will raise a debate. All these corner cases and additional features are left to be covered by the external `dateutil` package. – Jeyekomon Jan 21 '20 at 10:12
  • 2
    wow can't believe i have to scroll down this much – JChao Apr 13 '21 at 08:53
  • @dTanMan one challenge with timedelta is that the biggest unit is days, so adding months and years is not straightforward but the .replace() function could work: dateTimeObj = dateTimeObj.replace(year = dateTimeObj.year - 1), though this becomes challenging when adding days months goes past the edge of a month / year – Peej1226 Dec 21 '22 at 13:38
73

Well, that depends what you mean by 6 months from the current date.

  1. Using natural months:

    inc = 6
    year = year + (month + inc - 1) // 12
    month = (month + inc - 1) % 12 + 1
    
  2. Using a banker's definition, 6*30:

    date += datetime.timedelta(6 * 30)
    
Nico Schlömer
  • 53,797
  • 27
  • 201
  • 249
vartec
  • 131,205
  • 36
  • 218
  • 244
  • 6
    Could you throw in the half-year definition (183 days) plus the 26 weeks definition, too? It helps to have them all in one place. – S.Lott Feb 13 '09 at 18:02
  • 12
    just a quick remark: I think, for month, the formula would be instead (month + 5) % 12 + 1 b/c for june, your formula gives 0 whereas the expected result is 12... despite this little error, to my mind, your answer is the one that best answers the question – pierroz Mar 15 '10 at 16:02
  • 4
    and same for year: it should be year + (month + 5)/12 – pierroz Mar 15 '10 at 16:15
  • 13
    What if the date is 31, and the month six month later can't have 31 days (which is the case for most months with 31 days)? – akv Apr 06 '11 at 11:37
  • 3
    30 days isn't 'the bankers definition'. There are LOTS of ways of counting days for interest payments etc., and the several methods based on 30/360 :) are just a few of them. http://en.wikipedia.org/wiki/Day_count_convention#30.2F360_methods – jwg Mar 04 '15 at 09:46
  • 9
    Downvote because the first solution ``(day, month, year) = (day, (month+6)%12, year+(month+6)/12)`` can buggy, as it generates invalid dates such as ``(31, 8, 2015)`` -> ``(31, 2, 2016)`` – ohw Jan 16 '16 at 23:40
  • does not work with python 3. There you get a float year (e.g. 2020.6666666666667) – stackunderflow Jul 21 '20 at 19:24
  • @dev-null, just wrap year in `int()` – ruslan_krivoshein Aug 11 '20 at 14:26
  • 3
    @ruslan_krivoshein, ok that works. But I think it would be even better to use the quotient operator // in the last expression: `year + (month + 5)//12`. Then there's no type conversion needed and the purpose is clearer. – stackunderflow Aug 12 '20 at 19:40
  • For method 1, the month should be the "old" month variable. Otherwise it's broken. Edit: never mind, it WAS correct but someone changed and broke it. – fireattack Jun 24 '22 at 06:53
  • @nico-schlömer would you mind to modify it one more time to fix the issue mentioned above? Thanks (I can't edit this answer due to "queue being full". – fireattack Jun 24 '22 at 06:56
  • @nico-schlömer Sorry I wasn't clear. They were not swapped. The issue is that with the old way, which is `(month, year) = ((month + 5) % 12 + 1, year + (month + 5)/12)` both variables are changed together, so the new variable of `month` won't be used when evaluating `year`. Your change, which split it into two lines, would have that problem and gives incorrect result. See: https://stackoverflow.com/questions/11502268/how-does-pythons-comma-operator-work-during-assignment – fireattack Jun 25 '22 at 09:34
  • I finally be able to modified it myself, so it should be solved now. Thanks for your time – fireattack Jun 25 '22 at 09:44
65

With Python 3.x you can do it like this:

from datetime import datetime, timedelta
from dateutil.relativedelta import *

date = datetime.now()
print(date)
# 2018-09-24 13:24:04.007620

date = date + relativedelta(months=+6)
print(date)
# 2019-03-24 13:24:04.007620

but you will need to install python-dateutil module:

pip install python-dateutil
Luka Lopusina
  • 2,557
  • 3
  • 27
  • 32
  • 13
    I'd recommend `from dateutil.relativedelta import relativedelta`. Using `import *` isn't explicit. – Sam Morgan Apr 29 '20 at 13:55
  • 6
    `pd.DateOffset` exactly the same thing in newer `pandas`, `date = date + pd.DateOffset(months=6)` without need to install `dateutil`, see help – gregV May 04 '21 at 19:40
24

So, here is an example of the dateutil.relativedelta which I found useful for iterating through the past year, skipping a month each time to the present date:

>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> today = datetime.datetime.today()
>>> month_count = 0
>>> while month_count < 12:
...  day = today - relativedelta(months=month_count)
...  print day
...  month_count += 1
... 
2010-07-07 10:51:45.187968
2010-06-07 10:51:45.187968
2010-05-07 10:51:45.187968
2010-04-07 10:51:45.187968
2010-03-07 10:51:45.187968
2010-02-07 10:51:45.187968
2010-01-07 10:51:45.187968
2009-12-07 10:51:45.187968
2009-11-07 10:51:45.187968
2009-10-07 10:51:45.187968
2009-09-07 10:51:45.187968
2009-08-07 10:51:45.187968

As with the other answers, you have to figure out what you actually mean by "6 months from now." If you mean "today's day of the month in the month six years in the future" then this would do:

datetime.datetime.now() + relativedelta(months=6)
dannyman
  • 621
  • 1
  • 9
  • 27
20

For beginning of month to month calculation:

from datetime import timedelta
from dateutil.relativedelta import relativedelta

end_date = start_date + relativedelta(months=delta_period) + timedelta(days=-delta_period)
JRodDynamite
  • 12,325
  • 5
  • 43
  • 63
breddi
  • 217
  • 2
  • 2
  • 20
    This isn't the relativedelta solution mentioned above in the comments. Keep scrolling for the solution with 600+ upvotes. – Nostalg.io Feb 12 '18 at 20:16
20

Python can use datautil package for that, Please see the example below

It's not Just limited to that, you can pass combination of days, Months and Years at the same time also.

import datetime
from dateutil.relativedelta import relativedelta

# subtract months
proc_dt = datetime.date(2021,8,31)
proc_dt_minus_3_months = proc_dt + relativedelta(months=-3)
print(proc_dt_minus_3_months)

# add months
proc_dt = datetime.date(2021,8,31)
proc_dt_plus_3_months = proc_dt + relativedelta(months=+3)
print(proc_dt_plus_3_months)

# subtract days:
proc_dt = datetime.date(2021,8,31)
proc_dt_minus_3_days = proc_dt + relativedelta(days=-3)
print(proc_dt_minus_3_days)

# add days days:
proc_dt = datetime.date(2021,8,31)
proc_dt_plus_3_days = proc_dt + relativedelta(days=+3)
print(proc_dt_plus_3_days)

# subtract years:
proc_dt = datetime.date(2021,8,31)
proc_dt_minus_3_years = proc_dt + relativedelta(years=-3)
print(proc_dt_minus_3_years)

# add years:
proc_dt = datetime.date(2021,8,31)
proc_dt_plus_3_years = proc_dt + relativedelta(years=+3)
print(proc_dt_plus_3_years)

Results:

2021-05-31

2021-11-30

2021-08-28

2021-09-03

2018-08-31

2024-08-31

Arpan Saini
  • 4,623
  • 1
  • 42
  • 50
19

This solution works correctly for December, which most of the answers on this page do not. You need to first shift the months from a 1-based index (ie Jan = 1) to a 0-based index (ie Jan = 0) before using modulus ( % ) or integer division ( // ), otherwise November (11) plus 1 month gives you 12, which when finding the remainder ( 12 % 12 ) gives 0.

(And dont suggest "(month % 12) + 1" or Oct + 1 = december!)

def AddMonths(d,x):
    newmonth = ((( d.month - 1) + x ) % 12 ) + 1
    newyear  = int(d.year + ((( d.month - 1) + x ) / 12 ))
    return datetime.date( newyear, newmonth, d.day)

However ... This doesnt account for problem like Jan 31 + one month. So we go back to the OP - what do you mean by adding a month? One solution is to backtrack until you get to a valid day, given that most people would presume the last day of jan, plus one month, equals the last day of Feb. This will work on negative numbers of months too. Proof:

>>> import datetime
>>> AddMonths(datetime.datetime(2010,8,25),1)
datetime.date(2010, 9, 25)
>>> AddMonths(datetime.datetime(2010,8,25),4)
datetime.date(2010, 12, 25)
>>> AddMonths(datetime.datetime(2010,8,25),5)
datetime.date(2011, 1, 25)
>>> AddMonths(datetime.datetime(2010,8,25),13)
datetime.date(2011, 9, 25)
>>> AddMonths(datetime.datetime(2010,8,25),24)
datetime.date(2012, 8, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-1)
datetime.date(2010, 7, 25)
>>> AddMonths(datetime.datetime(2010,8,25),0)
datetime.date(2010, 8, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-12)
datetime.date(2009, 8, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-8)
datetime.date(2009, 12, 25)
>>> AddMonths(datetime.datetime(2010,8,25),-7)
datetime.date(2010, 1, 25)>>> 
jamesdlin
  • 81,374
  • 13
  • 159
  • 204
user417751
  • 307
  • 2
  • 2
  • 5
    Fixes the "Jan 31 + one month" problem: days_in_month = calendar.monthrange(newyear, newmonth)[1]; newday = min(d.day, days_in_month); (bumps the day down if too big eg Feb 31 to Feb 28/29) – Curtis Yallop Nov 30 '15 at 18:33
  • This worked for me only by converting the newyear to an integer data type def AddMonths(d,x): newmonth = ((( d.month - 1) + x ) % 12 ) + 1 newyear = d.year + int((( d.month - 1) + x ) / 12 ) return datetime.date( newyear, newmonth, d.day) – Manmeet Singh Nov 29 '19 at 10:28
  • Why fiddle around with all these magic numbers when the correct solution has already been implemented in the `dateutil` package? – Roland Illig Aug 26 '22 at 20:31
  • @RolandIllig works great for my situation because the tool I'm using does not allow package installation – Jak Oct 19 '22 at 06:09
  • how about if the day is 2020-3-1, and i need to go back a full month to get 2020-1-30 or 2020-1-31 – dataviews Apr 13 '23 at 01:23
18

What do you mean by "6 months"?

Is 2009-02-13 + 6 months == 2009-08-13? Or is it 2009-02-13 + 6*30 days?

import mx.DateTime as dt

#6 Months
dt.now()+dt.RelativeDateTime(months=6)
#result is '2009-08-13 16:28:00.84'

#6*30 days
dt.now()+dt.RelativeDateTime(days=30*6)
#result is '2009-08-12 16:30:03.35'

More info about mx.DateTime

Lii
  • 11,553
  • 8
  • 64
  • 88
Johannes Weiss
  • 52,533
  • 16
  • 102
  • 136
17

This doesn't answer the specific question (using datetime only) but, given that others suggested the use of different modules, here there is a solution using pandas.

import datetime as dt
import pandas as pd

date = dt.date.today() - \
       pd.offsets.DateOffset(months=6)

print(date)

2019-05-04 00:00:00

Which works as expected in leap years

date = dt.datetime(2019,8,29) - \
       pd.offsets.DateOffset(months=6)
print(date)

2019-02-28 00:00:00
rpanai
  • 12,515
  • 2
  • 42
  • 64
16

There's no direct way to do it with Python's datetime.

Check out the relativedelta type at python-dateutil. It allows you to specify a time delta in months.

Akbar ibrahim
  • 5,110
  • 3
  • 26
  • 23
14

Just use the timetuple method to extract the months, add your months and build a new dateobject. If there is a already existing method for this I do not know it.

import datetime

def in_the_future(months=1):
    year, month, day = datetime.date.today().timetuple()[:3]
    new_month = month + months
    return datetime.date(year + (new_month / 12), (new_month % 12) or 12, day)

The API is a bit clumsy, but works as an example. Will also obviously not work on corner-cases like 2008-01-31 + 1 month. :)

kolypto
  • 31,774
  • 17
  • 105
  • 99
pi.
  • 21,112
  • 8
  • 38
  • 59
  • 4
    Error in your code: `new_month % 12` should be `(new_month % 12) or 12`. Otherwise if you try this in November you will get an error. :) – Jordan Reiter May 02 '11 at 21:45
  • This is actually much cleaner than the accepted solution. This doesn't require any new import, just basic maths – Simon PA Apr 02 '19 at 16:26
  • return datetime.date(year + (new_month/ / 12), (new_month % 12) or 12, day) – paytam Apr 19 '20 at 05:57
  • This didn't handle new_month % 12 == 0, it just skip a whole year – Asoul Jun 24 '22 at 08:53
  • 2
    Doing `new_month / 12` and `new_month % 12` is wrong since month uses a 1-based index. You need to convert to 0-based indexing, perform division/modulus, and then convert back to 1-based indexing. – jamesdlin Jul 15 '22 at 20:51
14

I know this was for 6 months, however the answer shows in google for "adding months in python" if you are adding one month:

import calendar

date = datetime.date.today()    //Or your date

datetime.timedelta(days=calendar.monthrange(date.year,date.month)[1])

this would count the days in the current month and add them to the current date, using 365/12 would ad 1/12 of a year can causes issues for short / long months if your iterating over the date.

Mike Davies
  • 393
  • 2
  • 10
11

Using Python standard libraries, i.e. without dateutil or others, and solving the 'February 31st' problem:

import datetime
import calendar

def add_months(date, months):
    months_count = date.month + months

    # Calculate the year
    year = date.year + int(months_count / 12)

    # Calculate the month
    month = (months_count % 12)
    if month == 0:
        month = 12

    # Calculate the day
    day = date.day
    last_day_of_month = calendar.monthrange(year, month)[1]
    if day > last_day_of_month:
        day = last_day_of_month

    new_date = datetime.date(year, month, day)
    return new_date

Testing:

>>>date = datetime.date(2018, 11, 30)

>>>print(date, add_months(date, 3))
(datetime.date(2018, 11, 30), datetime.date(2019, 2, 28))

>>>print(date, add_months(date, 14))
(datetime.date(2018, 12, 31), datetime.date(2020, 2, 29))
David Ragazzi
  • 300
  • 2
  • 6
  • 2
    This has a bug: If the target month is December, it will return a date one year later than needed. Example: `add_months(datetime.date(2018, 11, 30), 1)` returns `datetime.date(2019, 12, 30)` (the year should be 2018, not 2019). So, better use a dedicated, well tested library for this! If you really need to only use standard library modules, see [my answer to a similar question](https://stackoverflow.com/a/35067015/40076). – taleinat May 06 '20 at 15:40
10

Dateutil package has implementation of such functionality. But be aware, that this will be naive, as others pointed already.

zgoda
  • 12,775
  • 4
  • 37
  • 46
6

A quick suggestion is Arrow

pip install arrow

>>> import arrow

>>> arrow.now().date()
datetime.date(2019, 6, 28)
>>> arrow.now().shift(months=6).date()
datetime.date(2019, 12, 28)
Diego Magalhães
  • 1,704
  • 9
  • 14
  • Use .datetime to avoid timezone (aware / naive) warning: arrow.now().shift(months=6).datetime – Flo Mar 10 '21 at 19:40
5

I have a better way to solve the 'February 31st' problem:

def add_months(start_date, months):
    import calendar

    year = start_date.year + (months / 12)
    month = start_date.month + (months % 12)
    day = start_date.day

    if month > 12:
        month = month % 12
        year = year + 1

    days_next = calendar.monthrange(year, month)[1]
    if day > days_next:
        day = days_next

    return start_date.replace(year, month, day)

I think that it also works with negative numbers (to subtract months), but I haven't tested this very much.

bstpierre
  • 30,042
  • 15
  • 70
  • 103
amoyafoss
  • 59
  • 1
  • 1
4

The QDate class of PyQt4 has an addmonths function.

>>>from PyQt4.QtCore import QDate  
>>>dt = QDate(2009,12,31)  
>>>required = dt.addMonths(6) 

>>>required
PyQt4.QtCore.QDate(2010, 6, 30)

>>>required.toPyDate()
datetime.date(2010, 6, 30)
user213043
  • 161
  • 1
  • 1
  • 6
3

Modified Johannes Wei's answer in the case 1new_month = 121. This works perfectly for me. The months could be positive or negative.

def addMonth(d,months=1):
    year, month, day = d.timetuple()[:3]
    new_month = month + months
    return datetime.date(year + ((new_month-1) / 12), (new_month-1) % 12 +1, day)
stealthyninja
  • 10,343
  • 11
  • 51
  • 59
  • 4
    It DOESN'T "work perfectly" when the day in the start date is greater than the number of days in the target month. Example: 2001-01-31 plus one month tries to create a date 2001-02-31. – John Machin Jan 16 '10 at 09:25
3

Modified the AddMonths() for use in Zope and handling invalid day numbers:

def AddMonths(d,x):
    days_of_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    newmonth = ((( d.month() - 1) + x ) % 12 ) + 1
    newyear  = d.year() + ((( d.month() - 1) + x ) // 12 ) 
    if d.day() > days_of_month[newmonth-1]:
      newday = days_of_month[newmonth-1]
    else:
      newday = d.day() 
    return DateTime( newyear, newmonth, newday)
Jost
  • 31
  • 1
3
import time

def add_month(start_time, months):  

        ret = time.strptime(start_time, '%Y-%m-%d')
        t = list(ret)

        t[1] += months

        if t[1] > 12:
            t[0] += 1 + int(months / 12)

            t[1] %= 12

        return int(time.mktime(tuple(t)))
ares
  • 31
  • 1
2

Yet another solution - hope someone will like it:

def add_months(d, months):
    return d.replace(year=d.year+months//12).replace(month=(d.month+months)%12)

This solution doesn't work for days 29,30,31 for all cases, so more robust solution is needed (which is not so nice anymore :) ):

def add_months(d, months):
    for i in range(4):
        day = d.day - i
        try:
            return d.replace(day=day).replace(year=d.year+int(months)//12).replace(month=(d.month+int(months))%12)
        except:
            pass
    raise Exception("should not happen")
Robert Lujo
  • 15,383
  • 5
  • 56
  • 73
2

From this answer, see parsedatetime. Code example follows. More details: unit test with many natural-language -> YYYY-MM-DD conversion examples, and apparent parsedatetime conversion challenges/bugs.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import time, calendar
from datetime import date

# from https://github.com/bear/parsedatetime
import parsedatetime as pdt

def print_todays_date():
    todays_day_of_week = calendar.day_name[date.today().weekday()]
    print "today's date = " + todays_day_of_week + ', ' + \
                              time.strftime('%Y-%m-%d')

def convert_date(natural_language_date):
    cal = pdt.Calendar()
    (struct_time_date, success) = cal.parse(natural_language_date)
    if success:
        formal_date = time.strftime('%Y-%m-%d', struct_time_date)
    else:
        formal_date = '(conversion failed)'
    print '{0:12s} -> {1:10s}'.format(natural_language_date, formal_date)

print_todays_date()
convert_date('6 months')

The above code generates the following from a MacOSX machine:

$ ./parsedatetime_simple.py 
today's date = Wednesday, 2015-05-13
6 months     -> 2015-11-13
$ 
Community
  • 1
  • 1
Johnny Utahh
  • 2,389
  • 3
  • 25
  • 41
2

How about this? Not using another library (dateutil) or timedelta? building on vartec's answer I did this and I believe it works:

import datetime

today = datetime.date.today()
six_months_from_today = datetime.date(today.year + (today.month + 6)/12, (today.month + 6) % 12, today.day)

I tried using timedelta, but because it is counting the days, 365/2 or 6*356/12 does not always translate to 6 months, but rather 182 days. e.g.

day = datetime.date(2015, 3, 10)
print day
>>> 2015-03-10

print (day + datetime.timedelta(6*365/12))
>>> 2015-09-08

I believe that we usually assume that 6 month's from a certain day will land on the same day of the month but 6 months later (i.e. 2015-03-10 --> 2015-09-10, Not 2015-09-08)

I hope you find this helpful.

Community
  • 1
  • 1
Babak K
  • 469
  • 5
  • 13
2

Here's a example which allows the user to decide how to return a date where the day is greater than the number of days in the month.

def add_months(date, months, endOfMonthBehaviour='RoundUp'):
    assert endOfMonthBehaviour in ['RoundDown', 'RoundIn', 'RoundOut', 'RoundUp'], \
        'Unknown end of month behaviour'
    year = date.year + (date.month + months - 1) / 12
    month = (date.month + months - 1) % 12 + 1
    day = date.day
    last = monthrange(year, month)[1]
    if day > last:
        if endOfMonthBehaviour == 'RoundDown' or \
            endOfMonthBehaviour == 'RoundOut' and months < 0 or \
            endOfMonthBehaviour == 'RoundIn' and months > 0:
            day = last
        elif endOfMonthBehaviour == 'RoundUp' or \
            endOfMonthBehaviour == 'RoundOut' and months > 0 or \
            endOfMonthBehaviour == 'RoundIn' and months < 0:
            # we don't need to worry about incrementing the year
            # because there will never be a day in December > 31
            month += 1
            day = 1
    return datetime.date(year, month, day)


>>> from calendar import monthrange
>>> import datetime
>>> add_months(datetime.datetime(2016, 1, 31), 1)
datetime.date(2016, 3, 1)
>>> add_months(datetime.datetime(2016, 1, 31), -2)
datetime.date(2015, 12, 1)
>>> add_months(datetime.datetime(2016, 1, 31), -2, 'RoundDown')
datetime.date(2015, 11, 30)
2

given that your datetime variable is called date:

date=datetime.datetime(year=date.year+int((date.month+6)/12),
                       month=(date.month+6)%13 + (1 if (date.month + 
                       months>12) else 0), day=date.day)
2

General function to get next date after/before x months.

from datetime import date

def after_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 = after_month(today, mm)
        print(next_date)
2
import datetime


'''
Created on 2011-03-09

@author: tonydiep
'''

def add_business_months(start_date, months_to_add):
    """
    Add months in the way business people think of months. 
    Jan 31, 2011 + 1 month = Feb 28, 2011 to business people
    Method: Add the number of months, roll back the date until it becomes a valid date
    """
    # determine year
    years_change = months_to_add / 12

    # determine if there is carryover from adding months
    if (start_date.month + (months_to_add % 12) > 12 ):
        years_change = years_change + 1

    new_year = start_date.year + years_change

    # determine month
    work = months_to_add % 12
    if 0 == work:
        new_month = start_date.month
    else:
        new_month = (start_date.month + (work % 12)) % 12

    if 0 == new_month:
        new_month = 12 

    # determine day of the month
    new_day = start_date.day
    if(new_day in [31, 30, 29, 28]):
        #user means end of the month
        new_day = 31


    new_date = None
    while (None == new_date and 27 < new_day):
        try:
            new_date = start_date.replace(year=new_year, month=new_month, day=new_day)
        except:
            new_day = new_day - 1   #wind down until we get to a valid date

    return new_date


if __name__ == '__main__':
    #tests
    dates = [datetime.date(2011, 1, 31),
             datetime.date(2011, 2, 28),
             datetime.date(2011, 3, 28),
             datetime.date(2011, 4, 28),
             datetime.date(2011, 5, 28),
             datetime.date(2011, 6, 28),
             datetime.date(2011, 7, 28),
             datetime.date(2011, 8, 28),
             datetime.date(2011, 9, 28),
             datetime.date(2011, 10, 28),
             datetime.date(2011, 11, 28),
             datetime.date(2011, 12, 28),
             ]
    months = range(1, 24)
    for start_date in dates:
        for m in months:
            end_date = add_business_months(start_date, m)
            print("%s\t%s\t%s" %(start_date, end_date, m))
tony diep
  • 29
  • 2
2

Im chiming in late, but

check out Ken Reitz Maya module,

https://github.com/kennethreitz/maya

something like this may help you, just change hours=1 to days=1 or years=1

>>> from maya import MayaInterval

# Create an event that is one hour long, starting now.
>>> event_start = maya.now()
>>> event_end = event_start.add(hours=1)

>>> event = MayaInterval(start=event_start, end=event_end)
perfecto25
  • 772
  • 9
  • 13
2

The "python-dateutil" (external extension) is a good solution, but you can do it with build-in Python modules (datetime and datetime)

I made a short and simple code, to solve it (dealing with year, month and day)

(running: Python 3.8.2)

from datetime import datetime
from calendar import monthrange

# Time to increase (in months)
inc = 12

# Returns mod of the division for 12 (months)
month = ((datetime.now().month + inc) % 12) or 1

# Increase the division by 12 (months), if necessary (+ 12 months increase)
year = datetime.now().year + int((month + inc) / 12)

# (IF YOU DON'T NEED DAYS,CAN REMOVE THE BELOW CODE)
# Returns the same day in new month, or the maximum day of new month
day = min(datetime.now().day,monthrange(year, month)[1])

print("Year: {}, Month: {}, Day: {}".format(year, month, day))
2

I often need last day of month to remain last day of month. To solve that I add one day before calculation and then subtract it again before return.

from datetime import date, timedelta

# it's a lot faster with a constant day
DAY = timedelta(1)

def add_month(a_date, months):
    "Add months to date and retain last day in month."
    next_day = a_date + DAY
    # calculate new year and month
    m_sum = next_day.month + months - 1
    y = next_day.year + m_sum // 12
    m = m_sum % 12 + 1
    try:
        return date(y, m, next_day.day) - DAY
    except ValueError:
        # on fail return last day in month
        # can't fail on december so I don't bother changing the year
        return date(y, m + 1, 1) - DAY
Jolbas
  • 757
  • 5
  • 15
2

Rework of an earlier answer by user417751. Maybe not so pythonic way, but it takes care of different month lengths and leap years. In this case 31 January 2012 + 1 month = 29 February 2012.

import datetime
import calendar

def add_mths(d, x):
    newday = d.day
    newmonth = (((d.month - 1) + x) % 12) + 1
    newyear  = d.year + (((d.month - 1) + x) // 12)
    if newday > calendar.mdays[newmonth]:
        newday = calendar.mdays[newmonth]
        if newyear % 4 == 0 and newmonth == 2:
            newday += 1
    return datetime.date(newyear, newmonth, newday)
Ireneusz
  • 153
  • 8
0

This is what I came up with. It moves the correct number of months and years but ignores days (which was what I needed in my situation).

import datetime

month_dt = 4
today = datetime.date.today()
y,m = today.year, today.month
m += month_dt-1
year_dt = m//12
new_month = m%12
new_date = datetime.date(y+year_dt, new_month+1, 1)
marshallpenguin
  • 167
  • 2
  • 8
0

I use this function to change year and month but keep day:

def replace_month_year(date1, year2, month2):
    try:
        date2 = date1.replace(month = month2, year = year2)
    except:
        date2 = datetime.date(year2, month2 + 1, 1) - datetime.timedelta(days=1)
    return date2

You should write:

new_year = my_date.year + (my_date.month + 6) / 12
new_month = (my_date.month + 6) % 12
new_date = replace_month_year(my_date, new_year, new_month)
gt_rocker
  • 36
  • 3
0

Another solution: calculate sum of days in month for next n month and add result to current date.

import calendar
import datetime

def date_from_now(months):
    today = datetime.datetime.today()

    month = today.month
    year = today.year
    sum_days = 0

    for i in range(int(months)):
        month += 1

        if month == 13:
            month = 1
            year += 1

        sum_days += calendar.monthrange(year, month)[1]

    return datetime.date.today() + datetime.timedelta(sum_days)

print(date_from_now(12)) # if to day is 2017-01-01, output: 2019-01-01 
bii811
  • 1
0

I think it would be safer to do something like this instead of manually adding days:

import datetime
today = datetime.date.today()

def addMonths(dt, months = 0):
    new_month = months + dt.month
    year_inc = 0
    if new_month>12:
        year_inc +=1
        new_month -=12
    return dt.replace(month = new_month, year = dt.year+year_inc)

newdate = addMonths(today, 6)
DataGreed
  • 13,245
  • 8
  • 45
  • 64
0

I solved this problem like this:

import calendar
from datetime import datetime
moths2add = 6
now = datetime.now()
current_year = now.year
current_month = now.month
#count days in months you want to add using calendar module
days = sum(
  [calendar.monthrange(current_year, elem)[1] for elem in range(current_month, current_month + moths)]
    )
print now + days
BabySnake
  • 9
  • 1
0

I used the replace() method and write this recursive function. the dt is a datetime.datetime object:

def month_timedelta(dt, m):
    y = m // 12
    dm = m % 12
    if y == 0:
        if dt.month + m <= 12:
            return dt.replace(month = dt.month + m)
        else:
            dy = (dt.month + m) // 12
            ndt = dt.replace(year=dt.year + dy)
            return ndt.replace(month=(ndt.month + m) % 12)
    else:
        return month_timedelta(dt.replace(year=dt.year + y),dm)
Daniel MM. Kamani
  • 855
  • 1
  • 7
  • 5
0

My implementation based on taleinat's answer:

import datetime
import calendar

def add_months(orig_date, month_count = 1):
    while month_count > 12:
        month_count -= 12
        orig_date = add_months(orig_date, 12)
    new_year = orig_date.year
    new_month = orig_date.month + month_count
    # note: in datetime.date, months go from 1 to 12
    if new_month > 12:
        new_year += 1
        new_month -= 12

    last_day_of_month = calendar.monthrange(new_year, new_month)[1]
    new_day = min(orig_date.day, last_day_of_month)

    return orig_date.replace(year=new_year, month=new_month, day=new_day)

With this function you can add as many months as you'd like.

from datetime import date
dt = date(2021, 1, 31)

print(add_months(dt, 49))

returns 2025-02-28

hypertown
  • 43
  • 4
0

I know that there are many answers to this question already, but using collections.deque and then the rotate() method, a function can be made that takes a datetime object as the input and then outputs a new datetime object that is one "business month" later than the current one. If the day of the month does not exist in the next month, then it subtracts one until it arrives on a valid day of the month and then returns that object.

import collections
import datetime

def next_month(dt: datetime.datetime):
    month_list = list(range(1, 12 + 1))
    months = collections.deque(month_list)
    while True:
        this_month = list(months)[0]
        if dt.month == this_month:
            break
        months.rotate(-1)
    months.rotate(-1)
    month_plus = list(months)[0]
    for i in range(4):
        try:
            return dt.replace(month=month_plus, day=dt.day - i)
        except ValueError:
            continue

The same end result can be done using itertools.cycle.

import datetime
import itertools

def next_month(dt: datetime.datetime):
    month_list = list(range(1, 12 + 1))
    month = itertools.cycle(month_list)
    while True:
        if next(month) == dt.month:
            break
    month_plus = next(month)
    for i in range(4):
        try:
            return dt.replace(month=month_plus, day=dt.day - i)
        except ValueError:
            continue
Utkonos
  • 631
  • 6
  • 21
0

I actually managed to do this using datetime and MonthDelta

pip install MonthDelta

Then

from datetime import datetime
from monthdelta import monthdelta

# Date and time today, now
now        = datetime.now()

# Date next six months from now
six_months = now + monthdelta(6)

print( now.strftime("%Y-%m-%d") )
print( six_months.strftime("%Y-%m-%d") )
>>> 2023-06-11
>>> 2023-12-11
Amin Matola
  • 137
  • 2
  • 7
-1

In this function, n can be positive or negative.

def addmonth(d, n):
    n += 1
    dd = datetime.date(d.year + n/12, d.month + n%12, 1)-datetime.timedelta(1)
    return datetime.date(dd.year, dd.month, min(d.day, dd.day))
newby
  • 7
  • 2
-1

We probably should use dateutil.relativedelta

however for academic interest I will just add that before I discovered it I was goint to use this:

try:
   vexpDt = K.today.replace(K.today.year + (K.today.month+6)//12, (K.today.month+5)%12+1, K.today.day)
except:
   vexpDt = K.today.replace(K.today.year + (K.today.month+6)//12, (K.today.month+6)%12+1, 1) - timedelta(days = 1)

it seems quite simple but still catches all the issues like 29,30,31

it also works for - 6 mths by doing -timedelta

nb - don't be confused by K.today its just a variable in my program

Frosty Snowman
  • 435
  • 5
  • 5
-1
    def addDay(date, number):
        for i in range(number)
            #try to add a day
            try:
                date = date.replace(day = date.day + 1)
            #in case it's impossible ex:january 32nd add a month and restart at day 1
            except:
                #add month part
                try:
                    date = date.replace(month = date.month +1, day = 1)
                except:
                    date = date.replace(year = date.year +1, month = 1, day = 1)

For everyone still reading this post. I think this code is way clearer, especially compared to code using modulo(%).

Sorry for any grammatical error, english is so not my main language

-1

This is what I do when I need to add months or years and don't want to import more libraries.

import datetime
__author__ = 'Daniel Margarido'


# Check if the int given year is a leap year
# return true if leap year or false otherwise
def is_leap_year(year):
    if (year % 4) == 0:
        if (year % 100) == 0:
            if (year % 400) == 0:
                return True
            else:
                return False
        else:
            return True
    else:
        return False


THIRTY_DAYS_MONTHS = [4, 6, 9, 11]
THIRTYONE_DAYS_MONTHS = [1, 3, 5, 7, 8, 10, 12]

# Inputs -> month, year Booth integers
# Return the number of days of the given month
def get_month_days(month, year):
    if month in THIRTY_DAYS_MONTHS:   # April, June, September, November
        return 30
    elif month in THIRTYONE_DAYS_MONTHS:   # January, March, May, July, August, October, December
        return 31
    else:   # February
        if is_leap_year(year):
            return 29
        else:
            return 28

# Checks the month of the given date
# Selects the number of days it needs to add one month
# return the date with one month added
def add_month(date):
    current_month_days = get_month_days(date.month, date.year)
    next_month_days = get_month_days(date.month + 1, date.year)

    delta = datetime.timedelta(days=current_month_days)
    if date.day > next_month_days:
        delta = delta - datetime.timedelta(days=(date.day - next_month_days) - 1)

    return date + delta


def add_year(date):
    if is_leap_year(date.year):
        delta = datetime.timedelta(days=366)
    else:
        delta = datetime.timedelta(days=365)

    return date + delta


# Validates if the expected_value is equal to the given value
def test_equal(expected_value, value):
    if expected_value == value:
        print "Test Passed"
        return True

    print "Test Failed : " + str(expected_value) + " is not equal to " str(value)
    return False

# Test leap year
print "---------- Test leap year ----------"
test_equal(True, is_leap_year(2012))
test_equal(True, is_leap_year(2000))
test_equal(False, is_leap_year(1900))
test_equal(False, is_leap_year(2002))
test_equal(False, is_leap_year(2100))
test_equal(True, is_leap_year(2400))
test_equal(True, is_leap_year(2016))

# Test add month
print "---------- Test add month ----------"
test_equal(datetime.date(2016, 2, 1), add_month(datetime.date(2016, 1, 1)))
test_equal(datetime.date(2016, 6, 16), add_month(datetime.date(2016, 5, 16)))
test_equal(datetime.date(2016, 3, 15), add_month(datetime.date(2016, 2, 15)))
test_equal(datetime.date(2017, 1, 12), add_month(datetime.date(2016, 12, 12)))
test_equal(datetime.date(2016, 3, 1), add_month(datetime.date(2016, 1, 31)))
test_equal(datetime.date(2015, 3, 1), add_month(datetime.date(2015, 1, 31)))
test_equal(datetime.date(2016, 3, 1), add_month(datetime.date(2016, 1, 30)))
test_equal(datetime.date(2016, 4, 30), add_month(datetime.date(2016, 3, 30)))
test_equal(datetime.date(2016, 5, 1), add_month(datetime.date(2016, 3, 31)))

# Test add year
print "---------- Test add year ----------"
test_equal(datetime.date(2016, 2, 2), add_year(datetime.date(2015, 2, 2)))
test_equal(datetime.date(2001, 2, 2), add_year(datetime.date(2000, 2, 2)))
test_equal(datetime.date(2100, 2, 2), add_year(datetime.date(2099, 2, 2)))
test_equal(datetime.date(2101, 2, 2), add_year(datetime.date(2100, 2, 2)))
test_equal(datetime.date(2401, 2, 2), add_year(datetime.date(2400, 2, 2)))

Just create a datetime.date() object, call add_month(date) to add a month and add_year(date) to add a year.

-1

Using below given function you can get date after/before x months.

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)
  • This answer is an exact duplicate of an answer you posted 8 minutes earlier to this same question. Please delete this redundant answer. – Tom Aranda Dec 07 '17 at 00:19
-1

I could not find an exact solution to this question so i'll post my solution in case it may be of any help using stantard Calendar and datetime libs. this works for add and substract months, and accounts for month-end rolls and cases where the final month has less days than the initial one. I also have a more generalized solution if you are looking for more complex manipulation, it adds regular intervals (days, months, years, quarters, semeters, etc) like: '1m', '-9m', '-1.5y', '-3q', '1s' etc.

from datetime import datetime
from calendar import monthrange
def date_bump_months(start_date, months):
    """
    bumps months back and forth. 
    --> if initial date is end-of-month, i will move to corresponding month-end
    --> ir inital date.day is greater than end of month of final date, it casts it to momth-end
    """
    signbit = -1 if months < 0 else 1
    d_year, d_month = divmod(abs(months),12)    
    end_year = start_date.year + d_year*signbit 
    end_month = 0
    if signbit ==-1:            
        if d_month < start_date.month:
            end_month = start_date.month - d_month
        else:
            end_year -=1
            end_month = 12 - (d_month - start_date.month)
    else:
        end_month +=start_date.month
        if end_month  > 12:
            end_year +=1
            end_month -=12
    # check if we are running end-of-month dates
    eom_run = monthrange(start_date.year, start_date.month)[1]==start_date.day
    eom_month = monthrange((end_year), (end_month))[1]
    if eom_run:
        end_day = eom_month 
    else:
        end_day = min(start_date.day, eom_month )    
    return date(end_year, end_month, end_day)
Bravhek
  • 327
  • 3
  • 10
-1

My modification to Tony Diep's answer, possibly marginally more elegant (Python 2 of course, matching the date of the question & original answers, for Python 3 modify as necessary, including / to // at least):

def add_months(date, months):
    month = date.month + months - 1
    year = date.year + (month / 12)
    month = (month % 12) + 1
    day = date.day
    while (day > 0):
        try:
            new_date = date.replace(year=year, month=month, day=day)
            break
        except:
            day = day - 1    
    return new_date

adds months according to a 'business needs' interpretation that dates mapping beyond the end of the month, should map to the end of the month rather than into the following month.

Mark
  • 2,196
  • 1
  • 14
  • 8
-6

Use the python datetime module to add a timedelta of six months to datetime.today() .

http://docs.python.org/library/datetime.html

You will of course have to solve the issue raised by Johannes Weiß-- what do you mean by 6 months?

Devin Jeanpierre
  • 92,913
  • 4
  • 55
  • 79
  • 4
    timedelta doesn't support months and so sidesteps the possible answers to the question "how many days in 6-month?" Eef's code will set a review date so I would suggest one could consider setting the timedelta using days (6*30). If the period represents clients' access to a product/service then a business definition may be required/preferred. – Carl Sep 21 '09 at 15:46