mirror of
https://github.com/nikdoof/businesshours.git
synced 2025-12-18 12:19:23 +00:00
Initial import.
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
.idea
|
||||||
|
.pyc
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
1
businesshours/__init__.py
Normal file
1
businesshours/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from .core import calc_business_hours
|
||||||
50
businesshours/core.py
Normal file
50
businesshours/core.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
from datetime import datetime, timedelta, time
|
||||||
|
|
||||||
|
|
||||||
|
def calc_business_hours(start, end, weekdays=(1, 2, 3, 4, 5), hours=range(8, 18)):
|
||||||
|
"""Calculates the number of seconds hours between two dates"""
|
||||||
|
|
||||||
|
def date_range(start_date, end_date):
|
||||||
|
"""Converts two datetimes into a list of dates between them"""
|
||||||
|
if isinstance(start_date, datetime):
|
||||||
|
start_date = start_date.date()
|
||||||
|
if isinstance(end_date, datetime):
|
||||||
|
end_date = end_date.date()
|
||||||
|
if start_date > end_date:
|
||||||
|
raise ValueError('You provided a start_date that comes after the end_date.')
|
||||||
|
while True:
|
||||||
|
yield start_date
|
||||||
|
start_date = start_date + timedelta(days=1)
|
||||||
|
if start_date > end_date:
|
||||||
|
break
|
||||||
|
|
||||||
|
if start.date() == end.date():
|
||||||
|
if not start.date().isoweekday() in weekdays:
|
||||||
|
return 0
|
||||||
|
if start.time().hour in hours:
|
||||||
|
actual_start = start
|
||||||
|
else:
|
||||||
|
if start.time().hour >= hours[-1] + 1:
|
||||||
|
return 0
|
||||||
|
actual_start = datetime.combine(start.date(), time(hours[0], 0, 0))
|
||||||
|
if end.time().hour in hours:
|
||||||
|
actual_end = end
|
||||||
|
else:
|
||||||
|
if end.time().hour <= hours[0]:
|
||||||
|
return 0
|
||||||
|
actual_end = datetime.combine(end.date(), time(hours[-1] + 1, 0, 0))
|
||||||
|
secs = (actual_end - actual_start).total_seconds()
|
||||||
|
return secs
|
||||||
|
else:
|
||||||
|
total_seconds = 0
|
||||||
|
dates = [dt for dt in date_range(start, end)]
|
||||||
|
|
||||||
|
for idx, dt in enumerate(dates):
|
||||||
|
day_start = datetime.combine(dt, time(0, 0, 0))
|
||||||
|
day_end = datetime.combine(dt, time(23, 59, 59))
|
||||||
|
if idx == 0:
|
||||||
|
day_start = start
|
||||||
|
if idx == len(dates) - 1:
|
||||||
|
day_end = end
|
||||||
|
total_seconds += calc_business_hours(day_start, day_end, weekdays, hours)
|
||||||
|
return total_seconds
|
||||||
1
businesshours/tests/__init__.py
Normal file
1
businesshours/tests/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__author__ = 'nikdoof'
|
||||||
36
businesshours/tests/test_core.py
Normal file
36
businesshours/tests/test_core.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from businesshours import calc_business_hours
|
||||||
|
|
||||||
|
|
||||||
|
class TestCalcBusinessHours(unittest.TestCase):
|
||||||
|
|
||||||
|
def run_tests(self, tests):
|
||||||
|
for res, dt1, dt2 in tests:
|
||||||
|
self.assertEqual(calc_business_hours(dt1, dt2,), res)
|
||||||
|
|
||||||
|
def test_invalid_arguments(self):
|
||||||
|
self.assertRaises(ValueError, calc_business_hours, datetime(2014,1,2), datetime(2014,1,1))
|
||||||
|
|
||||||
|
def test_simple_day(self):
|
||||||
|
self.run_tests([
|
||||||
|
(60, datetime(2013, 8, 1, 9, 0, 0), datetime(2013, 8, 1, 9, 1, 0)),
|
||||||
|
(600, datetime(2013, 8, 1, 9, 0, 0), datetime(2013, 8, 1, 9, 10, 0)), # Simple multiday
|
||||||
|
(36000, datetime(2013, 8, 1, 9, 0, 0), datetime(2013, 8, 2, 9, 0, 0)),
|
||||||
|
(72000, datetime(2013, 8, 5, 9, 0, 0), datetime(2013, 8, 7, 9, 0, 0)),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_multiday_out_of_hours(self):
|
||||||
|
self.run_tests([
|
||||||
|
(68400, datetime(2013, 8, 5, 9, 0, 0), datetime(2013, 8, 7, 0, 1, 0)),
|
||||||
|
(72000, datetime(2013, 8, 5, 7, 0, 0), datetime(2013, 8, 7, 8, 0, 0)),
|
||||||
|
(72000, datetime(2013, 8, 5, 6, 0, 0), datetime(2013, 8, 7, 8, 0, 0)),
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_weekend_single_day(self):
|
||||||
|
self.run_tests([
|
||||||
|
(0, datetime(2013, 8, 24, 9, 0, 0), datetime(2013, 8, 24, 9, 1, 0)),
|
||||||
|
(0, datetime(2013, 8, 25, 9, 0, 0), datetime(2013, 8, 25, 9, 10, 0)), # Multiday during weekend
|
||||||
|
(0, datetime(2013, 8, 3, 7, 0, 0), datetime(2013, 8, 4, 9, 0, 0)),
|
||||||
|
])
|
||||||
Reference in New Issue
Block a user