Python: get all months in range?

后端 未结 9 1175
天涯浪人
天涯浪人 2020-12-15 18:00

I want to get all months between now and August 2010, as a list formatted like this:

[\'2010-08-01\', \'2010-09-01\', .... , \'2016-02-01\']
<
相关标签:
9条回答
  • 2020-12-15 18:23

    It's seems like there's a very simple and clean way to do this by generating a list of dates and subsetting to take only the first day of each month, as shown in the example below.

    import datetime
    import pandas as pd
    
    start_date = datetime.date(2010,8,1)
    end_date = datetime.date(2016,2,1)
    
    date_range = pd.date_range(start_date, end_date)
    date_range = date_range[date_range.day==1]
    
    print(date_range)
    
    0 讨论(0)
  • 2020-12-15 18:26

    I had a look at the dateutil documentation. Turns out it provides an even more convenient way than using dateutil.relativedelta: recurrence rules (examples)

    For the task at hand, it's as easy as

    from dateutil.rrule import *
    from datetime import date
    
    months = map(
        date.isoformat,
        rrule(MONTHLY, dtstart=date(2010, 8, 1), until=date.today())
    )
    

    The fine print

    Note that we're cheating a little bit, here. The elements dateutil.rrule.rrule produces are of type datetime.datetime, even if we pass dtstart and until of type datetime.date, as we do above. I let map feed them to date's isoformat function, which just turns out to convert them to strings as if it were just dates without any time-of-day information.

    Therefore, the seemingly equivalent list comprehension

    [day.isoformat()
        for day in rrule(MONTHLY, dtstart=date(2010, 8, 1), until=date.today())]
    

    would return a list like

    ['2010-08-01T00:00:00',
     '2010-09-01T00:00:00',
     '2010-10-01T00:00:00',
     '2010-11-01T00:00:00',
     ⋮
     '2015-12-01T00:00:00',
     '2016-01-01T00:00:00',
     '2016-02-01T00:00:00']
    

    Thus, if we want to use a list comprehension instead of map, we have to do something like

    [dt.date().isoformat()
        for dt in rrule(MONTHLY, dtstart=date(2010, 8, 1), until=date.today())]
    
    0 讨论(0)
  • 2020-12-15 18:26

    fresh pythonic one-liner from me

    from dateutil.relativedelta import relativedelta
    import datetime
    
    [(start_date + relativedelta(months=+m)).isoformat() for m in range(0,relativedelta(start_date,end_date).months+1)] 
    
    
    0 讨论(0)
  • 2020-12-15 18:29

    I got another way using datetime, timedelta and calender:

    from calendar import monthrange
    from datetime import datetime, timedelta
    
    def monthdelta(d1, d2):
        delta = 0
        while True:
            mdays = monthrange(d1.year, d1.month)[1]
            d1 += timedelta(days=mdays)
            if d1 <= d2:
                delta += 1
            else:
                break
        return delta
    
    start_date = datetime(2016, 1, 1)
    end_date = datetime(2016, 12, 1)
    
    num_months = [i-12 if i>12 else i for i in range(start_date.month, monthdelta(start_date, end_date)+start_date.month+1)]
    monthly_daterange = [datetime(start_date.year,i, start_date.day, start_date.hour) for i in num_months]
    
    0 讨论(0)
  • 2020-12-15 18:33

    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)
    
    0 讨论(0)
  • 2020-12-15 18:40

    You could reduce the number of if statements to two lines instead of four lines because having a second if statement that does the same thing with the previous if statement is a bit redundant.

    if (y == 2010 and m < 8) or (y == 2016 and m > 2):
        continue
    
    0 讨论(0)
提交回复
热议问题