Python: How to Find the First Day of Every Month Between Two Date Ranges

Generate list of months between interval in python

>>> from datetime import datetime, timedelta
>>> from collections import OrderedDict
>>> dates = ["2014-10-10", "2016-01-07"]
>>> start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
>>> OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()
['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']

Update: a bit of explanation, as requested in one comment. There are three problems here: parsing the dates into appropriate data structures (strptime); getting the date range given the two extremes and the step (one month); formatting the output dates (strftime). The datetime type overloads the subtraction operator, so that end - start makes sense. The result is a timedelta object that represents the difference between the two dates, and the .days attribute gets this difference expressed in days. There is no .months attribute, so we iterate one day at a time and convert the dates to the desired output format. This yields a lot of duplicates, which the OrderedDict removes while keeping the items in the right order.

Now this is simple and concise because it lets the datetime module do all the work, but it's also horribly inefficient. We're calling a lot of methods for each day while we only need to output months. If performance is not an issue, the above code will be just fine. Otherwise, we'll have to work a bit more. Let's compare the above implementation with a more efficient one:

from datetime import datetime, timedelta
from collections import OrderedDict

dates = ["2014-10-10", "2016-01-07"]

def monthlist_short(dates):
start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
return OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()

def monthlist_fast(dates):
start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
total_months = lambda dt: dt.month + 12 * dt.year
mlist = []
for tot_m in xrange(total_months(start)-1, total_months(end)):
y, m = divmod(tot_m, 12)
mlist.append(datetime(y, m+1, 1).strftime("%b-%y"))
return mlist

assert monthlist_fast(dates) == monthlist_short(dates)

if __name__ == "__main__":
from timeit import Timer
for func in "monthlist_short", "monthlist_fast":
print func, Timer("%s(dates)" % func, "from __main__ import dates, %s" % func).timeit(1000)

On my laptop, I get the following output:

monthlist_short 2.3209939003
monthlist_fast 0.0774540901184

The concise implementation is about 30 times slower, so I would not recommend it in time-critical applications :)

How to display all the months between given two dates?

You could use the dateutil extension's relativedelta method like below -

from datetime import datetime
from dateutil.relativedelta import relativedelta

startDate = '2016-1-28'
endDate = '2017-3-26'

cur_date = start = datetime.strptime(startDate, '%Y-%m-%d').date()
end = datetime.strptime(endDate, '%Y-%m-%d').date()

while cur_date < end:
print(cur_date)
cur_date += relativedelta(months=1)

Following is the output

2016-01-28
2016-02-28
2016-03-28
2016-04-28
2016-05-28
2016-06-28
2016-07-28
2016-08-28
2016-09-28
2016-10-28
2016-11-28
2016-12-28
2017-01-28
2017-02-28

Python Find a day dates between a range of two dates

from datetime import date, timedelta, datetime
import time

firstDay = '1-January-2000'
lastDay = '22-february-2000'
weekDay = 'Monday'

firstDay = datetime.strptime(firstDay, '%d-%B-%Y')
lastDay = datetime.strptime(lastDay, '%d-%B-%Y')
dates = [firstDay + timedelta(days=x) for x in range((lastDay-firstDay).days + 1) if (firstDay + timedelta(days=x)).weekday() == time.strptime(weekDay, '%A').tm_wday]

output

dates
[datetime.datetime(2000, 1, 3, 0, 0),
datetime.datetime(2000, 1, 10, 0, 0),
datetime.datetime(2000, 1, 17, 0, 0),
datetime.datetime(2000, 1, 24, 0, 0),
datetime.datetime(2000, 1, 31, 0, 0),
datetime.datetime(2000, 2, 7, 0, 0),
datetime.datetime(2000, 2, 14, 0, 0),
datetime.datetime(2000, 2, 21, 0, 0)]


The output in format weekDay-Month-year

[d.strftime("%A-%B-%Y") for d in dates] 

['Monday-January-2000',
'Monday-January-2000',
'Monday-January-2000',
'Monday-January-2000',
'Monday-January-2000',
'Monday-February-2000',
'Monday-February-2000',
'Monday-February-2000']

output in firstDay format: 1-January-2000

[d.strftime("%-d-%B-%Y") for d in dates]   
['3-January-2000',
'10-January-2000',
'17-January-2000',
'24-January-2000',
'31-January-2000',
'7-February-2000',
'14-February-2000',
'21-February-2000']

Python list of first day of month for given period

>>> startyear = 2014
>>> startmonth = 4
>>> endyear = 2015
>>> endmonth = 2
>>> [datetime.date(m/12, m%12+1, 1) for m in xrange(startyear*12+startmonth-1, endyear*12+endmonth)]
[datetime.date(2014, 4, 1), datetime.date(2014, 5, 1), datetime.date(2014, 6, 1), datetime.date(2014, 7, 1), datetime.date(2014, 8, 1), datetime.date(2014, 9, 1), datetime.date(2014, 10, 1), datetime.date(2014, 11, 1), datetime.date(2014, 12, 1), datetime.date(2015, 1, 1), datetime.date(2015, 2, 1)]

For Python 3, you'll need to use range instead of xrange, and // (floor division) instead of / (which does float division in Python 3):

[datetime.date(m//12, m%12+1, 1) for m in range(startyear*12+startmonth-1, endyear*12+endmonth)]

Best way to find the months between two dates

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.

Python: get all months in range?

dateutil.relativedelta is handy here.

I've left the formatting out as an exercise.

from dateutil.relativedelta import relativedelta
import datetime

result = []

today = datetime.date.today()
current = datetime.date(2010, 8, 1)

while current <= today:
result.append(current)
current += relativedelta(months=1)


Related Topics



Leave a reply



Submit