Date time and classes

build a class to handle the calculations around whether a date falls into working hours or the date is a holiday or not during work hours.

This also seems like functionality that makes sense in a class.

instance construction

  • Instance accepts working hours start and end and working days array.
  • Instance accepts optional holiday days array

instance methods

  • Given a time determines if the time is or is not a 'working time'
  • Given a starting time, instance can produce an enumeration of times at some interval where the times are continuous, but categorized as working time, non-working time.
    • start time, duration -> start time, end time, work minutes

In [ ]:


In [ ]:
#working hours.  Nobody works 24x7 so there are two possibilities for non-work hours.
#going to do this the way I've seen it specified elsewhere.

#normal hours worked
work_hours = {'start': '08:00',
              'end': '17:00'}
work_days = ['Monday','Tuesday','Wednesday','Thursday','Friday']

#assume that the holiday is all day.
holiday_days = ['2017-05-29', '2017-07-04']

In [ ]:
##some days to test and expected results as tuple (date, expected is_within_workday result)
sample_datetimes = [('2017-05-20 08:00:01', False), ##8am but on a weekend
                    ('2017-05-20 07:59:59', False), ##before 8am but on a weekend
                    ('2017-05-18 08:00:01', True),  ##8am on a week day.. yes working hours
                    ('2017-05-18 07:59:59', False), ##before 8am on a working day.  not working hours
                    ('2017-05-20 16:59:59', False), ##before 5pm but on a non workday
                    ('2017-05-20 17:00:01', False), ##after 5pm but on a non workday
                    ('2017-05-18 16:59:59', True),  ##before 5pm but on a weekday yes working hour
                    ('2017-05-18 17:00:01', False), ##after 5pm on a work day.. no not worktime
                    ('2017-05-29 08:00:01', False), ##after 8 am but on a holiday no not work time
                    ('2017-05-29 07:59:59', False), ##before 8am but on a holiday not work time
                    ('2017-05-18 08:00:00', True), ##at 8am but on a holiday not work time
                    ('2017-05-18 17:00:00', True), ##before 8am but on a holiday not work time
                   ]

In [ ]:
# Python cookbook 3.13 looks useful!

In [ ]:
from datetime import datetime, date, time
class WorkingTimes:
    """
    WorkingTimes
    
    Class used to encapsulate the work day, work hours and holiday 
    details needed for calcuating whether a specific date is or is not within working hours.
    
    Checks are:
    Is the datetime on a work day.
    Is the datetime between the start of work day and end of work day
    Is the datetime on a day that has been identified as a holiday.
        
    """
    DAYLIST = ['Monday','Tuesday','Wednesday','Thursday','Friday', 'Saturday', 'Sunday']
    STRDT = '%Y-%m-%d %H:%M:%S'
    SDRD = '%Y-%m-%d'
    STRHR = '%H:%M'
    def __init__(self, work_hours_start, work_hours_end, work_days, holiday_list):
        self.whs = work_hours_start
        self.whe = work_hours_end
        
        ##all workdays should be real days from DAYLIST  ['Monday','Friday']
        self.wda = work_days
        
        ##holiday_list (convert them all to dates)
        self.hdl = [datetime.strptime(hd, WorkingTimes.SDRD) for hd in holiday_list]
        
    def __str__(self):
        return("work start: {}, end: {}, days {}".format(self.whs, self.whe, self.wda))
        
    def is_within_workday(self, dts = datetime.now()):
        dt = datetime.strptime(dts, WorkingTimes.STRDT) if isinstance(dts,str) else dts
        work_hours_start_datetime = datetime.combine(dt.date(),
                                                     datetime.strptime(self.whs, WorkingTimes.STRHR).time())
        work_hours_end_datetime = datetime.combine(dt.date(), 
                                                   datetime.strptime(self.whe, WorkingTimes.STRHR).time())
        is_workday = any( [s for s in self.wda if ( s == WorkingTimes.DAYLIST[dt.weekday()])])
        holiday = any([d for d in self.hdl if (d.date() == dt.date())])
        return((not holiday) and (is_workday and (dt>=work_hours_start_datetime and dt<= work_hours_end_datetime)))

In [ ]:
foo = WorkingTimes(work_hours['start'], work_hours['end'], work_days, holiday_days)

In [ ]:
print(foo)

In [ ]:
#check all my test cases to see if they are returning as expected.

[e == foo.is_within_workday(dt) for dt,e in sample_datetimes ]