How to create a month iterator
months
function using the dateutil
module
from dateutil.rrule import rrule, MONTHLY
from datetime import datetime
def months(start_month, start_year, end_month, end_year):
start = datetime(start_year, start_month, 1)
end = datetime(end_year, end_month, 1)
return [(d.month, d.year) for d in rrule(MONTHLY, dtstart=start, until=end)]
Example Usage
print months(11, 2010, 2, 2011)
#[(11, 2010), (12, 2010), (1, 2011), (2, 2011)]
Or in iterator form
def month_iter(start_month, start_year, end_month, end_year):
start = datetime(start_year, start_month, 1)
end = datetime(end_year, end_month, 1)
return ((d.month, d.year) for d in rrule(MONTHLY, dtstart=start, until=end))
Iterator usage
for m in month_iter(11, 2010, 2, 2011):
print m
#(11, 2010)
#(12, 2010)
#(1, 2011)
#(2, 2011)
Perhaps the elegance or speed of this could be improved, but it's the straightforward solution:
def months(start_month, start_year, end_month, end_year):
month, year = start_month, start_year
while True:
yield month, year
if (month, year) == (end_month, end_year):
return
month += 1
if (month > 12):
month = 1
year += 1
EDIT: And here's a less straightforward one that avoids even needing to use yield
by using a generator comprehension:
def months2(start_month, start_year, end_month, end_year):
return (((m_y % 12) + 1, m_y / 12) for m_y in
range(12 * start_year + start_month - 1, 12 * end_year + end_month))
Since others have already provided code for generators, I wanted to add that Pandas has a method called 'period_range' that, in this case, can take in a starting and end, year and month, and return a period index, suitable for iteration.
import pandas as pd
pr = pd.period_range(start='2010-08',end='2011-03', freq='M')
prTupes=tuple([(period.month,period.year) for period in pr])
#This returns: ((8, 2010), (9, 2010), (10, 2010), (11, 2010), (12, 2010), (1, 2011), (2, 2011), (3, 2011))
The calendar works like this.
def month_year_iter( start_month, start_year, end_month, end_year ):
ym_start= 12*start_year + start_month - 1
ym_end= 12*end_year + end_month - 1
for ym in range( ym_start, ym_end ):
y, m = divmod( ym, 12 )
yield y, m+1
All multiple-unit things work like this. Feet and Inches, Hours, Minutes and Seconds, etc., etc. The only thing that's not this simple is months-days or months-weeks because months are irregular. Everything else is regular, and you need to work in the finest-grained units.