Handling Holidays and Business Hours

It seems I’m always apologizing for not writing blog posts more often. I looked back and see that the last entry was back in July. Things have been tremendously busy to say the least.

I ran into a quick requirement the other day for NDstorefront’s new admin application. An important metric to track is the time between order placement and fulfillment. Pretty straightforward requirement, right? Of course upon further inspection, it became clear that in order to have meaningful data on these times between order placement and shipping of the order, we’d need to take into account business hours. For example, if an order is placed on a Saturday night and fulfilled by the distributor on the following Monday, it’s effectively same-day fulfillment, as Monday is the first business day following the order date.

To that end, I located a pretty useful gem called business_time that handles this quite nicely. It’s an extension to ActiveSupport that adds some nice extensions to Time and Date to say things like:


You can configure the business hours as well to properly manage times outside of operating hours like so:

BusinessTime::Config.beginning_of_workday = "8:30 am"
BusinessTime::Config.end_of_workday = "5:00 pm"

I won’t go into the details of implementation as the documentation is quite good. This of course is only half a solution as it really only accounts for weekdays versus weekends as workdays. There is holiday support in the gem, but you must configure the list of holidays. I really didn’t want to be in the business of configuring and maintaining a set of holidays for each year. As well, there are all sorts of special cases as to when statutory holidays are observed as was the case in our last Christmas season. December 25th fell on a Sunday in 2011 and here in Ontario, that effectively meant Monday and Tuesday were stat holidays (with the observation of Boxing Day being shifted one day because Christmas was shifted as well).

Seems like a possible nightmare. Enter the holidays gem.

This is really well written gem by a fellow Canadian Alex Dunae out of Vancouver (Island). He’s tackled the non-trivial task of determining holidays for particular locales, including handling tricky cases like the one mentioned above, Easter (which is really tricky because it can fall in different months) and whether you want the actual holiday dates or the observed dates.

For my purpose, I needed to fork the gem and add some functionality to handle some dates for Ontario in a different manner. In practice, New Year’s Day is observed on a Monday when falling on a weekend as it is granted as a statutory holiday. The gem also didn’t quite have the support for the case where Boxing Day falls on a Monday , but needs to be shifted to Tuesday to make way for Christmas Day. Fortunately, modifying it was a breeze. Alex has used YAML files to describe the rules that in turn build the code required via Rake task.

With a modified version of the gem in hand, I was now able to feed the holidays into business_time. I worked on the assumption that we would be deploying a new app within 2 years and only needed to project that far forward with holidays:

Holidays.between(Date.civil(2011, 1, 1), 2.years.from_now, :ca_on, :observed).map{|holiday| BusinessTime::Config.holidays << holiday[:date]}

And that was it. I was able to look back on some recent data we had collected on shipments by our distributor and get an accurate reflection just how long it was actually taking to fulfill orders.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s