Skip to content

Instantly share code, notes, and snippets.

@agentbellnorm
Last active July 30, 2018 09:43
Show Gist options
  • Save agentbellnorm/a3e02af7cfc4a7db13ac1f154e9269ea to your computer and use it in GitHub Desktop.
Save agentbellnorm/a3e02af7cfc4a7db13ac1f154e9269ea to your computer and use it in GitHub Desktop.
Calculate business hours between two datetime instances. Non office hours and configured holidays are excluded.
from datetime import datetime, timedelta
import numpy
def hour_diff(a, b):
delta = a - b
day_hours = delta.days * 24
second_hours = delta.seconds / 3600
return day_hours + second_hours
def businessday(timestamp):
return numpy.is_busday(timestamp.date(), holidays=["2018-12-24", "2018-12-31", "2019-12-24", "2019-12-31"])
def business_hours_between_datetimes_internal(t1, t2, cursor):
min_hour = 5
max_hour = 17
next_cursor = cursor.replace(hour=0, minute=0, second=0) + timedelta(days=1)
if cursor > t2:
return 0
if not businessday(cursor):
return 0 + business_hours_between_datetimes_internal(t1, t2, next_cursor)
# only one day
if t1.date() == t2.date():
if t2.hour < min_hour or t1.hour >= max_hour:
return 0
else:
return hour_diff(min(t2, t2.replace(hour=max_hour, minute=0, second=0)),
max(t1, t1.replace(hour=min_hour, minute=0, second=0)))
# last day of interval:
if cursor.date() == t2.date():
if t2.hour < min_hour:
return 0
if t2.hour > max_hour:
return max_hour - min_hour
else:
return hour_diff(t2, t2.replace(hour=min_hour, minute=0, second=0))
# first day of interval:
if cursor.date() == t1.date():
if t1.hour < min_hour:
return max_hour - min_hour + business_hours_between_datetimes_internal(t1, t2, next_cursor)
if t1.hour > max_hour:
return 0 + business_hours_between_datetimes_internal(t1, t2, next_cursor)
else:
return hour_diff(t1.replace(hour=max_hour, minute=0, second=0), t1) + business_hours_between_datetimes_internal(t1, t2,
next_cursor)
# Middle of range, full day
return max_hour - min_hour + business_hours_between_datetimes_internal(t1, t2, next_cursor)
def business_hours_between_datetimes(t1, t2):
return business_hours_between_datetimes_internal(t1, t2, t1)
def main():
fmt = "%Y-%m-%dT%H:%M:%S.%f%z"
t1 = datetime.strptime("2018-01-01T12:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-01T17:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 5, actual: " + str(res))
assert res == 5
t1 = datetime.strptime("2018-01-01T04:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-01T18:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12, actual: " + str(res))
assert res == 12
t1 = datetime.strptime("2018-01-01T04:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-03T16:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 35, actual: " + str(res))
assert res == 35
t1 = datetime.strptime("2018-01-01T06:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-03T16:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 34, actual: " + str(res))
assert res == 34
t1 = datetime.strptime("2018-01-01T19:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-03T02:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12, actual: " + str(res))
assert res == 12
t1 = datetime.strptime("2018-01-01T19:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-02T02:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 0, actual: " + str(res))
assert res == 0
t1 = datetime.strptime("2018-01-01T19:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-01T20:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 0, actual: " + str(res))
assert res == 0
t1 = datetime.strptime("2018-01-01T02:00:00.000+0200", fmt)
t2 = datetime.strptime("2018-01-01T03:00:00.000+0200", fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 0, actual: " + str(res))
assert res == 0
t1 = datetime.strptime('2018-01-03T04:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-01-03T18:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12.0, actual: " + str(res))
assert 12.0 == res
t1 = datetime.strptime('2018-01-03T08:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-01-03T12:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 4.0, actual: " + str(res))
assert 4.0 == res
t1 = datetime.strptime('2018-01-03T06:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-01-03T06:00:45.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 0.0125, actual: " + str(res))
assert 0.0125 == res
t1 = datetime.strptime('2018-01-03T06:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-01-03T06:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 0.0002777777777777778, actual: " + str(res))
assert 0.0002777777777777778 == res
t1 = datetime.strptime('2018-12-23T02:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-12-26T22:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 24, actual: " + str(res))
assert 24 == res
t1 = datetime.strptime('2018-07-23T05:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-23T22:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12, actual: " + str(res))
assert 12 == res
t1 = datetime.strptime('2018-07-23T05:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-24T22:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 24, actual: " + str(res))
assert 24 == res
t1 = datetime.strptime('2018-07-23T05:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-25T22:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 36, actual: " + str(res))
assert 36 == res
t1 = datetime.strptime('2018-07-23T05:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-26T22:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 48, actual: " + str(res))
assert 48 == res
t1 = datetime.strptime('2018-07-23T05:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-27T22:00:01.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 60, actual: " + str(res))
assert 60 == res
t1 = datetime.strptime('2018-07-13T05:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-16T18:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 24.0, actual: " + str(res))
assert 24.0 == res
t1 = datetime.strptime('2018-07-13T19:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-16T18:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12.0, actual: " + str(res))
assert 12.0 == res
t1 = datetime.strptime('2018-07-17T19:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-18T18:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12.0, actual: " + str(res))
assert 12.0 == res
t1 = datetime.strptime('2018-07-16T19:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-17T18:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12.0, actual: " + str(res))
assert 12.0 == res
t1 = datetime.strptime('2018-07-16T02:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-16T04:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 0, actual: " + str(res))
assert 0 == res
t1 = datetime.strptime('2018-07-23T22:00:00.123123+0200', fmt)
t2 = datetime.strptime('2018-07-25T04:00:00.123123+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 12, actual: " + str(res))
assert 12 == res
t1 = datetime.strptime('2018-07-11T15:29:03.837000+0200', fmt)
t2 = datetime.strptime('2018-07-12T06:01:41.874000+0200', fmt)
res = business_hours_between_datetimes(t1, t2)
print("expected: 2.5438888888888886, actual: " + str(res))
assert 2.5438888888888886 == res
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment