Last active
April 20, 2021 20:03
-
-
Save python273/83bd826b5f47bd31bfdb9689929a1c00 to your computer and use it in GitHub Desktop.
Python Date Range & number of unique overlapping days with date range
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
:authors: python273 | |
:license: Apache License, Version 2.0 | |
:copyright: (c) 2018 python273 | |
""" | |
from datetime import datetime, timedelta | |
from functools import reduce | |
import operator | |
class DatetimeRange: | |
def __init__(self, start_date, end_date): | |
if end_date < start_date: | |
raise ValueError('{!r} < {!r}'.format(end_date, start_date)) | |
self.start_date = start_date | |
self.end_date = end_date | |
def __contains__(self, date): | |
""" (start_date, end_date] """ | |
return self.start_date <= date < self.end_date | |
def __eq__(self, other): | |
return ( | |
(self.start_date == other.start_date) and | |
(self.end_date == other.end_date) | |
) | |
def __and__(self, other): | |
""" Return overlapping range for dr1 & dr2. """ | |
dates = sorted([ | |
self.start_date, | |
self.end_date, | |
other.start_date, | |
other.end_date | |
]) | |
if self.end_date < other.start_date or self.start_date > other.end_date: | |
return | |
return DatetimeRange(dates[1], dates[2]) | |
def __sub__(self, other): | |
""" Return list with ranges """ | |
intersection = self & other | |
if intersection is None: | |
return [self] | |
result = [] | |
if intersection.start_date != self.start_date: | |
result.append(DatetimeRange(self.start_date, intersection.start_date)) | |
if intersection.end_date != self.end_date: | |
result.append(DatetimeRange(intersection.end_date, self.end_date)) | |
return result | |
@property | |
def delta(self): | |
return self.end_date - self.start_date | |
def __repr__(self): | |
return '<DatetimeRange({!r}, {!r}>'.format( | |
self.start_date, self.end_date | |
) | |
def coverage(primary_range, sub_ranges): | |
left = [primary_range] | |
for sub_range in sub_ranges: | |
new_left = [] | |
for left_range in left: | |
new_left.extend(left_range - sub_range) | |
left = new_left | |
if not left: | |
break | |
delta_left = reduce( | |
operator.add, | |
(i.delta for i in left), | |
timedelta() | |
) | |
return primary_range.delta - delta_left | |
primary = DatetimeRange( | |
datetime(2018, 1, 1), | |
datetime(2018, 2, 1) | |
) | |
print(coverage(primary, [ | |
DatetimeRange( | |
datetime(2018, 1, 10), | |
datetime(2018, 1, 15) | |
), | |
DatetimeRange( | |
datetime(2018, 1, 10), | |
datetime(2018, 1, 15) | |
), | |
])) # 5 days, 0:00:00 | |
print(coverage(primary, [ | |
DatetimeRange( | |
datetime(2018, 1, 10), | |
datetime(2018, 1, 15) | |
), | |
DatetimeRange( | |
datetime(2017, 12, 30), | |
datetime(2018, 2, 15) | |
), | |
])) # 31 days, 0:00:00 | |
print(coverage(primary, [ | |
DatetimeRange( | |
datetime(2018, 1, 10), | |
datetime(2018, 1, 15) | |
), | |
DatetimeRange( | |
datetime(2018, 1, 15), | |
datetime(2018, 1, 25) | |
), | |
DatetimeRange( | |
datetime(2018, 1, 10), | |
datetime(2018, 1, 25) | |
), | |
DatetimeRange( | |
datetime(2018, 1, 11), | |
datetime(2018, 1, 24) | |
), | |
])) # 15 days, 0:00:00 | |
print(coverage(primary, [ | |
DatetimeRange( | |
datetime(2018, 1, 10), | |
datetime(2018, 2, 15) | |
), | |
])) # 22 days, 0:00:00 | |
print(coverage( | |
DatetimeRange( | |
datetime(2018, 10, 1), | |
datetime(2018, 11, 1) | |
), [ | |
DatetimeRange( | |
datetime(2018, 10, 1), | |
datetime(2018, 10, 6) | |
), | |
DatetimeRange( | |
datetime(2018, 10, 1), | |
datetime(2018, 10, 6) | |
), | |
DatetimeRange( | |
datetime(2018, 10, 10), | |
datetime(2018, 10, 16) | |
), | |
DatetimeRange( | |
datetime(2018, 10, 15), | |
datetime(2018, 10, 21) | |
), | |
DatetimeRange( | |
datetime(2018, 10, 21), | |
datetime(2018, 10, 26) | |
), | |
] | |
)) # 21 days, 0:00:00 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Very useful, thanks.