Reputation: 43
I have pairs of DateTime objects. Each DateTime object is either a 'start' or an 'end' of a time range. The time ranges overlap sometimes. I need to find the gaps.
I tried the DateTimeRange module off pypi, testing for intersecting ranges and then constructing encompassed ranges (as per their documentation) but I wasn't able to build these components into a piece of code that does what I want
EDIT: A 'gap' in this case is defined as any period of time between the earliest 'start' and the latest 'end' that is not encompassed by one of the pairs of 'start' and 'end' DateTime objects
import dateparser
from pprint import pprint
foo = []
foo.append({
"start": dateparser.parse("8:00 AM"),
"end": dateparser.parse("8:06 AM")
})
foo.append({
"start": dateparser.parse("8:03 AM"),
"end": dateparser.parse("8:07 AM")
})
foo.append({
"start": dateparser.parse("8:02 AM"),
"end": dateparser.parse("8:16 AM")
})
foo.append({
"start": dateparser.parse("8:20 AM"),
"end": dateparser.parse("8:30 AM")
})
pprint(foo)
#[{'end': datetime.datetime(2019, 2, 15, 8, 6),
# 'start': datetime.datetime(2019, 2, 15, 8, 0)},
# {'end': datetime.datetime(2019, 2, 15, 8, 7),
# 'start': datetime.datetime(2019, 2, 15, 8, 3)},
# {'end': datetime.datetime(2019, 2, 15, 8, 16),
# 'start': datetime.datetime(2019, 2, 15, 8, 2)},
# {'end': datetime.datetime(2019, 2, 15, 8, 30),
# 'start': datetime.datetime(2019, 2, 15, 8, 20)}]
find_gaps(foo)
#desired output
#
#[{'end': datetime.datetime(2019, 2, 15, 8, 20),
# 'start': datetime.datetime(2019, 2, 15, 8, 16)}]
Upvotes: 4
Views: 1989
Reputation: 1279
You can sort your ranges based on the start times, then keep track of the end times until you find a gap between an end time and the next start time. If you find that gap, you append it. You need to advance the end time if the next end time is greater than the current end time.
def find_gaps(ranges):
if len(ranges) <= 1:
return []
# sort by start times
ranges = sorted(ranges, key=lambda x:x['start'])
gaps = []
# Start at the end of the first range
now = ranges[0]['end']
# Iterate through ranges, ignoring the first range
for pair in ranges[1:]:
# if next start time is before current end time, keep going until we find a gap
# if next start time is after current end time, found the first gap
if pair['start'] > now:
gaps.append({
'start':now,
'end':pair['start']
})
# need to advance "now" only if the next end time is past the current end time
now = max(pair['end'], now)
return gaps
Output:
[{'end': datetime.datetime(2019, 2, 15, 8, 20),
'start': datetime.datetime(2019, 2, 15, 8, 16)}]
Upvotes: 9