This question already has answers here:
How do I calculate the date six months from the current date using the datetime Python module?
(47 answers)
Closed 7 years ago.
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?
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 will require python-dateutil module. If you are on Linux you need to run this command in the terminal in order to install it.
sudo apt-get update && sudo apt-get install python-dateutil
Explanation : Add month value in python
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.
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 :)
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)
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
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)
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
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)
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)
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)
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)
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
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()
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)
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))
def add_month(d,n=1): return type(d)(d.year+(d.month+n-1)/12, (d.month+n-1)%12+1, 1)
Just Use This:
import datetime
today = datetime.datetime.today()
nextMonthDatetime = today + datetime.timedelta(days=(today.max.day - today.day)+1)
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))
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)
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]))
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
I have the need to be able to accurately find the months between two dates in python. I have a solution that works but its not very good (as in elegant) or fast.
dateRange = [datetime.strptime(dateRanges[0], "%Y-%m-%d"), datetime.strptime(dateRanges[1], "%Y-%m-%d")]
months = []
tmpTime = dateRange[0]
oneWeek = timedelta(weeks=1)
tmpTime = tmpTime.replace(day=1)
dateRange[0] = tmpTime
dateRange[1] = dateRange[1].replace(day=1)
lastMonth = tmpTime.month
months.append(tmpTime)
while tmpTime < dateRange[1]:
if lastMonth != 12:
while tmpTime.month <= lastMonth:
tmpTime += oneWeek
tmpTime = tmpTime.replace(day=1)
months.append(tmpTime)
lastMonth = tmpTime.month
else:
while tmpTime.month >= lastMonth:
tmpTime += oneWeek
tmpTime = tmpTime.replace(day=1)
months.append(tmpTime)
lastMonth = tmpTime.month
So just to explain, what I'm doing here is taking the two dates and converting them from iso format into python datetime objects. Then I loop through adding a week to the start datetime object and check if the numerical value of the month is greater (unless the month is December then it checks if the date is less), If the value is greater I append it to the list of months and keep looping through until I get to my end date.
It works perfectly it just doesn't seem like a good way of doing it...
Start by defining some test cases, then you will see that the function is very simple and needs no loops
from datetime import datetime
def diff_month(d1, d2):
return (d1.year - d2.year) * 12 + d1.month - d2.month
assert diff_month(datetime(2010,10,1), datetime(2010,9,1)) == 1
assert diff_month(datetime(2010,10,1), datetime(2009,10,1)) == 12
assert diff_month(datetime(2010,10,1), datetime(2009,11,1)) == 11
assert diff_month(datetime(2010,10,1), datetime(2009,8,1)) == 14
You should add some test cases to your question, as there are lots of potential corner cases to cover - there is more than one way to define the number of months between two dates.
One liner to find a list of datetimes, incremented by month, between two dates.
import datetime
from dateutil.rrule import rrule, MONTHLY
strt_dt = datetime.date(2001,1,1)
end_dt = datetime.date(2005,6,1)
dates = [dt for dt in rrule(MONTHLY, dtstart=strt_dt, until=end_dt)]
This worked for me -
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime('2011-08-15 12:00:00', '%Y-%m-%d %H:%M:%S')
date2 = datetime.strptime('2012-02-15', '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months + (12*r.years)
from dateutil import relativedelta
r = relativedelta.relativedelta(date1, date2)
months_difference = (r.years * 12) + r.months
You can easily calculate this using rrule from dateutil module:
from dateutil import rrule
from datetime import date
print(list(rrule.rrule(rrule.MONTHLY, dtstart=date(2013, 11, 1), until=date(2014, 2, 1))))
will give you:
[datetime.datetime(2013, 11, 1, 0, 0),
datetime.datetime(2013, 12, 1, 0, 0),
datetime.datetime(2014, 1, 1, 0, 0),
datetime.datetime(2014, 2, 1, 0, 0)]
Get the ending month (relative to the year and month of the start month ex: 2011 January = 13 if your start date starts on 2010 Oct) and then generate the datetimes beginning the start month and that end month like so:
dt1, dt2 = dateRange
start_month=dt1.month
end_months=(dt2.year-dt1.year)*12 + dt2.month+1
dates=[datetime.datetime(year=yr, month=mn, day=1) for (yr, mn) in (
((m - 1) / 12 + dt1.year, (m - 1) % 12 + 1) for m in range(start_month, end_months)
)]
if both dates are on the same year, it could also be simply written as:
dates=[datetime.datetime(year=dt1.year, month=mn, day=1) for mn in range(dt1.month, dt2.month + 1)]
My simple solution:
import datetime
def months(d1, d2):
return d1.month - d2.month + 12*(d1.year - d2.year)
d1 = datetime.datetime(2009, 9, 26)
d2 = datetime.datetime(2019, 9, 26)
print(months(d1, d2))
This post nails it! Use dateutil.relativedelta.
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime(str('2011-08-15 12:00:00'), '%Y-%m-%d %H:%M:%S')
date2 = datetime.strptime(str('2012-02-15'), '%Y-%m-%d')
r = relativedelta.relativedelta(date2, date1)
r.months
Update 2018-04-20: it seems that OP #Joshkunz was asking for finding which months are between two dates, instead of "how many months" are between two dates. So I am not sure why #JohnLaRooy is upvoted for more than 100 times. #Joshkunz indicated in the comment under the original question he wanted the actual dates [or the months], instead of finding the total number of months.
So it appeared the question wanted, for between two dates 2018-04-11 to 2018-06-01
Apr 2018, May 2018, June 2018
And what if it is between 2014-04-11 to 2018-06-01? Then the answer would be
Apr 2014, May 2014, ..., Dec 2014, Jan 2015, ..., Jan 2018, ..., June 2018
So that's why I had the following pseudo code many years ago. It merely suggested using the two months as end points and loop through them, incrementing by one month at a time. #Joshkunz mentioned he wanted the "months" and he also mentioned he wanted the "dates", without knowing exactly, it was difficult to write the exact code, but the idea is to use one simple loop to loop through the end points, and incrementing one month at a time.
The answer 8 years ago in 2010:
If adding by a week, then it will approximately do work 4.35 times the work as needed. Why not just:
1. get start date in array of integer, set it to i: [2008, 3, 12],
and change it to [2008, 3, 1]
2. get end date in array: [2010, 10, 26]
3. add the date to your result by parsing i
increment the month in i
if month is >= 13, then set it to 1, and increment the year by 1
until either the year in i is > year in end_date,
or (year in i == year in end_date and month in i > month in end_date)
just pseduo code for now, haven't tested, but i think the idea along the same line will work.
Define a "month" as 1/12 year, then do this:
def month_diff(d1, d2):
"""Return the number of months between d1 and d2,
such that d2 + month_diff(d1, d2) == d1
"""
diff = (12 * d1.year + d1.month) - (12 * d2.year + d2.month)
return diff
You might try to define a month as "a period of either 29, 28, 30 or 31 days (depending on the year)". But you you do that, you have an additional problem to solve.
While it's usually clear that June 15th + 1 month should be July 15th, it's not usually not clear if January 30th + 1 month is in February or March. In the latter case, you may be compelled to compute the date as February 30th, then "correct" it to March 2nd. But when you do that, you'll find that March 2nd - 1 month is clearly February 2nd. Ergo, reductio ad absurdum (this operation is not well defined).
Here's how to do this with Pandas FWIW:
import pandas as pd
pd.date_range("1990/04/03", "2014/12/31", freq="MS")
DatetimeIndex(['1990-05-01', '1990-06-01', '1990-07-01', '1990-08-01',
'1990-09-01', '1990-10-01', '1990-11-01', '1990-12-01',
'1991-01-01', '1991-02-01',
...
'2014-03-01', '2014-04-01', '2014-05-01', '2014-06-01',
'2014-07-01', '2014-08-01', '2014-09-01', '2014-10-01',
'2014-11-01', '2014-12-01'],
dtype='datetime64[ns]', length=296, freq='MS')
Notice it starts with the month after the given start date.
Many people have already given you good answers to solve this but I have not read any using list comprehension so I give you what I used for a similar use case :
def compute_months(first_date, second_date):
year1, month1, year2, month2 = map(
int,
(first_date[:4], first_date[5:7], second_date[:4], second_date[5:7])
)
return [
'{:0>4}-{:0>2}'.format(year, month)
for year in range(year1, year2 + 1)
for month in range(month1 if year == year1 else 1, month2 + 1 if year == year2 else 13)
]
>>> first_date = "2016-05"
>>> second_date = "2017-11"
>>> compute_months(first_date, second_date)
['2016-05',
'2016-06',
'2016-07',
'2016-08',
'2016-09',
'2016-10',
'2016-11',
'2016-12',
'2017-01',
'2017-02',
'2017-03',
'2017-04',
'2017-05',
'2017-06',
'2017-07',
'2017-08',
'2017-09',
'2017-10',
'2017-11']
There is a simple solution based on 360 day years, where all months have 30 days.
It fits most use cases where, given two dates, you need to calculate the number of full months plus the remaining days.
from datetime import datetime, timedelta
def months_between(start_date, end_date):
#Add 1 day to end date to solve different last days of month
s1, e1 = start_date , end_date + timedelta(days=1)
#Convert to 360 days
s360 = (s1.year * 12 + s1.month) * 30 + s1.day
e360 = (e1.year * 12 + e1.month) * 30 + e1.day
#Count days between the two 360 dates and return tuple (months, days)
return divmod(e360 - s360, 30)
print "Counting full and half months"
print months_between( datetime(2012, 01, 1), datetime(2012, 03, 31)) #3m
print months_between( datetime(2012, 01, 1), datetime(2012, 03, 15)) #2m 15d
print months_between( datetime(2012, 01, 16), datetime(2012, 03, 31)) #2m 15d
print months_between( datetime(2012, 01, 16), datetime(2012, 03, 15)) #2m
print "Adding +1d and -1d to 31 day month"
print months_between( datetime(2011, 12, 01), datetime(2011, 12, 31)) #1m 0d
print months_between( datetime(2011, 12, 02), datetime(2011, 12, 31)) #-1d => 29d
print months_between( datetime(2011, 12, 01), datetime(2011, 12, 30)) #30d => 1m
print "Adding +1d and -1d to 29 day month"
print months_between( datetime(2012, 02, 01), datetime(2012, 02, 29)) #1m 0d
print months_between( datetime(2012, 02, 02), datetime(2012, 02, 29)) #-1d => 29d
print months_between( datetime(2012, 02, 01), datetime(2012, 02, 28)) #28d
print "Every month has 30 days - 26/M to 5/M+1 always counts 10 days"
print months_between( datetime(2011, 02, 26), datetime(2011, 03, 05))
print months_between( datetime(2012, 02, 26), datetime(2012, 03, 05))
print months_between( datetime(2012, 03, 26), datetime(2012, 04, 05))
Somewhat a little prettified solution by #Vin-G.
import datetime
def monthrange(start, finish):
months = (finish.year - start.year) * 12 + finish.month + 1
for i in xrange(start.month, months):
year = (i - 1) / 12 + start.year
month = (i - 1) % 12 + 1
yield datetime.date(year, month, 1)
You can also use the arrow library. This is a simple example:
from datetime import datetime
import arrow
start = datetime(2014, 1, 17)
end = datetime(2014, 6, 20)
for d in arrow.Arrow.range('month', start, end):
print d.month, d.format('MMMM')
This will print:
1 January
2 February
3 March
4 April
5 May
6 June
Hope this helps!
Get difference in number of days, months and years between two dates.
import datetime
from dateutil.relativedelta import relativedelta
iphead_proc_dt = datetime.datetime.now()
new_date = iphead_proc_dt + relativedelta(months=+25, days=+23)
# Get Number of Days difference bewtween two dates
print((new_date - iphead_proc_dt).days)
difference = relativedelta(new_date, iphead_proc_dt)
# Get Number of Months difference bewtween two dates
print(difference.months + 12 * difference.years)
# Get Number of Years difference bewtween two dates
print(difference.years)
Try something like this. It presently includes the month if both dates happen to be in the same month.
from datetime import datetime,timedelta
def months_between(start,end):
months = []
cursor = start
while cursor <= end:
if cursor.month not in months:
months.append(cursor.month)
cursor += timedelta(weeks=1)
return months
Output looks like:
>>> start = datetime.now() - timedelta(days=120)
>>> end = datetime.now()
>>> months_between(start,end)
[6, 7, 8, 9, 10]
You could use python-dateutil. See Python: Difference of 2 datetimes in months
just like range function, when month is 13, go to next year
def year_month_range(start_date, end_date):
'''
start_date: datetime.date(2015, 9, 1) or datetime.datetime
end_date: datetime.date(2016, 3, 1) or datetime.datetime
return: datetime.date list of 201509, 201510, 201511, 201512, 201601, 201602
'''
start, end = start_date.strftime('%Y%m'), end_date.strftime('%Y%m')
assert len(start) == 6 and len(end) == 6
start, end = int(start), int(end)
year_month_list = []
while start < end:
year, month = divmod(start, 100)
if month == 13:
start += 88 # 201513 + 88 = 201601
continue
year_month_list.append(datetime.date(year, month, 1))
start += 1
return year_month_list
example in python shell
>>> import datetime
>>> s = datetime.date(2015,9,1)
>>> e = datetime.date(2016, 3, 1)
>>> year_month_set_range(s, e)
[datetime.date(2015, 11, 1), datetime.date(2015, 9, 1), datetime.date(2016, 1, 1), datetime.date(2016, 2, 1),
datetime.date(2015, 12, 1), datetime.date(2015, 10, 1)]
It can be done using datetime.timedelta, where the number of days for skipping to next month can be obtained by calender.monthrange. monthrange returns weekday (0-6 ~ Mon-Sun) and number of days (28-31) for a given year and month.
For example: monthrange(2017, 1) returns (6,31).
Here is the script using this logic to iterate between two months.
from datetime import timedelta
import datetime as dt
from calendar import monthrange
def month_iterator(start_month, end_month):
start_month = dt.datetime.strptime(start_month,
'%Y-%m-%d').date().replace(day=1)
end_month = dt.datetime.strptime(end_month,
'%Y-%m-%d').date().replace(day=1)
while start_month <= end_month:
yield start_month
start_month = start_month + timedelta(days=monthrange(start_month.year,
start_month.month)[1])
`
it seems that the answers are unsatisfactory and I have since use my own code which is easier to understand
from datetime import datetime
from dateutil import relativedelta
date1 = datetime.strptime(str('2017-01-01'), '%Y-%m-%d')
date2 = datetime.strptime(str('2019-03-19'), '%Y-%m-%d')
difference = relativedelta.relativedelta(date2, date1)
months = difference.months
years = difference.years
# add in the number of months (12) for difference in years
months += 12 * difference.years
months
from datetime import datetime
from dateutil import relativedelta
def get_months(d1, d2):
date1 = datetime.strptime(str(d1), '%Y-%m-%d')
date2 = datetime.strptime(str(d2), '%Y-%m-%d')
print (date2, date1)
r = relativedelta.relativedelta(date2, date1)
months = r.months + 12 * r.years
if r.days > 0:
months += 1
print (months)
return months
assert get_months('2018-08-13','2019-06-19') == 11
assert get_months('2018-01-01','2019-06-19') == 18
assert get_months('2018-07-20','2019-06-19') == 11
assert get_months('2018-07-18','2019-06-19') == 12
assert get_months('2019-03-01','2019-06-19') == 4
assert get_months('2019-03-20','2019-06-19') == 3
assert get_months('2019-01-01','2019-06-19') == 6
assert get_months('2018-09-09','2019-06-19') == 10
#This definition gives an array of months between two dates.
import datetime
def MonthsBetweenDates(BeginDate, EndDate):
firstyearmonths = [mn for mn in range(BeginDate.month, 13)]<p>
lastyearmonths = [mn for mn in range(1, EndDate.month+1)]<p>
months = [mn for mn in range(1, 13)]<p>
numberofyearsbetween = EndDate.year - BeginDate.year - 1<p>
return firstyearmonths + months * numberofyearsbetween + lastyearmonths<p>
#example
BD = datetime.datetime.strptime("2000-35", '%Y-%j')
ED = datetime.datetime.strptime("2004-200", '%Y-%j')
MonthsBetweenDates(BD, ED)
Usually 90 days are NOT 3 months literally, just a reference.
So, finally, you need to check if days are bigger than 15 to add +1 to month counter. or better, add another elif with half month counter.
From this other stackoverflow answer i've finally ended with that:
#/usr/bin/env python
# -*- coding: utf8 -*-
import datetime
from datetime import timedelta
from dateutil.relativedelta import relativedelta
import calendar
start_date = datetime.date.today()
end_date = start_date + timedelta(days=111)
start_month = calendar.month_abbr[int(start_date.strftime("%m"))]
print str(start_date) + " to " + str(end_date)
months = relativedelta(end_date, start_date).months
days = relativedelta(end_date, start_date).days
print months, "months", days, "days"
if days > 16:
months += 1
print "around " + str(months) + " months", "(",
for i in range(0, months):
print calendar.month_abbr[int(start_date.strftime("%m"))],
start_date = start_date + relativedelta(months=1)
print ")"
Output:
2016-02-29 2016-06-14
3 months 16 days
around 4 months ( Feb Mar Apr May )
I've noticed that doesn't work if you add more than days left in current year, and that's is unexpected.
Here is my solution for this:
def calc_age_months(from_date, to_date):
from_date = time.strptime(from_date, "%Y-%m-%d")
to_date = time.strptime(to_date, "%Y-%m-%d")
age_in_months = (to_date.tm_year - from_date.tm_year)*12 + (to_date.tm_mon - from_date.tm_mon)
if to_date.tm_mday < from_date.tm_mday:
return age_in_months -1
else
return age_in_months
This will handle some edge cases as well where the difference in months between 31st Dec 2018 and 1st Jan 2019 will be zero (since the difference is only a day).
Assuming upperDate is always later than lowerDate and both are datetime.date objects:
if lowerDate.year == upperDate.year:
monthsInBetween = range( lowerDate.month + 1, upperDate.month )
elif upperDate.year > lowerDate.year:
monthsInBetween = range( lowerDate.month + 1, 12 )
for year in range( lowerDate.year + 1, upperDate.year ):
monthsInBetween.extend( range(1,13) )
monthsInBetween.extend( range( 1, upperDate.month ) )
I haven't tested this thoroughly, but it looks like it should do the trick.
Here is a method:
def months_between(start_dt, stop_dt):
month_list = []
total_months = 12*(stop_dt.year-start_dt.year)+(stop_dt.month-start_d.month)+1
if total_months > 0:
month_list=[ datetime.date(start_dt.year+int((start_dt+i-1)/12),
((start_dt-1+i)%12)+1,
1) for i in xrange(0,total_months) ]
return month_list
This is first computing the total number of months between the two dates, inclusive. Then it creates a list using the first date as the base and performs modula arithmetic to create the date objects.
I actually needed to do something pretty similar just now
Ended up writing a function which returns a list of tuples indicating the start and end of each month between two sets of dates so I could write some SQL queries off the back of it for monthly totals of sales etc.
I'm sure it can be improved by someone who knows what they're doing but hope it helps...
The returned value look as follows (generating for today - 365days until today as an example)
[ (datetime.date(2013, 5, 1), datetime.date(2013, 5, 31)),
(datetime.date(2013, 6, 1), datetime.date(2013, 6, 30)),
(datetime.date(2013, 7, 1), datetime.date(2013, 7, 31)),
(datetime.date(2013, 8, 1), datetime.date(2013, 8, 31)),
(datetime.date(2013, 9, 1), datetime.date(2013, 9, 30)),
(datetime.date(2013, 10, 1), datetime.date(2013, 10, 31)),
(datetime.date(2013, 11, 1), datetime.date(2013, 11, 30)),
(datetime.date(2013, 12, 1), datetime.date(2013, 12, 31)),
(datetime.date(2014, 1, 1), datetime.date(2014, 1, 31)),
(datetime.date(2014, 2, 1), datetime.date(2014, 2, 28)),
(datetime.date(2014, 3, 1), datetime.date(2014, 3, 31)),
(datetime.date(2014, 4, 1), datetime.date(2014, 4, 30)),
(datetime.date(2014, 5, 1), datetime.date(2014, 5, 31))]
Code as follows (has some debug stuff which can be removed):
#! /usr/env/python
import datetime
def gen_month_ranges(start_date=None, end_date=None, debug=False):
today = datetime.date.today()
if not start_date: start_date = datetime.datetime.strptime(
"{0}/01/01".format(today.year),"%Y/%m/%d").date() # start of this year
if not end_date: end_date = today
if debug: print("Start: {0} | End {1}".format(start_date, end_date))
# sense-check
if end_date < start_date:
print("Error. Start Date of {0} is greater than End Date of {1}?!".format(start_date, end_date))
return None
date_ranges = [] # list of tuples (month_start, month_end)
current_year = start_date.year
current_month = start_date.month
while current_year <= end_date.year:
next_month = current_month + 1
next_year = current_year
if next_month > 12:
next_month = 1
next_year = current_year + 1
month_start = datetime.datetime.strptime(
"{0}/{1}/01".format(current_year,
current_month),"%Y/%m/%d").date() # start of month
month_end = datetime.datetime.strptime(
"{0}/{1}/01".format(next_year,
next_month),"%Y/%m/%d").date() # start of next month
month_end = month_end+datetime.timedelta(days=-1) # start of next month less one day
range_tuple = (month_start, month_end)
if debug: print("Month runs from {0} --> {1}".format(
range_tuple[0], range_tuple[1]))
date_ranges.append(range_tuple)
if current_month == 12:
current_month = 1
current_year += 1
if debug: print("End of year encountered, resetting months")
else:
current_month += 1
if debug: print("Next iteration for {0}-{1}".format(
current_year, current_month))
if current_year == end_date.year and current_month > end_date.month:
if debug: print("Final month encountered. Terminating loop")
break
return date_ranges
if __name__ == '__main__':
print("Running in standalone mode. Debug set to True")
from pprint import pprint
pprint(gen_month_ranges(debug=True), indent=4)
pprint(gen_month_ranges(start_date=datetime.date.today()+datetime.timedelta(days=-365),
debug=True), indent=4)
Assuming that you wanted to know the "fraction" of the month that dates were in, which I did, then you need to do a bit more work.
from datetime import datetime, date
import calendar
def monthdiff(start_period, end_period, decimal_places = 2):
if start_period > end_period:
raise Exception('Start is after end')
if start_period.year == end_period.year and start_period.month == end_period.month:
days_in_month = calendar.monthrange(start_period.year, start_period.month)[1]
days_to_charge = end_period.day - start_period.day+1
diff = round(float(days_to_charge)/float(days_in_month), decimal_places)
return diff
months = 0
# we have a start date within one month and not at the start, and an end date that is not
# in the same month as the start date
if start_period.day > 1:
last_day_in_start_month = calendar.monthrange(start_period.year, start_period.month)[1]
days_to_charge = last_day_in_start_month - start_period.day +1
months = months + round(float(days_to_charge)/float(last_day_in_start_month), decimal_places)
start_period = datetime(start_period.year, start_period.month+1, 1)
last_day_in_last_month = calendar.monthrange(end_period.year, end_period.month)[1]
if end_period.day != last_day_in_last_month:
# we have lest days in the last month
months = months + round(float(end_period.day) / float(last_day_in_last_month), decimal_places)
last_day_in_previous_month = calendar.monthrange(end_period.year, end_period.month - 1)[1]
end_period = datetime(end_period.year, end_period.month - 1, last_day_in_previous_month)
#whatever happens, we now have a period of whole months to calculate the difference between
if start_period != end_period:
months = months + (end_period.year - start_period.year) * 12 + (end_period.month - start_period.month) + 1
# just counter for any final decimal place manipulation
diff = round(months, decimal_places)
return diff
assert monthdiff(datetime(2015,1,1), datetime(2015,1,31)) == 1
assert monthdiff(datetime(2015,1,1), datetime(2015,02,01)) == 1.04
assert monthdiff(datetime(2014,1,1), datetime(2014,12,31)) == 12
assert monthdiff(datetime(2014,7,1), datetime(2015,06,30)) == 12
assert monthdiff(datetime(2015,1,10), datetime(2015,01,20)) == 0.35
assert monthdiff(datetime(2015,1,10), datetime(2015,02,20)) == 0.71 + 0.71
assert monthdiff(datetime(2015,1,31), datetime(2015,02,01)) == round(1.0/31.0,2) + round(1.0/28.0,2)
assert monthdiff(datetime(2013,1,31), datetime(2015,02,01)) == 12*2 + round(1.0/31.0,2) + round(1.0/28.0,2)
provides an example that works out the number of months between two dates inclusively, including the fraction of each month that the date is in. This means that you can work out how many months is between 2015-01-20 and 2015-02-14, where the fraction of the date in the month of January is determined by the number of days in January; or equally taking into account that the number of days in February can change form year to year.
For my reference, this code is also on github - https://gist.github.com/andrewyager/6b9284a4f1cdb1779b10
Try this:
dateRange = [datetime.strptime(dateRanges[0], "%Y-%m-%d"),
datetime.strptime(dateRanges[1], "%Y-%m-%d")]
delta_time = max(dateRange) - min(dateRange)
#Need to use min(dateRange).month to account for different length month
#Note that timedelta returns a number of days
delta_datetime = (datetime(1, min(dateRange).month, 1) + delta_time -
timedelta(days=1)) #min y/m/d are 1
months = ((delta_datetime.year - 1) * 12 + delta_datetime.month -
min(dateRange).month)
print months
Shouldn't matter what order you input the dates, and it takes into account the difference in month lengths.