生成给定期间按月划分的日期范围

Generate date ranges broken by month for a given period

我正在努力编写一个 pythonic 的、干净的生成器方法,给定一个日期周期,比如 ['2014-01-15', '2015-02-03],它会给我这个:

['2014-01-15', '2014-01-31']
['2014-02-01', '2014-02-28']
...
['2015-02-01', '2015-02-03']

这是我想出的:

from datetime import datetime
import calendar

def genDatePeriods(startDate, endDate, format='%Y-%m-%d'):
    dt1 = datetime.strptime(startDate, format)
    dt2 = datetime.strptime(endDate, format)

    for year in range(dt1.year, dt2.year + 1):
        for month in range(1, 13):
            day0 = dt1.day if month == dt1.month and year == dt1.year else 1
            day1 = dt2.day if month == dt2.month and year == dt2.year else calendar.monthrange(year, month)[1]
            if (year == dt1.year and month < dt1.month) or (year == dt2.year and month > dt2.month):
                continue
            else:
                d0 = (year, month, day0)
                d1 = (year, month, day1)
                yield [datetime(*d).strftime(format) for d in [d0, d1]]

它有效,但我觉得还有更多 pythonic/tidy/efficient 方法可以做到这一点。有什么想法吗?

下面就简洁多了,每次使用datetime.date()个对象来查找下个月的第一天,直到到达结束日期:

from datetime import datetime, timedelta

def genDatePeriods(startDate, endDate, format='%Y-%m-%d'):
    curr = datetime.strptime(startDate, format).date()
    end = datetime.strptime(endDate, format).date()

    while curr <= end:
        # first day of the next month, using modular arithmetic
        next_month = curr.replace(
            month=curr.month % 12 + 1, year=curr.year + curr.month // 12,
            day=1)
        curr_formatted = curr.strftime(format)
        # end date is next month's first day, minus one day,
        # or the given endDate, whichever comes first
        end_formatted = min(next_month - timedelta(days=1), end).strftime(format)
        yield [curr_formatted, end_formatted]
        curr = next_month

演示:

>>> for res in genDatePeriods('2014-01-15', '2015-02-03'):
...     print res
... 
['2014-01-15', '2014-01-31']
['2014-02-01', '2014-02-28']
['2014-03-01', '2014-03-31']
['2014-04-01', '2014-04-30']
['2014-05-01', '2014-05-31']
['2014-06-01', '2014-06-30']
['2014-07-01', '2014-07-31']
['2014-08-01', '2014-08-31']
['2014-09-01', '2014-09-30']
['2014-10-01', '2014-10-31']
['2014-11-01', '2014-11-30']
['2014-12-01', '2014-12-31']
['2015-01-01', '2015-01-31']
['2015-02-01', '2015-02-03']