Skip to content

Instantly share code, notes, and snippets.

@iwanbolzern
Created August 13, 2019 18:25
Show Gist options
  • Save iwanbolzern/be1c66de78d22629e3945a5a79f7624f to your computer and use it in GitHub Desktop.
Save iwanbolzern/be1c66de78d22629e3945a5a79f7624f to your computer and use it in GitHub Desktop.
This is a simple solution to work with periods in time. For a detailed description of how to use this class check the attached Readme

Period

A simple solution to handle periods in time.

Simple usage

from datetime import datetime
from period import Period

period_1 = Period(datetime(2019, 1, 1), datetime(2019, 1, 1, 10))
period_2 = Period(datetime(2019, 1, 1, 5), datetime(2019, 1, 1, 15))

Get how much the two periods overlap. If they do not overlap it will return a negative timedelta.

period_1.get_overlap(period_2)  # timedelta(hours=5)

Check if two periods overlap

period_1.has_overlap(period_2)  # True

Get a new Period from the overlapping section

period_1.get_overlap_period(period_2)  # Period(datetime(2019, 1, 1, 5), datetime(2019, 1, 1, 10))
from datetime import datetime, timedelta
class PeriodException(Exception):
def __init__(self, message: str):
super(PeriodException, self).__init__(message)
class Period:
def __init__(self, from_point_in_time: datetime, to_point_in_time: datetime):
assert (from_point_in_time < to_point_in_time), 'from_point_in_time must be smaller than to_point_in_time'
self._from_point_in_time = from_point_in_time
self._to_point_in_time = to_point_in_time
def has_overlap(self, other_period: 'Period') -> bool:
return self.get_overlap(other_period) > timedelta()
def get_overlap(self, other_period: 'Period') -> timedelta:
return max(self._to_point_in_time, other_period._to_point_in_time) \
- min(self._from_point_in_time, other_period._from_point_in_time) \
- abs(other_period._from_point_in_time - self._from_point_in_time) \
- abs(other_period._to_point_in_time - self._to_point_in_time)
def get_overlap_period(self, other_period: 'Period') -> 'Period':
if not self.has_overlap(other_period):
raise PeriodException('The given Periods do not have overlaps. '
'Period 1 {}, Period 2 {}'.format(str(self), str(other_period)))
max_from_point_in_time = max(self._from_point_in_time, other_period._from_point_in_time)
overlap = self.get_overlap(other_period)
return Period(max_from_point_in_time, max_from_point_in_time + overlap)
def __str__(self):
return 'From: {} To: {}'.format(self._from_point_in_time.isoformat(),
self._to_point_in_time.isoformat())
def __eq__(self, other: 'Period'):
return self._from_point_in_time == other._from_point_in_time \
and self._to_point_in_time == other._to_point_in_time
from datetime import datetime, timedelta
from unittest import TestCase
from period import Period, PeriodException
class TestPeriod(TestCase):
def setUp(self) -> None:
# this 4 periods are the base for all tests
# ¦----p1-----¦
# ¦----p2-----¦
# ¦-----p3-----¦
# ¦-----------p4------------¦
self.start_date = datetime(2019, 1, 1)
self.period_1 = Period(self.start_date, self.start_date + timedelta(hours=6))
self.period_2 = Period(self.start_date + timedelta(hours=3), self.start_date + timedelta(hours=9))
self.period_3 = Period(self.start_date + timedelta(hours=7), self.start_date + timedelta(hours=12))
self.period_4 = Period(self.start_date + timedelta(hours=2), self.start_date + timedelta(hours=12))
def test_get_overlap_normal_case(self):
delta = self.period_1.get_overlap(self.period_2)
self.assertEqual(delta, timedelta(hours=3))
def test_get_overlap_normal_reverse(self):
delta = self.period_2.get_overlap(self.period_1)
self.assertEqual(delta, timedelta(hours=3))
def test_get_overlap_surrounded(self):
delta = self.period_2.get_overlap(self.period_4)
self.assertEqual(delta, timedelta(hours=6))
def test_get_overlap_surrounded_reverse(self):
delta = self.period_4.get_overlap(self.period_2)
self.assertEqual(delta, timedelta(hours=6))
def test_get_overlap_same_end(self):
delta = self.period_3.get_overlap(self.period_4)
self.assertEqual(delta, timedelta(hours=5))
def test_has_overlap(self):
has_overlap = self.period_1.has_overlap(self.period_2)
self.assertTrue(has_overlap)
def test_has_overlap_surrounded(self):
has_overlap = self.period_2.has_overlap(self.period_4)
self.assertTrue(has_overlap)
def test_has_overlap_negative(self):
has_overlap = self.period_1.has_overlap(self.period_3)
self.assertFalse(has_overlap)
def test_get_overlap_period(self):
overlap_period = self.period_1.get_overlap_period(self.period_2)
self.assertEqual(overlap_period, Period(self.start_date + timedelta(hours=3),
self.start_date + timedelta(hours=6)))
def test_get_overlap_period_no_overlap(self):
self.assertRaises(PeriodException, self.period_1.get_overlap_period, self.period_3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment