Improved test suite
Added support for :any and sub regions Added data/common_methods.yaml Updated docs
This commit is contained in:
parent
208e2eea70
commit
9b9b0b2046
11 changed files with 343 additions and 224 deletions
6
README
6
README
|
@ -1,13 +1,12 @@
|
||||||
= Ruby Holidays Gem
|
= Ruby Holidays Gem
|
||||||
|
|
||||||
A set of classes to deal with public/statutory holidays in Ruby.
|
A set of classes to deal with holidays in Ruby.
|
||||||
|
|
||||||
Extends Ruby's built-in Date class and supports custom holiday definition lists.
|
Extends Ruby's built-in Date class and supports custom holiday definition lists.
|
||||||
|
|
||||||
=== Examples
|
=== Examples
|
||||||
|
|
||||||
==== Using the Holidays class
|
==== Using the Holidays class
|
||||||
|
|
||||||
# Get all holidays on April 25, 2008 in Australia
|
# Get all holidays on April 25, 2008 in Australia
|
||||||
date = Date.civil(2008,4,25)
|
date = Date.civil(2008,4,25)
|
||||||
Holidays.by_day(date, :au)
|
Holidays.by_day(date, :au)
|
||||||
|
@ -23,9 +22,6 @@ Extends Ruby's built-in Date class and supports custom holiday definition lists.
|
||||||
|
|
||||||
|
|
||||||
==== Extending Ruby's Date class
|
==== Extending Ruby's Date class
|
||||||
require 'date'
|
|
||||||
require 'holidays'
|
|
||||||
|
|
||||||
# Lookup Canada Day in the :ca region
|
# Lookup Canada Day in the :ca region
|
||||||
Date.civil(2008,7,1).is_holiday?(:ca)
|
Date.civil(2008,7,1).is_holiday?(:ca)
|
||||||
=> true
|
=> true
|
||||||
|
|
|
@ -1,46 +1,53 @@
|
||||||
require 'yaml'
|
require 'yaml'
|
||||||
|
|
||||||
module_name = 'TestModule'
|
module_name = 'CA'
|
||||||
|
|
||||||
# Load the data files
|
# Load the data files
|
||||||
files = ['data/ca.yaml']
|
files = ['data/ca.yaml', 'data/common_methods.yaml']
|
||||||
regions = [] #wtf
|
regions = []
|
||||||
rules_by_month = {}
|
rules_by_month = {}
|
||||||
custom_methods = {}
|
custom_methods = {}
|
||||||
|
|
||||||
files.each do |file|
|
files.each do |file|
|
||||||
def_file = YAML.load_file(file)
|
def_file = YAML.load_file(file)
|
||||||
def_file['months'].each do |month, definitions|
|
puts "Loading #{file}"
|
||||||
rules_by_month[month] = []
|
if def_file['months']
|
||||||
definitions.each do |definition|
|
puts " importing dates..."
|
||||||
rule = {}
|
def_file['months'].each do |month, definitions|
|
||||||
definition.each do |key, val|
|
rules_by_month[month] = [] unless rules_by_month[month]
|
||||||
rule[key] = val
|
definitions.each do |definition|
|
||||||
end
|
rule = {}
|
||||||
regions = rule['regions'].collect { |r| r.to_sym }
|
definition.each do |key, val|
|
||||||
rule['regions'] = regions
|
rule[key] = val
|
||||||
|
end
|
||||||
|
|
||||||
existed = false
|
rule['regions'] = rule['regions'].collect { |r| r.to_sym }
|
||||||
# rules_by_month[month].each do |ex|
|
|
||||||
# if ex['name'] == rule['name'] and ex['wday'] == rule['wday'] and ex['mday'] == rule['mday'] and ex['week'] == rule['week']
|
|
||||||
# ex['regions'] << rule['regions'].flatten
|
|
||||||
# existed = true
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
unless existed
|
|
||||||
rules_by_month[month] << rule
|
|
||||||
end
|
|
||||||
|
|
||||||
end # /defs.each
|
regions << rule['regions']
|
||||||
|
|
||||||
|
exists = false
|
||||||
|
rules_by_month[month].each do |ex|
|
||||||
|
if ex['name'] == rule['name'] and ex['wday'] == rule['wday'] and ex['mday'] == rule['mday'] and ex['week'] == rule['week']
|
||||||
|
# ex['regions'] << rule['regions'].flatten
|
||||||
|
exists = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#unless exists
|
||||||
|
rules_by_month[month] << rule
|
||||||
|
#end
|
||||||
|
|
||||||
|
end # /defs.each
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if def_file['methods']
|
||||||
|
puts " importing methods..."
|
||||||
def_file['methods'].each do |name, code|
|
def_file['methods'].each do |name, code|
|
||||||
custom_methods[name] = code
|
custom_methods[name] = code
|
||||||
end # /methods.each
|
end # /methods.each
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Build the definitions
|
# Build the definitions
|
||||||
month_strs = []
|
month_strs = []
|
||||||
rules_by_month.each do |month, rules|
|
rules_by_month.each do |month, rules|
|
||||||
|
@ -76,20 +83,20 @@ end
|
||||||
|
|
||||||
# Build the output file
|
# Build the output file
|
||||||
out =<<-EOC
|
out =<<-EOC
|
||||||
# This file is generated by the Ruby Holiday gem.
|
|
||||||
#
|
|
||||||
# To use the definitions in the file, load them right after you load the
|
|
||||||
# Holiday gem:
|
|
||||||
#
|
|
||||||
# require 'holidays'
|
|
||||||
# require 'path/to/#{module_name.downcase}'
|
|
||||||
#
|
|
||||||
# More definitions are available at http://code.dunae.ca/holidays.
|
|
||||||
#
|
|
||||||
# Definitions loaded: #{files.join(',')}
|
|
||||||
module Holidays
|
module Holidays
|
||||||
module #{module_name}
|
# This file is generated by the Ruby Holiday gem.
|
||||||
DEFINED_REGIONS = [:#{regions.join(', :')}]
|
#
|
||||||
|
# Definitions loaded: #{files.join(', ')}
|
||||||
|
#
|
||||||
|
# To use the definitions in the file, load them right after you load the
|
||||||
|
# Holiday gem:
|
||||||
|
#
|
||||||
|
# require 'holidays'
|
||||||
|
# require 'path/to/#{module_name.downcase}'
|
||||||
|
#
|
||||||
|
# More definitions are available at http://code.dunae.ca/holidays.
|
||||||
|
module #{module_name} # :nodoc:
|
||||||
|
DEFINED_REGIONS = [:#{regions.flatten.uniq.join(', :')}]
|
||||||
|
|
||||||
HOLIDAYS_BY_MONTH = {
|
HOLIDAYS_BY_MONTH = {
|
||||||
#{month_strs.join(",\n")}
|
#{month_strs.join(",\n")}
|
||||||
|
|
|
@ -55,7 +55,7 @@ months:
|
||||||
regions: [ca]
|
regions: [ca]
|
||||||
mday: 1
|
mday: 1
|
||||||
- name: Orangemen's Day
|
- name: Orangemen's Day
|
||||||
regions: [ca]
|
regions: [ca_nf]
|
||||||
mday: 12
|
mday: 12
|
||||||
- name: Nunavut Day
|
- name: Nunavut Day
|
||||||
regions: [ca_nu]
|
regions: [ca_nu]
|
||||||
|
@ -65,7 +65,7 @@ months:
|
||||||
week: 1
|
week: 1
|
||||||
regions: [ca_bc]
|
regions: [ca_bc]
|
||||||
wday: 1
|
wday: 1
|
||||||
- name: Saskatchewan Day
|
- name: Saskatchewan Day
|
||||||
week: 1
|
week: 1
|
||||||
regions: [ca_sk]
|
regions: [ca_sk]
|
||||||
wday: 1
|
wday: 1
|
||||||
|
@ -107,10 +107,6 @@ months:
|
||||||
regions: [ca]
|
regions: [ca]
|
||||||
mday: 26
|
mday: 26
|
||||||
methods:
|
methods:
|
||||||
easter: |
|
|
||||||
def self.easter(year)
|
|
||||||
Date.civil(2008,1,1)
|
|
||||||
end
|
|
||||||
ca_victoria_day: |
|
ca_victoria_day: |
|
||||||
# Monday on or before May 24
|
# Monday on or before May 24
|
||||||
def self.ca_victoria_day(year)
|
def self.ca_victoria_day(year)
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
module Holidays
|
# Common method definitions for the Ruby Holiday gem.
|
||||||
module Easter
|
# Updated 2008-11-21.
|
||||||
|
---
|
||||||
|
methods:
|
||||||
|
easter: |
|
||||||
# Get the date of Easter in a given year.
|
# Get the date of Easter in a given year.
|
||||||
#
|
#
|
||||||
# +year+ must be a valid Gregorian year.
|
# +year+ must be a valid Gregorian year.
|
||||||
|
@ -8,7 +11,7 @@ module Holidays
|
||||||
#--
|
#--
|
||||||
# from http://snippets.dzone.com/posts/show/765
|
# from http://snippets.dzone.com/posts/show/765
|
||||||
# TODO: check year to ensure Gregorian
|
# TODO: check year to ensure Gregorian
|
||||||
def easter(year)
|
def self.easter(year)
|
||||||
y = year
|
y = year
|
||||||
a = y % 19
|
a = y % 19
|
||||||
b = y / 100
|
b = y / 100
|
||||||
|
@ -26,5 +29,4 @@ module Holidays
|
||||||
day = ((h + l - 7 * m + 114) % 31) + 1
|
day = ((h + l - 7 * m + 114) % 31) + 1
|
||||||
Date.civil(year, month, day)
|
Date.civil(year, month, day)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end
|
|
142
lib/holidays.rb
142
lib/holidays.rb
|
@ -1,32 +1,17 @@
|
||||||
$:.unshift File.dirname(__FILE__)
|
$:.unshift File.dirname(__FILE__)
|
||||||
require 'holidays/easter'
|
|
||||||
|
|
||||||
module Holidays
|
module Holidays
|
||||||
# Exception thrown when an unknown region is requested.
|
# Exception thrown when an unknown region is requested.
|
||||||
class UnkownRegionError < ArgumentError; end
|
class UnkownRegionError < ArgumentError; end
|
||||||
|
|
||||||
self.extend Easter
|
|
||||||
|
|
||||||
VERSION = '0.9.0'
|
VERSION = '0.9.0'
|
||||||
|
|
||||||
WEEKS = {:first => 1, :second => 2, :third => 3, :fourth => 4, :fifth => 5, :last => -1}
|
WEEKS = {:first => 1, :second => 2, :third => 3, :fourth => 4, :fifth => 5, :last => -1}
|
||||||
MONTH_LENGTHS = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||||
|
|
||||||
DEFINED_REGIONS = [:us, :au]
|
|
||||||
HOLIDAYS_TYPES = [:bank, :statutory, :religious, :informal]
|
|
||||||
|
|
||||||
|
|
||||||
# :wday Day of the week (0 is Sunday, 6 is Saturday)
|
|
||||||
# :function Can return an integer representing mday or a Date object.
|
|
||||||
#
|
|
||||||
# The month <tt>0</tt> is used to store events that require lambda calculations
|
|
||||||
# and may occur in more than one month.
|
|
||||||
HOLIDAYS_BY_MONTH = {
|
|
||||||
1 => [{:mday => 1, :name => 'New Year\'s Day', :regions => [:ca, :au]},
|
|
||||||
{:mday => 1, :name => 'Australia Day', :regions => [:au]}]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
#--
|
||||||
|
#HOLIDAYS_TYPES = [:bank, :statutory, :religious, :informal]
|
||||||
|
#++
|
||||||
|
|
||||||
# Get all holidays on a given date.
|
# Get all holidays on a given date.
|
||||||
#
|
#
|
||||||
|
@ -34,6 +19,16 @@ module Holidays
|
||||||
# [<tt>regions</tt>] A symbol (e.g. <tt>:ca</tt>) or an array of symbols
|
# [<tt>regions</tt>] A symbol (e.g. <tt>:ca</tt>) or an array of symbols
|
||||||
# (e.g. <tt>[:ca, :ca_bc, :us]</tt>).
|
# (e.g. <tt>[:ca, :ca_bc, :us]</tt>).
|
||||||
#
|
#
|
||||||
|
# Returns an array of hashes or nil. See Holidays#between for the output
|
||||||
|
# format.
|
||||||
|
#
|
||||||
|
# Also available via Date#holidays.
|
||||||
|
def self.on(date, regions = :any)
|
||||||
|
self.between(date, date, regions)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get all holidays occuring between two dates, inclusively.
|
||||||
|
#
|
||||||
# Returns an array of hashes or nil.
|
# Returns an array of hashes or nil.
|
||||||
#
|
#
|
||||||
# Each holiday is returned as a hash with the following fields:
|
# Each holiday is returned as a hash with the following fields:
|
||||||
|
@ -42,57 +37,16 @@ module Holidays
|
||||||
# [<tt>:day</tt>] Integer.
|
# [<tt>:day</tt>] Integer.
|
||||||
# [<tt>:name</tt>] String.
|
# [<tt>:name</tt>] String.
|
||||||
# [<tt>:regions</tt>] An array of region symbols.
|
# [<tt>:regions</tt>] An array of region symbols.
|
||||||
# [<tt>:types</tt>] An array of holiday-type symbols.
|
|
||||||
def self.by_day(date, regions = :any)
|
|
||||||
regions = validate_regions(regions)
|
|
||||||
|
|
||||||
hbm = HOLIDAYS_BY_MONTH.values_at(0,date.mon).flatten
|
|
||||||
|
|
||||||
holidays = []
|
|
||||||
|
|
||||||
year = date.year
|
|
||||||
month = date.month
|
|
||||||
mday = date.mday
|
|
||||||
wday = date.wday
|
|
||||||
|
|
||||||
hbm.each do |h|
|
|
||||||
# start with the region check
|
|
||||||
next unless h[:regions].any?{ |reg| regions.include?(reg) }
|
|
||||||
|
|
||||||
if h[:mday] and h[:mday] == mday
|
|
||||||
# fixed day of the month
|
|
||||||
holidays << h
|
|
||||||
elsif h[:wday] == wday
|
|
||||||
# by week calculation
|
|
||||||
if Date.calculate_mday(year, month, h[:week], h[:wday]) == mday
|
|
||||||
holidays << h
|
|
||||||
end
|
|
||||||
elsif h[:function]
|
|
||||||
result = h[:function].call(year)
|
|
||||||
if result.kind_of?(Date) and result.mon == month and result.mday == mday
|
|
||||||
holidays << h
|
|
||||||
elsif result == mday
|
|
||||||
holidays << h
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
holidays
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get all holidays occuring between two dates, inclusively.
|
|
||||||
#
|
|
||||||
# Returns an array of hashes or nil. See Holidays#by_day for the output
|
|
||||||
# format.
|
|
||||||
#--
|
#--
|
||||||
# TODO: do not take full months
|
# [<tt>:types</tt>] An array of holiday-type symbols.
|
||||||
def self.between(start_date, end_date, regions = :any)
|
def self.between(start_date, end_date, regions = :any)
|
||||||
regions = validate_regions(regions)
|
regions = validate_regions(regions)
|
||||||
holidays = []
|
holidays = []
|
||||||
|
|
||||||
dates = {}
|
dates = {}
|
||||||
(start_date..end_date).each do |date|
|
(start_date..end_date).each do |date|
|
||||||
dates[date.year] = Array.new unless dates[date.year]
|
# Always include month '0' for variable-month holidays
|
||||||
|
dates[date.year] = [0] unless dates[date.year]
|
||||||
# TODO: test this, maybe should push then flatten
|
# TODO: test this, maybe should push then flatten
|
||||||
dates[date.year] << date.month unless dates[date.year].include?(date.month)
|
dates[date.year] << date.month unless dates[date.year].include?(date.month)
|
||||||
end
|
end
|
||||||
|
@ -101,21 +55,24 @@ module Holidays
|
||||||
months.each do |month|
|
months.each do |month|
|
||||||
next unless hbm = HOLIDAYS_BY_MONTH[month]
|
next unless hbm = HOLIDAYS_BY_MONTH[month]
|
||||||
hbm.each do |h|
|
hbm.each do |h|
|
||||||
next unless h[:regions].any?{ |reg| regions.include?(reg) }
|
next unless in_region?(regions, h[:regions])
|
||||||
|
|
||||||
if h[:function]
|
if h[:function]
|
||||||
result = h[:function].call(year)
|
result = h[:function].call(year)
|
||||||
if result.kind_of?(Date) and result.mon == month
|
if result.kind_of?(Date)
|
||||||
holidays << h.merge({:day => result.mday})
|
month = result.month
|
||||||
else #if result == mday
|
mday = result.mday
|
||||||
holidays << h
|
else
|
||||||
|
day = result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
else
|
else
|
||||||
day = h[:mday] || Date.calculate_mday(year, month, h[:week], h[:wday])
|
mday = h[:mday] || Date.calculate_mday(year, month, h[:week], h[:wday])
|
||||||
holidays << {:month => month, :day => day, :year => year, :name => h[:name], :regions => h[:regions]}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if Date.new(year, month, mday).between?(start_date, end_date)
|
||||||
|
holidays << {:month => month, :day => mday, :year => year, :name => h[:name], :regions => h[:regions]}
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -135,6 +92,24 @@ private
|
||||||
regions
|
regions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check sub regions.
|
||||||
|
#
|
||||||
|
# When request :any, all holidays should be returned.
|
||||||
|
# When requesting :ca_bc, holidays in :ca or :ca_bc should be returned.
|
||||||
|
# When requesting :ca, only holidays in :ca should be returned.
|
||||||
|
def self.in_region?(requested, available) # :nodoc:
|
||||||
|
return true if requested.include?(:any)
|
||||||
|
|
||||||
|
# When an underscore is encountered, derive the parent regions
|
||||||
|
# symbol and include both in the requested array.
|
||||||
|
requested = requested.collect do |r|
|
||||||
|
r.to_s =~ /_/ ? [r, r.to_s.gsub(/_[\w]*$/, '').to_sym] : r
|
||||||
|
end
|
||||||
|
|
||||||
|
requested = requested.flatten.uniq
|
||||||
|
|
||||||
|
available.any? { |r| requested.include?(r) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -143,21 +118,27 @@ class Date
|
||||||
|
|
||||||
# Get holidays on the current date.
|
# Get holidays on the current date.
|
||||||
#
|
#
|
||||||
# Returns an array.
|
# Returns an array of hashes or nil. See Holidays#between for the output
|
||||||
|
# format.
|
||||||
#
|
#
|
||||||
# Date.civil('2008-01-01').holidays(:ca)
|
# Date.civil('2008-01-01').holidays(:ca)
|
||||||
# => [{:name => 'Canada Day',...}]
|
# => [{:name => 'Canada Day',...}]
|
||||||
|
#
|
||||||
|
# Also available via Holidays#on.
|
||||||
def holidays(regions = :any)
|
def holidays(regions = :any)
|
||||||
holidays = Holidays.by_day(self, regions)
|
Holidays.on(self, regions)
|
||||||
!holidays.empty?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if the current date is a holiday.
|
# Check if the current date is a holiday.
|
||||||
#
|
#
|
||||||
|
# Returns an array of hashes or nil. See Holidays#between for the output
|
||||||
|
# format.
|
||||||
|
#
|
||||||
# Date.civil('2008-01-01').holiday?(:ca)
|
# Date.civil('2008-01-01').holiday?(:ca)
|
||||||
# => true
|
# => true
|
||||||
def holiday?(regions = :any)
|
def holiday?(regions = :any)
|
||||||
!self.holidays(regions).empty?
|
holidays = self.holidays(regions)
|
||||||
|
holidays && !holidays.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Calculate day of the month based on the week number and the day of the
|
# Calculate day of the month based on the week number and the day of the
|
||||||
|
@ -185,9 +166,9 @@ class Date
|
||||||
#--
|
#--
|
||||||
# see http://www.irt.org/articles/js050/index.htm
|
# see http://www.irt.org/articles/js050/index.htm
|
||||||
def self.calculate_mday(year, month, week, wday)
|
def self.calculate_mday(year, month, week, wday)
|
||||||
raise ArgumentError, "Week parameter must be one of Holidays::WEEKS (provided #{week})." unless WEEKS.include?(week)
|
raise ArgumentError, "Week parameter must be one of Holidays::WEEKS (provided #{week})." unless WEEKS.include?(week) or WEEKS.has_value?(week)
|
||||||
|
|
||||||
week = WEEKS[week]
|
week = WEEKS[week] if week.kind_of?(Symbol)
|
||||||
|
|
||||||
# :first, :second, :third, :fourth or :fifth
|
# :first, :second, :third, :fourth or :fifth
|
||||||
if week > 0
|
if week > 0
|
||||||
|
@ -195,9 +176,8 @@ class Date
|
||||||
end
|
end
|
||||||
|
|
||||||
days = MONTH_LENGTHS[month-1]
|
days = MONTH_LENGTHS[month-1]
|
||||||
if month == 1 and Date.civil(year,1,1).leap?
|
|
||||||
days = 29
|
days = 29 if month == 1 and Date.civil(year,1,1).leap?
|
||||||
end
|
|
||||||
|
|
||||||
return days - ((Date.civil(year, month, days).wday - wday + 7) % 7)
|
return days - ((Date.civil(year, month, days).wday - wday + 7) % 7)
|
||||||
end
|
end
|
||||||
|
|
105
lib/holidays/ca.rb
Normal file
105
lib/holidays/ca.rb
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
module Holidays
|
||||||
|
# This file is generated by the Ruby Holiday gem.
|
||||||
|
#
|
||||||
|
# Definitions loaded: data/ca.yaml, data/common_methods.yaml
|
||||||
|
#
|
||||||
|
# To use the definitions in the file, load them right after you load the
|
||||||
|
# Holiday gem:
|
||||||
|
#
|
||||||
|
# require 'holidays'
|
||||||
|
# require 'path/to/ca'
|
||||||
|
#
|
||||||
|
# More definitions are available at http://code.dunae.ca/holidays.
|
||||||
|
module CA # :nodoc:
|
||||||
|
DEFINED_REGIONS = [:ca, :ca_qc, :ca_nf, :ca_nt, :ca_nu, :ca_ab, :ca_on, :ca_sk, :ca_mb, :ca_bc, :ca_ns, :ca_yk]
|
||||||
|
|
||||||
|
HOLIDAYS_BY_MONTH = {
|
||||||
|
5 => [{:function => lambda { |year| ca_victoria_day(year) }, :name => "Victoria Day", :regions => [:ca]},
|
||||||
|
{:function => lambda { |year| ca_victoria_day(year) }, :name => "National Patriotes Day", :regions => [:ca_qc]}],
|
||||||
|
0 => [{:function => lambda { |year| easter(year)-2 }, :name => "Good Friday", :regions => [:ca]},
|
||||||
|
{:function => lambda { |year| easter(year)+1 }, :name => "Easter Monday", :regions => [:ca_qc]}],
|
||||||
|
11 => [{:mday => 11, :name => "Rememberance Day", :regions => [:ca]}],
|
||||||
|
6 => [{:mday => 24, :name => "Discovery Day", :regions => [:ca_nf]},
|
||||||
|
{:mday => 24, :name => "Fête Nationale", :regions => [:ca_qc]},
|
||||||
|
{:mday => 21, :name => "National Aboriginal Day", :regions => [:ca_nt]}],
|
||||||
|
1 => [{:mday => 1, :name => "New Year's Day", :regions => [:ca]},
|
||||||
|
{:mday => 2, :name => "New Year's", :regions => [:ca_qc]}],
|
||||||
|
12 => [{:mday => 25, :name => "Christmas Day", :regions => [:ca]},
|
||||||
|
{:mday => 26, :name => "Boxing Day", :regions => [:ca]}],
|
||||||
|
7 => [{:mday => 1, :name => "Canada Day", :regions => [:ca]},
|
||||||
|
{:mday => 12, :name => "Orangemen's Day", :regions => [:ca_nf]},
|
||||||
|
{:mday => 9, :name => "Nunavut Day", :regions => [:ca_nu]}],
|
||||||
|
2 => [{:wday => 1, :week => 3, :name => "Family Day", :regions => [:ca_ab, :ca_on, :ca_sk]},
|
||||||
|
{:wday => 1, :week => 3, :name => "Louis Riel Day", :regions => [:ca_mb]}],
|
||||||
|
8 => [{:wday => 1, :week => 1, :name => "BC Day", :regions => [:ca_bc]},
|
||||||
|
{:wday => 1, :week => 1, :name => "Saskatchewan Day", :regions => [:ca_sk]},
|
||||||
|
{:wday => 1, :week => 1, :name => "Heritage Day", :regions => [:ca_ab]},
|
||||||
|
{:wday => 1, :week => 1, :name => "Natal Day", :regions => [:ca_ns]},
|
||||||
|
{:wday => 1, :week => 1, :name => "Civic Holiday", :regions => [:ca_on]},
|
||||||
|
{:wday => 1, :week => 3, :name => "Discovery Day", :regions => [:ca_yk]}],
|
||||||
|
3 => [{:mday => 23, :name => "St. George's Day", :regions => [:ca_nf]}],
|
||||||
|
9 => [{:wday => 1, :week => 1, :name => "Labour Day", :regions => [:ca]}],
|
||||||
|
10 => [{:wday => 1, :week => 2, :name => "Thanksgiving", :regions => [:ca]}]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the date of Easter in a given year.
|
||||||
|
#
|
||||||
|
# +year+ must be a valid Gregorian year.
|
||||||
|
#
|
||||||
|
# Returns a Date object.
|
||||||
|
#--
|
||||||
|
# from http://snippets.dzone.com/posts/show/765
|
||||||
|
# TODO: check year to ensure Gregorian
|
||||||
|
def self.easter(year)
|
||||||
|
y = year
|
||||||
|
a = y % 19
|
||||||
|
b = y / 100
|
||||||
|
c = y % 100
|
||||||
|
d = b / 4
|
||||||
|
e = b % 4
|
||||||
|
f = (b + 8) / 25
|
||||||
|
g = (b - f + 1) / 3
|
||||||
|
h = (19 * a + b - d - g + 15) % 30
|
||||||
|
i = c / 4
|
||||||
|
k = c % 4
|
||||||
|
l = (32 + 2 * e + 2 * i - h - k) % 7
|
||||||
|
m = (a + 11 * h + 22 * l) / 451
|
||||||
|
month = (h + l - 7 * m + 114) / 31
|
||||||
|
day = ((h + l - 7 * m + 114) % 31) + 1
|
||||||
|
Date.civil(year, month, day)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# Monday on or before May 24
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Holidays.class_eval do
|
||||||
|
existing_regions = []
|
||||||
|
if const_defined?(:DEFINED_REGIONS)
|
||||||
|
existing_regions = const_get(:DEFINED_REGIONS)
|
||||||
|
remove_const(:DEFINED_REGIONS)
|
||||||
|
end
|
||||||
|
const_set(:DEFINED_REGIONS, existing_regions | Holidays::CA::DEFINED_REGIONS)
|
||||||
|
|
||||||
|
existing_defs = {}
|
||||||
|
if const_defined?(:HOLIDAYS_BY_MONTH)
|
||||||
|
existing_defs = const_get(:HOLIDAYS_BY_MONTH)
|
||||||
|
remove_const(:HOLIDAYS_BY_MONTH)
|
||||||
|
end
|
||||||
|
#const_set(:HOLIDAYS_BY_MONTH, existing_defs.merge(Holidays::CA::HOLIDAYS_BY_MONTH))
|
||||||
|
const_set(:HOLIDAYS_BY_MONTH, Holidays::CA::HOLIDAYS_BY_MONTH)
|
||||||
|
|
||||||
|
include Holidays::CA
|
||||||
|
end
|
|
@ -1,7 +1,10 @@
|
||||||
require File.dirname(__FILE__) + '/test_helper'
|
require File.dirname(__FILE__) + '/test_helper'
|
||||||
|
|
||||||
class HolidaysTests < Test::Unit::TestCase
|
class CommonMethodsTests < Test::Unit::TestCase
|
||||||
def test_easter_dates
|
|
||||||
|
def test_inclusion
|
||||||
|
assert Holidays.include?(Holidays::CA)
|
||||||
|
flunk
|
||||||
assert_equal '1800-04-13', Holidays.easter(1800).to_s
|
assert_equal '1800-04-13', Holidays.easter(1800).to_s
|
||||||
assert_equal '1899-04-02', Holidays.easter(1899).to_s
|
assert_equal '1899-04-02', Holidays.easter(1899).to_s
|
||||||
assert_equal '1900-04-15', Holidays.easter(1900).to_s
|
assert_equal '1900-04-15', Holidays.easter(1900).to_s
|
||||||
|
@ -14,16 +17,14 @@ class HolidaysTests < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_easter_lambda
|
def test_easter_lambda
|
||||||
[Date.civil(1800,4,13), Date.civil(1899,4,2), Date.civil(1900,4,15),
|
|
||||||
Date.civil(2008,3,23), Date.civil(2035,3,25)].each do |date|
|
|
||||||
assert_equal 'Easter Sunday', Holidays.by_day(date, :christian)[0][:name]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_good_friday_lambda
|
|
||||||
[Date.civil(1800,4,11), Date.civil(1899,3,31), Date.civil(1900,4,13),
|
[Date.civil(1800,4,11), Date.civil(1899,3,31), Date.civil(1900,4,13),
|
||||||
Date.civil(2008,3,21), Date.civil(2035,3,23)].each do |date|
|
Date.civil(2008,3,21), Date.civil(2035,3,23)].each do |date|
|
||||||
assert_equal 'Good Friday', Holidays.by_day(date, :christian)[0][:name]
|
assert_equal 'Good Friday', Holidays.on(date, :ca)[0][:name]
|
||||||
|
end
|
||||||
|
|
||||||
|
[Date.civil(1800,4,14), Date.civil(1899,4,3), Date.civil(1900,4,16),
|
||||||
|
Date.civil(2008,3,24), Date.civil(2035,3,26)].each do |date|
|
||||||
|
assert_equal 'Easter Monday', Holidays.on(date, :ca_qc)[0][:name]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
73
test/test_date.rb
Normal file
73
test/test_date.rb
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
require File.dirname(__FILE__) + '/test_helper'
|
||||||
|
|
||||||
|
class DateTests < Test::Unit::TestCase
|
||||||
|
def setup
|
||||||
|
@date = Date.civil(2008,1,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_extending_date_class
|
||||||
|
assert @date.respond_to?('holiday?')
|
||||||
|
assert @date.respond_to?('holiday?')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_calculating_mdays
|
||||||
|
# US Memorial day
|
||||||
|
assert_equal 29, Date.calculate_mday(2006, 5, :last, 1)
|
||||||
|
assert_equal 28, Date.calculate_mday(2007, 5, :last, 1)
|
||||||
|
assert_equal 26, Date.calculate_mday(2008, 5, :last, 1)
|
||||||
|
assert_equal 25, Date.calculate_mday(2009, 5, :last, 1)
|
||||||
|
assert_equal 31, Date.calculate_mday(2010, 5, :last, 1)
|
||||||
|
assert_equal 30, Date.calculate_mday(2011, 5, :last, 1)
|
||||||
|
|
||||||
|
# Labour day
|
||||||
|
assert_equal 3, Date.calculate_mday(2007, 9, :first, 1)
|
||||||
|
assert_equal 1, Date.calculate_mday(2008, 9, :first, 1)
|
||||||
|
assert_equal 7, Date.calculate_mday(2009, 9, :first, 1)
|
||||||
|
assert_equal 5, Date.calculate_mday(2011, 9, :first, 1)
|
||||||
|
assert_equal 5, Date.calculate_mday(2050, 9, :first, 1)
|
||||||
|
assert_equal 4, Date.calculate_mday(2051, 9, :first, 1)
|
||||||
|
|
||||||
|
# Canadian thanksgiving
|
||||||
|
assert_equal 8, Date.calculate_mday(2007, 10, :second, 1)
|
||||||
|
assert_equal 13, Date.calculate_mday(2008, 10, :second, 1)
|
||||||
|
assert_equal 12, Date.calculate_mday(2009, 10, :second, 1)
|
||||||
|
assert_equal 11, Date.calculate_mday(2010, 10, :second, 1)
|
||||||
|
|
||||||
|
# Misc
|
||||||
|
assert_equal 21, Date.calculate_mday(2008, 1, :third, 1)
|
||||||
|
assert_equal 1, Date.calculate_mday(2007, 1, :first, 1)
|
||||||
|
assert_equal 2, Date.calculate_mday(2007, 3, :first, 5)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_mday_allows_integers_or_symbols
|
||||||
|
assert_nothing_raised do
|
||||||
|
Date.calculate_mday(2008, 1, 1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_nothing_raised do
|
||||||
|
Date.calculate_mday(2008, 1, -1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_nothing_raised do
|
||||||
|
Date.calculate_mday(2008, 1, :last, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_mday_requires_valid_week
|
||||||
|
assert_raises ArgumentError do
|
||||||
|
Date.calculate_mday(2008, 1, :none, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raises ArgumentError do
|
||||||
|
Date.calculate_mday(2008, 1, nil, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_raises ArgumentError do
|
||||||
|
Date.calculate_mday(2008, 1, 0, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_holiday?
|
||||||
|
assert Date.civil(2008,1,1).holiday?('ca')
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,3 +4,4 @@ require 'rubygems'
|
||||||
require 'test/unit'
|
require 'test/unit'
|
||||||
require 'date'
|
require 'date'
|
||||||
require 'holidays'
|
require 'holidays'
|
||||||
|
require 'holidays/ca'
|
||||||
|
|
|
@ -6,67 +6,34 @@ class HolidaysTests < Test::Unit::TestCase
|
||||||
@date = Date.civil(2008,1,1)
|
@date = Date.civil(2008,1,1)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_extending_date_class
|
def test_on
|
||||||
assert @date.respond_to?('is_holiday?')
|
h = Holidays.on(Date.civil(2008,9,1), :ca)
|
||||||
|
assert_equal 'Labour Day', h[0][:name]
|
||||||
|
|
||||||
|
holidays = Holidays.on(Date.civil(2008,7,1), :ca)
|
||||||
|
assert_equal 1, holidays.length
|
||||||
|
|
||||||
|
holidays = Holidays.on(Date.civil(2008,7,4), :ca)
|
||||||
|
assert_equal 0, holidays.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_date_ranges
|
def test_between
|
||||||
holidays = Holidays.between(Date.civil(2008,1,1), Date.civil(2008,12,31), :au)
|
holidays = Holidays.between(Date.civil(2008,7,1), Date.civil(2008,7,31), :ca)
|
||||||
holidays.each do |h|
|
assert_equal 1, holidays.length
|
||||||
#puts h.inspect
|
|
||||||
end
|
holidays = Holidays.between(Date.civil(2008,7,2), Date.civil(2008,7,31), :ca)
|
||||||
|
assert_equal 0, holidays.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_calculating_mdays
|
|
||||||
# US Memorial day
|
|
||||||
assert_equal 29, Date.calculate_mday(2006, 5, :last, 1)
|
|
||||||
assert_equal 28, Date.calculate_mday(2007, 5, :last, 1)
|
|
||||||
assert_equal 26, Date.calculate_mday(2008, 5, :last, 1)
|
|
||||||
assert_equal 25, Date.calculate_mday(2009, 5, :last, 1)
|
|
||||||
assert_equal 31, Date.calculate_mday(2010, 5, :last, 1)
|
|
||||||
assert_equal 30, Date.calculate_mday(2011, 5, :last, 1)
|
|
||||||
|
|
||||||
# Labour day
|
|
||||||
assert_equal 3, Date.calculate_mday(2007, 9, :first, 1)
|
|
||||||
assert_equal 1, Date.calculate_mday(2008, 9, :first, 1)
|
|
||||||
assert_equal 7, Date.calculate_mday(2009, 9, :first, 1)
|
|
||||||
assert_equal 5, Date.calculate_mday(2011, 9, :first, 1)
|
|
||||||
assert_equal 5, Date.calculate_mday(2050, 9, :first, 1)
|
|
||||||
assert_equal 4, Date.calculate_mday(2051, 9, :first, 1)
|
|
||||||
|
|
||||||
# Canadian thanksgiving
|
|
||||||
assert_equal 8, Date.calculate_mday(2007, 10, :second, 1)
|
|
||||||
assert_equal 13, Date.calculate_mday(2008, 10, :second, 1)
|
|
||||||
assert_equal 12, Date.calculate_mday(2009, 10, :second, 1)
|
|
||||||
assert_equal 11, Date.calculate_mday(2010, 10, :second, 1)
|
|
||||||
|
|
||||||
# Misc
|
|
||||||
assert_equal 21, Date.calculate_mday(2008, 1, :third, 1)
|
|
||||||
assert_equal 1, Date.calculate_mday(2007, 1, :first, 1)
|
|
||||||
assert_equal 2, Date.calculate_mday(2007, 3, :first, 5)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_requires_valid_week
|
|
||||||
assert_raises ArgumentError do
|
|
||||||
Date.calculate_mday(2008, 1, :none, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_raises ArgumentError do
|
|
||||||
Date.calculate_mday(2008, 1, nil, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_raises ArgumentError do
|
|
||||||
Date.calculate_mday(2008, 1, 0, 1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_requires_valid_regions
|
def test_requires_valid_regions
|
||||||
assert_raises Holidays::UnkownRegionError do
|
assert_raises Holidays::UnkownRegionError do
|
||||||
Holidays.by_day(Date.civil(2008,1,1), :xx)
|
Holidays.on(Date.civil(2008,1,1), :xx)
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_raises Holidays::UnkownRegionError do
|
assert_raises Holidays::UnkownRegionError do
|
||||||
Holidays.by_day(Date.civil(2008,1,1), [:ca,:xx])
|
Holidays.on(Date.civil(2008,1,1), [:ca,:xx])
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_raises Holidays::UnkownRegionError do
|
assert_raises Holidays::UnkownRegionError do
|
||||||
|
@ -75,38 +42,30 @@ class HolidaysTests < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_region_params
|
def test_region_params
|
||||||
holidays = Holidays.by_day(@date, :us)
|
holidays = Holidays.on(@date, :ca)
|
||||||
assert_equal 1, holidays.length
|
assert_equal 1, holidays.length
|
||||||
|
|
||||||
holidays = Holidays.by_day(@date, [:us,:ca])
|
holidays = Holidays.on(@date, [:ca_bc,:ca])
|
||||||
assert_equal 1, holidays.length
|
assert_equal 1, holidays.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_by_day_spot_checks
|
def test_any_region
|
||||||
h = Holidays.by_day(Date.civil(2008,5,1), :gr)
|
# Should return Victoria Day
|
||||||
assert_equal 'Labour Day', h[0][:name]
|
holidays = Holidays.between(Date.civil(2008,5,1), Date.civil(2008,5,31), :ca)
|
||||||
|
|
||||||
h = Holidays.by_day(Date.civil(2045,11,1), :fr)
|
|
||||||
assert_equal 'Touissant', h[0][:name]
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_by_day_and_iterate
|
|
||||||
holidays = Holidays.by_day(@date, :ca)
|
|
||||||
holidays.each do |h|
|
|
||||||
#puts h[:name]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_lookup_holiday
|
|
||||||
holidays = Holidays.by_day(Date.civil(2008,1,21), :ca)
|
|
||||||
assert_equal 0, holidays.length
|
|
||||||
|
|
||||||
holidays = Holidays.by_day(Date.civil(2008,1,21), :us)
|
|
||||||
assert_equal 1, holidays.length
|
assert_equal 1, holidays.length
|
||||||
|
|
||||||
|
# Should return Victoria Day and National Patriotes Day
|
||||||
|
holidays = Holidays.between(Date.civil(2008,5,1), Date.civil(2008,5,31), :any)
|
||||||
|
assert_equal 2, holidays.length
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_basic
|
def test_sub_regions
|
||||||
assert Date.civil(2008,1,1).is_holiday?('ca')
|
# Should return Victoria Day
|
||||||
end
|
holidays = Holidays.between(Date.civil(2008,5,1), Date.civil(2008,5,31), :ca)
|
||||||
|
assert_equal 1, holidays.length
|
||||||
|
|
||||||
|
# Should return Victoria Day and National Patriotes Day
|
||||||
|
holidays = Holidays.between(Date.civil(2008,5,1), Date.civil(2008,5,31), :ca_qc)
|
||||||
|
assert_equal 2, holidays.length
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
require File.dirname(__FILE__) + '/test_helper'
|
require File.dirname(__FILE__) + '/test_helper'
|
||||||
require 'fixtures/ca'
|
|
||||||
|
|
||||||
class CATests < Test::Unit::TestCase
|
class CATests < Test::Unit::TestCase
|
||||||
def test_ca_victoria_day
|
def test_ca_victoria_day
|
||||||
[Date.civil(2004,5,24), Date.civil(2005,5,23), Date.civil(2006,5,22),
|
[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|
|
Date.civil(2007,5,21), Date.civil(2008,5,19)].each do |date|
|
||||||
assert_equal 'Victoria Day', Holidays.by_day(date, :ca)[0][:name]
|
assert_equal 'Victoria Day', Holidays.on(date, :ca)[0][:name]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue