The reusable app scalendar
provides calendaring and scheduling functionality for Django.
We use the term "Scalendar" to refer to a customizable calendar object which holds information about working and non-working days. You can define the general pattern for a work-week (e.g. weekdays and weekend) when you first initialize a Scalendar object. If needed, you can then define various exceptions (e.g. public holidays, working weekends, etc.)
A Scalendar objects has methods that allow you to check whether any given date is a working day or not.
By default, when you initialize a new Scalendar
object, it holds a standard western schedule, in which the week begins on Monday, the working days are Monday to Friday, and the weekend is on Saturday and Sunday. No explicit exceptions are defined by default.
To create a default Scalendar
object, just give it a name:
from scalendar.models import Scalendar
default_scalendar = Scalendar.objects.create(name="My Default Scalendar")
The workdays
field contains an integer-encoded binary number, where each bit from right to left represents a day of the week, starting from Monday. I.e., the most significant bit of workdays
is set when Sunday is a working day and the least significant bit is set when Monday is a working day. For example, to check whether Thursday is a workday in our Scalendar
:
import calendar
assert(default_scalendar.workdays & (1 << calendar.THURSDAY) == 8)
There is a (private) helper method that does this:
assert(default_scalendar._is_workday(calendar.THURSDAY) == True)
To query a specific date, use the is_workday
or the is_not_workday
methods:
from datetime import date
monday = date(2014, 5, 26)
assert(default_scalendar.is_workday(monday) == True)
assert(default_scalendar.is_not_workday(monday) == False)
saturday = date(2014, 5, 31)
assert(default_scalendar.is_workday(saturday) == False)
assert(default_scalendar.is_not_workday(saturday) == True)
You can define a custom Scalendar with a non-standard work-week by passing a binary number for the 'workdays' value to the constructor. For example, to define a work-week in which the weekend is on Friday and Saturday:
uae_calendar = Scalendar.objects.create(name="UAE Calendar", int('1001111', 2))
You can now query the custom calendar:
friday = date(2014, 5, 30)
assert(uae_calendar.is_workday(friday) == False)
assert(uae_calendar.is_not_workday(friday) == True)
Individual dates, such as public holidays, can be re-defined in a Scalendar
by using the ScalendarException
model.
from scalendar.models import Scalendar, ScalendarException
default_scalendar = Scalendar.objects.create(name="My Default Scalendar")
christmas_day = date(2014, 12, 25)
assert(default_scalendar.is_workday(christmas_day) == True) # It's a Thursday
christmas_exception = ScalendarException.objects.create(calendar=default_calendar,
name="Christmas Day Exception", date=christmas_day, working=False)
assert(default_scalendar.is_workday(christmas_day) == False)
assert(default_scalendar.is_not_workday(christmas_day) == True)
You can also, for example, override a specific weekend date to be a workday:
a_sunday = date(2014, 6, 8)
assert(default_scalendar.is_workday(a_sunday) == False)
working_sunday_exception = default_scalendar.exceptions.create(name="Working this Sunday",
date=a_sunday, working=True)
assert(default_scalendar.is_workday(a_sunday) == True)
The ScalendarException
constructor accepts an end_date
argument for specifying a range of dates for the exception (if not provided, the end_date
is set to the same value as date
when the object is saved):
start_date = date(2014, 8, 1)
end_date = date(2014, 8, 31)
august_vacation = ScalendarException.objects.create(calendar=default_calendar,
name="August Vacation", date=start_date, end_date=end_date,
working=False)
assert(default_scalendar.is_workday(date(2014, 8, 4)) == False)
The behaviour of overlapping exceptions is not defined.
Recurring exceptions are not yet supported.