Skip to content

Instantly share code, notes, and snippets.

@jbylund
Created December 8, 2015 14:19
Show Gist options
  • Save jbylund/cc5e3197e1ec2490ac6b to your computer and use it in GitHub Desktop.
Save jbylund/cc5e3197e1ec2490ac6b to your computer and use it in GitHub Desktop.
Date range logic
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