Recipe3.5.Calculating the Number of Weekdays Between Two Dates


Recipe 3.5. Calculating the Number of Weekdays Between Two Dates

Credit: Anna Martelli Ravenscroft

Problem

You want to calculate the number of weekdays (working days), as opposed to calendar days, that fall between two dates.

Solution

Since weekends and other "days off" vary by country, by region, even sometimes within a single company, there is no built-in way to perform this task. However, using dateutil along with datetime objects, it's reasonably simple to code a solution:

from dateutil import rrule  import datetime def workdays(start, end, holidays=0, days_off=None):     if days_off is None:         days_off = 5, 6         # default to: saturdays and sundays     workdays = [x for x in range(7) if x not in days_off]     days = rrule.rrule(rrule.DAILY, dtstart=start, until=end,                        byweekday=workdays)     return days.count( ) - holidays if _ _name_ _ == '_ _main_ _': # test when run as main script     testdates = [ (datetime.date(2004, 9, 1), datetime.date(2004, 11, 14), 2),                   (datetime.date(2003, 2, 28), datetime.date(2003, 3, 3), 1), ]     def test(testdates, days_off=None):         for s, e, h in testdates:             print 'total workdays from %s to %s is %s with %s holidays' % (                         s, e, workdays(s, e, h, days_off), h)     test(testdates)     test(testdates, days_off=[6])

Discussion

This project was my very first one in Python: I needed to know the number of actual days in training of our trainees, given a start date and end date (inclusive). This problem was a bit trickier back in Python 2.2; today, the datetime module and the dateutil third-party package make the problem much simpler to solve.

Function workdays starts by assigning a reasonable default value to variable days_off (unless an explicit value was passed for it as an argument), which is a sequence of the weekday numbers of our normal days off. In my company, weekly days off varied among individuals but were usually fewer than the workdays, so it was easier to track and modify the days off rather than the workdays. I made this an argument to the function so that I can easily pass a different value for days_off if and when I have different needs. Then, the function uses a list comprehension to create a list of actual weekly workdays, which are all weekdays not in days_off. Now the function is ready to do its calculations.

The workhorse in this recipe is an instance, named days, of dateutil's rrule (recurrence rule) class. Class rrule may be instantiated with various parameters to produce a rule object. In this example, I pass a frequency (rrule.DAILY), a beginning date and an ending dateboth of which must be datetime.date objectsand which weekdays to include (workdays). Then, I simply call method days.count to count the number of occurrences generated by the rule. (See Recipe 3.3 for other uses for the count method of rrule.)

You can easily set your own definition of weekend: just pass as days_off whichever values you need. In this recipe, the default value is set to the standard U.S. weekend of Saturday and Sunday. However, if your company normally works a four-day week, say, Tuesday through Friday, you would pass days_off=(5, 6, 0). Just be sure to pass the days_off value as an iterable, such as a list or tuple, even if, as in the second test, you only have a single day in that container.

A simple but useful enhancement might be to automatically check whether your start and end dates are weekends (for weekend-shift workers), and use an if/else to handle the weekend shifts, with appropriate changes to days_off. Further enhancements would be to add the ability to enter sick days, or to perform a call to an automatic holiday lookup function, rather than passing the number of holidays directly, as I do in this recipe. See Recipe 3.6 for a simple implementation of a holidays list for this purpose.

See Also

Refer to the dateutil documentation, which is available at https://moin.conectiva.com.br/DateUtil?action=highlight&value=DateUtil, datetime documentation in the Library Reference; Recipe 3.3 for another use of rrule.count; Recipe 3.6 for automatic holiday lookup.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net