Overlapping dates between two date ranges in python

I am looking to find matching dates between two date ranges as follows:

range1 = start(2016-06-01) end (2016-06-20) range2 = start(2016-06-10) end (2016-06-13) 
Result

is 4 dates (2016-06-10,2016-06-11,2016-06-12,2016-06-13) . another example:

  range1 = start(2016-06-01) end (2016-06-20) range2 = start(2016-06-18) end (2016-06-25) 
Result

is 3 dates (2016-06-18,2016-06-19,2016-06-20) . and if the dates do not match, then the result will be 0.

I found helping to determine the number of matching dates, but I was wondering if I can get the actual dates without using long if / else statements?

Thanks in advance!

+5
source share
3 answers

I would suggest creating dates in each of the two ranges, and then choosing the intersection between the two sets. It might look like this:

 from datetime import date, timedelta def f(d1, d2): delta = d2 - d1 return set([d1 + timedelta(days=i) for i in range(delta.days + 1)]) range1 = [date(2016, 6, 1), date(2016, 6, 20)] range2 = [date(2016, 6, 10), date(2016, 6, 13)] print f(*range1) & f(*range2) 

For performance reasons, you can also drop d1 + timedelta(days=i) to str when creating dates in a given range.

+2
source

If you are not already working with Python3 and therefore cannot use memory-efficient range objects, you can do namedtuple as shown in the answer you are referencing (otherwise you could use the new range objects). From there, all you have to do is use datetime.date.fromordinal on an overlapping daterange:

 >>> from datetime import date >>> from collections import namedtuple >>> Range = namedtuple('Range', ['start', 'end']) >>> r1 = Range(start=date(2016, 1, 1), end=date(2016, 2, 5)) >>> r2 = Range(start=date(2016, 1, 28), end=date(2016, 2, 28)) >>> latest_start = max(r1.start, r2.start) >>> earliest_end = min(r1.end, r2.end) >>> overlap = (earliest_end - latest_start).days + 1 >>> overlapping_dates = [] # default >>> if overlap > 0: ... overlapping_dates = range(latest_start.toordinal(), earliest_end.toordinal() + 1) # as numbers ... overlapping_dates = [ date.fromordinal(x) for x in overlapping_dates ] # back to datetime.date objects ... >>> overlapping_dates [datetime.date(2016, 1, 28), datetime.date(2016, 1, 29), datetime.date(2016, 1, 30), datetime.date(2016, 1, 31), datetime.date(2016, 2, 1), datetime.date(2016, 2, 2), datetime.date(2016, 2, 3), datetime.date(2016, 2, 4), datetime.date(2016, 2, 5)] 

An approach using set will also work (one of these approaches is in this answer editing history), but is usually less efficient since it must have all dates in memory, even those that are not at the intersection.

+1
source

Recently, I wrote a small function that returns if there is an overlap, and then a range from

 def date_overlap(start1, end1, start2, end2): overlaps = start1 <= end2 and end1 >= start2 if not overlaps: return False, None, None return True, max(start1, start2), min(end1, end2) 

Obviously, the output format can be adapted to what is needed.

0
source

All Articles