All holidays are defined in YAML files in the data/
directory. These definition files have three main parts: months, methods and tests. Before you start, you may want to look some of the existing files at code.dunae.ca/svn/holidays/trunk/data.
Holidays are grouped by month from 1 through 12. Each entry within a month can have several fields.
name
-
The name of the holiday.
regions
-
One or more region codes.
mday
-
Integer representing day of the month (1 through 31).
For example, the following holiday is on the first of January and available in the ca
, us
and au
regions.
1: - name: New Year's Day regions: [ca, us, au] mday: 1
wday
-
Integer representing day of the week (0 = Sunday through 6 = Saturday).
week
-
Integer representing week number (1 = first week, 3 = third week, -1 = last week),
For example, the following holiday is on the first Monday of September and available in the ca
region.
9: - name: Labour Day regions: [ca] week: 1 wday: 1
In addition to defining holidays by day or week, you can create custom methods to calculate a date.
For example, Canada celebrates Victoria Day, which falls on the Monday on or before May 24. So, under the methods
section we could create a custom method that returns a Date object.
methods: ca_victoria_day: | def self.ca_victoria_day(year) date = Date.civil(year,5,24) if date.wday > 1 date -= (date.wday - 1) elsif date.wday == 0 date -= 6 end date end
This would be represented in the months
section as:
5: - name: Victoria Day regions: [ca] function: ca_victoria_day(year)
If a holiday can occur in different months (e.g. Easter) it can go in the ‘0’ month.
0: - name: Easter Monday regions: [ca] function: easter(year)+1
Calculated-date functions take the year (integer) as a parameter and must return either a Date object or an integer representing the day of the month.
Several built-in methods are available for holidays that are observed on varying dates. For example, for a holiday that is observed on Monday if it falls on a weekend you could write:
7: - name: Canada Day regions: [ca] mday: 1 observed: to_monday_if_weekend(date)
Methods included in the Holidays module are:
-
Holidays#to_monday_if_sunday
-
Holidays#to_monday_if_weekend
-
Holidays#to_weekday_if_boxing_weekend
-
Holidays#to_weekday_if_weekend
Observed-date functions take a Date object as a parameter and must return either a Date object or an integer representing the day of the month.
All definition files should have tests included. In the YAML file, tests are just a block of Ruby code.
tests: | {Date.civil(2008,1,1) => 'New Year\'s Day', Date.civil(2008,3,21) => 'Good Friday', Date.civil(2008,3,24) => 'Easter Monday', Date.civil(2008,9,1) => 'Labour Day', Date.civil(2008,12,25) => 'Christmas Day', Date.civil(2008,12,26) => 'Boxing Day'}.each do |date, name| assert_equal name, (Holidays.on(date, :ca, :informal)[0] || {})[:name] end # Victoria Day [Date.civil(2004,5,24), Date.civil(2005,5,23), Date.civil(2006,5,22), Date.civil(2007,5,21), Date.civil(2008,5,19)].each do |date| assert_equal 'Victoria Day', Holidays.on(date, :ca)[0][:name] end