RailsSon
RailsSon

Reputation: 20637

How do I calculate the date six months from the current date using the datetime Python module?

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.

Upvotes: 626

Views: 813576

Answers (30)

Nandlal Yadav
Nandlal Yadav

Reputation: 21

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)

Upvotes: -1

Frosty Snowman
Frosty Snowman

Reputation: 525

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

Upvotes: 0

Ghorban M. Tavakoly
Ghorban M. Tavakoly

Reputation: 1249

OP mentions Python datetime module.

Here is my implementation by just using Python datetime class from datetime module:

from datetime import datetime

MONTH_DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]


def six_months_later(dt: datetime) -> datetime:
    if dt.month <= 6:
        year = dt.year
        month = dt.month + 6
    else:
        year = dt.year + 1
        month = dt.month - 6 
    if month == 2 and year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
        day = min(dt.day, 29)
    else:
        day = min(dt.day, MONTH_DAYS[month])
    return dt.replace(year=year, month=month, day=day)

With help of built-in Python calendar module:

from calendar import monthrange
from datetime import datetime


def six_months_later(dt: datetime) -> datetime:
    if dt.month <= 6:
        year = dt.year
        month = dt.month + 6
    else:
        year = dt.year + 1
        month = dt.month - 6 
    day = min(dt.day, monthrange(year, month)[1])
    return dt.replace(year=year, month=month, day=day)

Both codes returns exact month day of six months later. If that day does not exist (March 31 + 6 Months), it returns last day of the month (September 30).

Leap years considered in code, too.

Here is test code with results added as docstring:

if __name__ == '__main__':
    test_ymds = {
        (2020,  5, 10): (2020, 11, 10),
        (2020,  7, 10): (2021,  1, 10),
        (2020,  1, 31): (2020,  7, 31),
        (2020,  3, 31): (2020,  9, 30),
        (2020,  5, 31): (2020, 11, 30),
        (2020,  8, 31): (2021,  2, 28),
        (2020, 10, 31): (2021,  4, 30),
        (2020, 11, 30): (2021,  5, 30),
        (2020, 12, 31): (2021,  6, 30),
        (2023,  8, 28): (2024,  2, 28),
        (2023,  8, 29): (2024,  2, 29),
        (2023,  8, 30): (2024,  2, 29),
        (2023,  8, 31): (2024,  2, 29),
    }

    print('test       expected   calculated is_correct?')
    print('---------- ---------- ---------- -----------')
    for test_ymd, expected_ymd in test_ymds.items():
        dt = datetime(*test_ymd)
        expected = datetime(*expected_ymd)
        calculated = six_months_later(dt)
        print(dt.date(), calculated.date(), expected.date(), expected==calculated)

'''
test       expected   calculated is_correct?
---------- ---------- ---------- -----------
2020-05-10 2020-11-10 2020-11-10 True
2020-07-10 2021-01-10 2021-01-10 True
2020-01-31 2020-07-31 2020-07-31 True
2020-03-31 2020-09-30 2020-09-30 True
2020-05-31 2020-11-30 2020-11-30 True
2020-08-31 2021-02-28 2021-02-28 True
2020-10-31 2021-04-30 2021-04-30 True
2020-11-30 2021-05-30 2021-05-30 True
2020-12-31 2021-06-30 2021-06-30 True
2023-08-28 2024-02-28 2024-02-28 True
2023-08-29 2024-02-29 2024-02-29 True
2023-08-30 2024-02-29 2024-02-29 True
2023-08-31 2024-02-29 2024-02-29 True
'''

Upvotes: 0

Mike
Mike

Reputation: 51

The native python datetime module does not provide an algorithm to add or subtract months as a unit.

I propose two categories of solution:

  • Write an algorithm using datetime.timedelta

  • Use a 3rd party library which implements an algorithm

Here’s several algorithms I’ve used in production:

  1. Add 183 days (1/2 of 365.25 rounded to nearest integer)
  2. Add 180 days (this is called bond basis or 30/360 convention)
  3. Hold the day of the week constant (aka Weekday Anniversary)
  4. Hold the day of the month constant (aka Monthday Anniversary), aka EOM rule or end-to-end rule

#4 is the most common use case unless you are working with bond math in the US and Europe.

But, you have an edge case where you roll the 31st monthday 6 months forward into a month with only 30 days. This gives you two options:

  1. Assume all months have 31 days, go to the 6th 31st, and use the 30th to stay in that same month
  2. Assume all months have 31 days, go to the 6th 31st, and use the 1st of the next month

Most commonly used, in my opinion, is use the #1 above (call it solution 4.1)

Regarding third-party libraries:

1 python-dateutil

from datetime import date
from dateutil.relativedelta import relative delta as rd

d = date(2024,4,15)
review_date = d + rd(months=6)

dateutil implements solution 4.1

2 dateroll (I am the maintainer)

from dateroll import ddh, Date

d = Date(2024,4,15)
review_date = d + '6m'

dateutil implements solution 4.1.

Further, the above answer uses string-to-date upcasting as syntactic sugar. There are several other ways to accomplish this if you are interested in the docs

Upvotes: 1

Amin Matola
Amin Matola

Reputation: 177

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

Upvotes: 1

Utkonos
Utkonos

Reputation: 795

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

Upvotes: 0

user417751
user417751

Reputation: 317

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

Upvotes: 20

vartec
vartec

Reputation: 134681

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)
    

Upvotes: 76

Arpan Saini
Arpan Saini

Reputation: 5219

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

Upvotes: 23

hypertown
hypertown

Reputation: 43

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

Upvotes: 0

Jolbas
Jolbas

Reputation: 752

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

Upvotes: 2

Breno Monteiro
Breno Monteiro

Reputation: 101

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

Upvotes: 2

Johannes Weiss
Johannes Weiss

Reputation: 54081

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

Upvotes: 18

rpanai
rpanai

Reputation: 13447

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

Upvotes: 22

Diego Magalh&#227;es
Diego Magalh&#227;es

Reputation: 1762

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)

Upvotes: 5

pi.
pi.

Reputation: 21582

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

Upvotes: 14

Mark
Mark

Reputation: 2321

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.

Upvotes: -1

perfecto25
perfecto25

Reputation: 852

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)

Upvotes: 2

Luka Lopusina
Luka Lopusina

Reputation: 2647

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

Upvotes: 67

David Ragazzi
David Ragazzi

Reputation: 310

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

Upvotes: 11

Daniel MM. Kamani
Daniel MM. Kamani

Reputation: 885

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)

Upvotes: 0

Bravhek
Bravhek

Reputation: 331

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)

Upvotes: -1

Nandlal Yadav
Nandlal Yadav

Reputation: 21

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)

Upvotes: 2

blacknight12321
blacknight12321

Reputation: 91

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)

Upvotes: 2

breddi
breddi

Reputation: 217

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)

Upvotes: 20

bii811
bii811

Reputation: 1

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 

Upvotes: 0

Mahendra
Mahendra

Reputation: 13995

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)

Upvotes: 1382

Bruce Jakeway
Bruce Jakeway

Reputation: 21

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)

Upvotes: 2

Daniel Margarido
Daniel Margarido

Reputation: 75

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.

Upvotes: -2

Babak K
Babak K

Reputation: 469

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.

Upvotes: 2

Related Questions