sal
sal

Reputation: 1239

Generate list of tuples (year, month, days_in_month, full_month) from list of dates

I have a list of dates as generated by:

from dateutil import parser
from datetime import date, timedelta

d1 = parser.parse("2015-11-25")
d2 = parser.parse("2016-02-06")

delta = (d2-d1).days 

date_list = [d1 + timedelta(days=x) for x in range(0, delta+1)]

In this list there are 6 days in the month of november 2015, 31 days in december 2015 , 31 days in january 2016 and 6 days in february 2016. December 2015 and January 2016 are "full" months, i.e. the datelist has all days in those months.

How can I get this information programatically in python, in order to produce a list such as:

[(2015,11,6,False),(2015,12,31,True),(2016,1,31,True),(2016,2,6,False)]

Upvotes: 2

Views: 2475

Answers (3)

DJanssens
DJanssens

Reputation: 20729

The previous mentioned solutions seem ok, however I believe I have a more optimal solution, since they require to calculate a list that contains all the days. For a small date difference this won't be problematic. However if the difference increases, your list will become a lot larger.

I want to give another approach that is more intuitive, since you basically know that all months that between the dates are full, and the months of the dates themselves are not full.

I try to leverage that information and the loop will only iterate the amount of months between the dates.

The code:

from dateutil import parser
from calendar import monthrange

d1 = parser.parse("2015-11-25")
d2 = parser.parse("2016-02-06")

# needed to calculate amount of months between the dates
m1 = d1.year * 12 + (d1.month- 1)
m2 = d2.year * 12 + (d2.month - 1)

result = []
# append first month since this will not be full
result.append((d1.year,d1.month,monthrange(d1.year, d1.month)[1]-d1.day+1,False))
current_month = d1.month
current_year = d1.year
# loop through the months and years that follow d1.
for _ in xrange(0,(m2-m1)-1):
    if current_month+1 > 12:
        current_month = 1
        current_year += 1
    else:
        current_month += 1
    result.append((current_year,current_month,monthrange(current_year, current_month)[1],True))
# append last month since this will not be full either.
result.append((d2.year,d2.month,d2.day,False))
print result

Keep in mind that the code I gave is an example, it doesn't support for instance the scenario where the 2 given dates have the same month.

Upvotes: 0

sal
sal

Reputation: 1239

Found a neat short solution:

from dateutil import parser
from datetime import date, timedelta
from collections import Counter
from calendar import monthrange

d1 = parser.parse("2015-11-25")
d2 = parser.parse("2016-02-06")

delta = (d2-d1).days 

date_list = [d1 + timedelta(days=x) for x in range(0, delta+1)]
month_year_list = [(d.year, d.month) for d in date_list]

result = [(k[0],k[1],v , True if  monthrange(k[0], k[1])[1] == v else 
False) for k,v in Counter(month_year_list).iteritems()]

print result

Upvotes: 1

Roadmaster
Roadmaster

Reputation: 5357

Walk the list and accumulate the number of days for each year/month combination:

import collections

days_in_year_month = defaultdict(int)
for each_date in date_list:
   days_in_year_month[(each_date.year, each_date.month)] += 1

Next output the tuples with each year, month, count and T/F:

import calendar
result = []
for year_month in date_list.keys():
   days_in_ym = days_in_year_month([year_month[0], year_month[1])
   is_complete = days_in_ym == calendar.monthrange(year_month[0], year_month[1])[1]
   result.append(year_month[0], year_month[1], days_in_ym, is_complete)

So:

  1. I learned about monthrange here: How do we determine the number of days for a given month in python
  2. My solution sucks because it will do a total of 3 loops: the initial loop from your list comprehension, plus the two loops I added. Since you're walking the days in order for your list comprehension, this could be much optimized to run in a single loop.
  3. I didn't test it :)

Upvotes: 0

Related Questions