Generate list of months between interval in python

后端 未结 10 1162
庸人自扰
庸人自扰 2020-12-08 02:14

I want to generate a python list containing all months occurring between two dates, with the input and output formatted as follows:

date1 = \"2014-10-10\"  #         


        
相关标签:
10条回答
  • 2020-12-08 02:37

    I found a very succinct way to do this with Pandas, sharing in case it helps anybody:


    UPDATE: I've got it down to a one-liner with the help of this post :)

    pd.date_range('2014-10-10','2016-01-07', 
                  freq='MS').strftime("%Y-%b").tolist()
    

    OLD ANSWER:

    daterange = pd.date_range('2014-10-10','2016-01-07' , freq='1M') 
    daterange = daterange.union([daterange[-1] + 1])  
    daterange = [d.strftime('%y-%b') for d in daterange]
    

    The second line prevents the last date from getting clipped off the list.

    0 讨论(0)
  • 2020-12-08 02:37

    If you're interested in keeping your dates in a Python format, you can try using to_pydatetime().

    import pandas as pd
    from datetime import datetime
    
    datemin = datetime(2010, 1, 1)
    datemax = datetime(2019, 12, 31)
    
    # First day of month
    pd.date_range(datemin, datemax, freq='MS').to_pydatetime().tolist()
    
    # Last day of month
    pd.date_range(datemin, datemax, freq='M').to_pydatetime().tolist()
    

    List of Offset Aliases

    0 讨论(0)
  • 2020-12-08 02:43

    Having done similar stuff previously, I took a stab at solving this. Using distinct components for doing this is more flexible and enables you to mix and match them for different use-cases. They also can be tested more easily this way, as you can see by the doctests in iterate_months.

    Also I suggest to use datetime.date objects for your input as you can just do more with those. To do that you'll have to first parse your input string, but this is very easily done.

    Parsing the date-strings

    def datify(date):
        if isinstance(date, datetime.date):
            return date
        elif isinstance(date, datetime.datetime):
            return date.date()
        else:
            # taken from simleo's answer
            return datetime.strptime(date, "%Y-%m-%d")
    

    First, we iterate through the months

    import datetime
    
    
    def iterate_months(start_date, end_date):
        """Iterate monthly between two given dates.
    
        Emitted will be the first day of each month.
    
        >>> list(iterate_months(datetime.date(1999, 11, 1),
        ...                     datetime.date(2000, 2, 1)))
        [datetime.date(1999, 11, 1), datetime.date(1999, 12, 1),\
     datetime.date(2000, 1, 1), datetime.date(2000, 2, 1)]
    
        """
        assert isinstance(start_date, datetime.date)
        assert isinstance(end_date, datetime.date)
        assert start_date < end_date
    
        year = start_date.year
        month = start_date.month
        while True:
            current = datetime.date(year, month, 1)
            yield current
            if current.month == end_date.month and current.year == end_date.year:
                break
            else:
                month = ((month + 1) % 12) or 12
                if month == 1:
                    year += 1
    
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    

    To format your dates, use something like this

    def format_month(date):
        return date.strftime(r"%b-%y")
    

    Putting it all together

    start = datify("2014-10-10")
    end = datify("2016-01-07")
    
    for entry in iterate_months(start, end):
        print format_month(entry)
    

    Or save it as a list:

    result = list(iterate_months(start, end))
    
    0 讨论(0)
  • 2020-12-08 02:49

    Find below my approach to this problem using split and simple modulo-based iterations without importing any special module.

    date1 = "2014-10-10"
    date2 = "2016-01-07"
    
    y0 = int( date1.split('-')[0] ) # 2014
    y1 = int( date2.split('-')[0] ) # 2016
    
    m0 = int( date1.split('-')[1] ) - 1 # 10-1 --> 9 because will be used for indexing
    m1 = int( date2.split('-')[1] ) - 1 # 01-1 --> 0 because will be used for indexing
    
    months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
    result = []
    start = m0
    for y in range(y0, y1+1):
        for m in range(start,12):
            result.append( str( months[m  % 12])+'-'+str(y) )
            if y == y1 and (m % 12) == m1:
                break
        start = 0
    
    print result
    

    $ python dates.py

    ['Oct-2014', 'Nov-2014', 'Dec-2014', 'Jan-2015', 'Feb-2015', 'Mar-2015', 'Apr-2015', 'May-2015', 'Jun-2015', 'Jul-2015', 'Aug-2015', 'Sep-2015', 'Oct-2015', 'Nov-2015', 'Dec-2015', 'Jan-2016']
    
    0 讨论(0)
  • 2020-12-08 02:51
    >>> 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 :)

    0 讨论(0)
  • 2020-12-08 02:59

    You have to use Calendar and Datetime

    import calendar
    from datetime import *
    date1 = datetime.strptime("2014-10-10", "%Y-%m-%d")
    date2 = datetime.strptime("2016-01-07", "%Y-%m-%d")
    date1 = date1.replace(day = 1)
    date2 = date2.replace(day = 1)
    months_str = calendar.month_name
    months = []
    while date1 < date2:
        month = date1.month
        year  = date1.year
        month_str = months_str[month][0:3]
        months.append("{0}-{1}".format(month_str,str(year)[-2:]))
        next_month = month+1 if month != 12 else 1
        next_year = year + 1 if next_month == 1 else year
        date1 = date1.replace( month = next_month, year= next_year)
    
    print months
    

    This code returns

    ['Oct-14', 'Nov-14', 'Dec-14', 'Jan-14', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-15']
    
    0 讨论(0)
提交回复
热议问题