Created
December 8, 2015 14:19
-
-
Save jbylund/cc5e3197e1ec2490ac6b to your computer and use it in GitHub Desktop.
Date range logic
This file contains 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
class DateRange(object): | |
start_of_time = datetime.date.min | |
end_of_time = datetime.date.max | |
def __init__(self, start=start_of_time, end=end_of_time): | |
if type(start) != type(DateRange.start_of_time): | |
self.start = datetime.datetime.strptime(start, '%Y-%m-%d').date() | |
else: | |
self.start = start | |
if type(end) != type(DateRange.end_of_time): | |
self.end = datetime.datetime.strptime(end, '%Y-%m-%d').date() | |
else: | |
self.end = end | |
@classmethod | |
def empty(cls): | |
return cls(start=cls.end_of_time, end=cls.start_of_time) | |
def __lt__(self, other): | |
if self.start < other.start: | |
return True | |
elif other.start < self.start: | |
return False | |
return self.end < other.end | |
def __and__(self, other): | |
intersection_start = max(self.start, other.start) | |
intersection_end = min(self.end, other.end) | |
if intersection_start <= intersection_end: | |
return DateRange(start=intersection_start, end=intersection_end) | |
else: | |
return DateRange(start=DateRange.end_of_time, end=DateRange.start_of_time) | |
def __or__(self, other): | |
# if both are false return an empty array | |
if not(self) and not(other): | |
return [] | |
if not(self): | |
return [other] | |
if not(other): | |
return [self] | |
# at this point both are non empty | |
if self & other: | |
pass # they intersect | |
else: | |
ans = sorted([self, other]) | |
if ans[0].end + datetime.date.resolution == ans[1].start: | |
ans = [DateRange(ans[0].start, ans[1].end)] | |
return ans | |
def __repr__(self): | |
return "({}, {})".format(self.start, self.end) | |
def __str__(self): | |
return self.__repr__() | |
def __sub__(self, other): | |
# return an array of of the dateranges | |
ans = [] | |
intersect = self & other | |
if intersect: | |
intersect_start = intersect.start - datetime.date.resolution | |
intersect_end = intersect.end + datetime.date.resolution | |
left_part = DateRange(start=min(self.start, intersect_start), end=min(self.end, intersect_start)) | |
right_part = DateRange(start=max(self.start, intersect_end), end=max(self.end, intersect_end)) # go from the right part of it | |
if left_part: | |
ans.append(left_part) | |
if right_part: | |
ans.append(right_part) | |
return ans | |
else: | |
return [self] # if the overlap is empty, then just return the thing itself | |
def __contains__(self, other): | |
if type(other) != type(DateRange.start_of_time): | |
other = datetime.datetime.strptime(other, '%Y-%m-%d').date() | |
return (self.start <= other) and (other <= self.end) | |
def __nonzero__(self): | |
return self.start <= self.end | |
def union_ranges(*ranges): | |
ranges = sorted(ranges) | |
union = [DateRange.empty()] | |
for irange in ranges: | |
next_union = list() | |
for j in xrange(len(union)): | |
next_union.extend(union [j] | irange) | |
union = next_union | |
return union | |
def is_contigious(*ranges): | |
unioned = union_ranges(*ranges) | |
print unioned | |
return 1 == len(unioned) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment