Last active
July 30, 2018 09:43
-
-
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.
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
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