Python datetime month range

Python: get all months in range?

Ani, this is nearly correct, but you have to format the datetime in a way that it appears in the ISO format. This can be done by appending the isoformat. You should change the line to result.append(current.isoformat()) — [EDIT: didn’t see that you left the formatting out as an exercise. but oh well.]

Thanks, I actually did result.append(datetime.strftime(current, ‘%Y-%m-01’)) to get the format I wanted.

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())] 
['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())] 

Note that the iteration variable day in the first list comprehension is a deliberate misnomer. As explained, it holds a datetime , not a date .

Читайте также:  Learn javascript pro apk

use datetime and timedelta standard Python’s modules — without installing any new libraries

from datetime import datetime, timedelta now = datetime(datetime.now().year, datetime.now().month, 1) ctr = datetime(2010, 8, 1) list = [ctr.strftime('%Y-%m-%d')] while ctr  

I'm adding 32 days to enter new month every time (longest months has 31 days)

this is a nice solution, but you should add ctr = ctr.replace(day=1) after ctr = timedelta(days=32) , otherwise you'll to skip a month in a longer period. Look at this sequence: [2018-10-28, 2018-11-29, 2018-12-31, 2019-02-01, 2019-03-05] -> January has been skipped here. Setting day=1 avoids this problem.

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) 

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 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] 

Similar to @Mattaf, but simpler. pandas.date_range() has an option frequency freq='m'. Here I am adding a day ( pd.Timedelta('1d') ) in order to reach the beginning of each new month:

import pandas as pd date_range = pd.date_range('2010-07-01','2016-02-01',freq='M')+pd.Timedelta('1d') print(list(date_range)) 

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 

I don't know whether it's better, but an approach like the following might be considered more 'pythonic':

months = [ '<>-2>-01'.format(year, month) for year in xrange(2010, 2016 + 1) for month in xrange(1, 12 + 1) if not (year = 2016 and month > 2) ] 

The main differences here are:

  • As we want the iteration(s) to produce a list, use a list comprehension instead of aggregating list elements in a for loop.
  • Instead of explicitly making a distinction between numbers below 10 and numbers 10 and above, use the capabilities of the format specification mini-language for the .format() method of str to specify
    • a field width (the 2 in the 2> place holder)
    • right-alignment within the field (the > in the 2> place holder)
    • zero-padding (the 0 in the 2> place holder)

    Even more pythonic is of course using dateutil and datetime to deal with dates and time differences, as Ani's answer does.

    A different approach that doesn't require any additional libraries, nor nested or while loops. Simply convert your dates into an absolute number of months from some reference point (it can be any date really, but for simplicity we can use 1st January 0001). For example

    a=datetime.date(2010,2,5) abs_months = a.year * 12 + a.month 

    Once you have a number representing the month you are in you can simply use range to loop over the months, and then convert back:

    Solution to the generalized problem:

    import datetime def range_of_months(start_date, end_date): months = [] for i in range(start_date.year * 12 + start_date.month, end_date.year*12+end_date.month + 1) months.append(datetime.date((i-13) // 12 + 1, (i-1) % 12 + 1, 1)) return months 

    Additional Notes/explanation:

    Here // divides rounding down to the nearest whole number, and % 12 gives the remainder when divided by 12, e.g. 13 % 12 is 1 .

    (Note also that in the above date.year *12 + date.month does not give the number of months since the 1st of January 0001. For example if date = datetime.datetime(1,1,1) , then date.year * 12 + date.month gives 13 . If I wanted to do the actual number of months I would need to subtract 1 from the year and month, but that would just make the calculations more complicated. All that matters is that we have a consistent way to convert to and from some integer representation of what month we are in.)

    Источник

    Generate list of months between interval in python

    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" # input start date date2 = "2016-01-07" # input end date month_list = ['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'] # output 

    13 Answers 13

    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() 
    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.

    Its not returning start date. eg: pd.date_range('2021-10-2','2021-12-30', freq='MS').strftime("%Y-%b").tolist() then output is ['2021-Nov', '2021-Dec'] . But its suppose to be ['Oct-21', 'Nov-21', 'Dec-21']

    >>> 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 🙂

    Источник

Оцените статью