From 1da573274780ef8a7008923fde40666246140824 Mon Sep 17 00:00:00 2001 From: Alex Dunae Date: Thu, 22 Nov 2007 00:35:56 +0000 Subject: [PATCH] Updated data/ca.yaml Started documentation in CUSTOM DATES --- CUSTOM DATES | 104 ++++++++++++++++++++++++++++++++++ create_defs.rb | 16 ++++-- data/ca.yaml | 94 +++++++++++++++++++++++++++--- lib/holidays.rb | 44 +++++--------- rakefile.rb | 3 +- test/fixtures/ca.rb | 82 +++++++++++++++++++++++++++ test/fixtures/mixin_module.rb | 21 ------- test/test_mixins.rb | 18 +++--- 8 files changed, 307 insertions(+), 75 deletions(-) create mode 100644 CUSTOM DATES create mode 100644 test/fixtures/ca.rb delete mode 100644 test/fixtures/mixin_module.rb diff --git a/CUSTOM DATES b/CUSTOM DATES new file mode 100644 index 0000000..f91abcb --- /dev/null +++ b/CUSTOM DATES @@ -0,0 +1,104 @@ += Custom dates with the Ruby Holidays Gem + +It's easy to add your own custom holiday definitions to the Holidays gem. In a nutshell: +1. Build a definition file +2. Run a rake to make a custom module +3. require your new module + + +== Building the YAML file + +Definition files have two main parts: *months* and *methods*. Before you start, you may want to look some of the existing files at http://code.dunae.ca/svn/holidays/trunk/data. + +=== Months + +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. + +===== Dates defined by a fixed date (e.g. January 1st) + +[wday] Integer representing day of the month (1 through 31). + +For example, the following holiday is on the first of the month and available in the ca, us and au regions. + + - name: New Year's Day + regions: [ca,us,au] + mday: 1 + + +===== Dates defined by a week number (e.g. first Monday of a month) + +[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 the month and available in the ca region. + + - name: Labour Day + regions: [ca] + week: 1 + wday: 1 + + + + +=== Calculating dates with methods + +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: lambda { |year| ca_victoria_day(year) } + + +Functions called in this manner must return either a Date object or an integer representing the day of the month. + + + +== Building the definition module + +Once you have your YAML definition file you need to generate a Ruby module using rake. + +To build the module for school holidays + + rake generate Custom custom.yaml + + +Your module can include definitions from multiple YAML files. + + rake generate Custom ca.yaml us.yaml mx.yaml custom.yaml + + +Your custom module is ready to use. + + require 'holidays' + require 'path/to/custom' + + Holidays.by_day(date, :your_region) + + + +=== Sharing + +If you've built a definition file that others might enjoy, why not share it? + +Send any definition files to 'code' at 'dunae.ca'. diff --git a/create_defs.rb b/create_defs.rb index bfd7a9b..e01da81 100644 --- a/create_defs.rb +++ b/create_defs.rb @@ -100,14 +100,22 @@ module Holidays end Holidays.class_eval do - new_regions = [] + existing_regions = [] if const_defined?(:DEFINED_REGIONS) - new_regions = const_get(:DEFINED_REGIONS) + existing_regions = const_get(:DEFINED_REGIONS) remove_const(:DEFINED_REGIONS) end - const_set(:DEFINED_REGIONS, new_regions | Holidays::#{module_name}::DEFINED_REGIONS) + const_set(:DEFINED_REGIONS, existing_regions | Holidays::#{module_name}::DEFINED_REGIONS) - include Holidays::MixinModule + 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::#{module_name}::HOLIDAYS_BY_MONTH)) + const_set(:HOLIDAYS_BY_MONTH, Holidays::#{module_name}::HOLIDAYS_BY_MONTH) + + include Holidays::#{module_name} end EOC diff --git a/data/ca.yaml b/data/ca.yaml index 326b94b..57c1ae4 100644 --- a/data/ca.yaml +++ b/data/ca.yaml @@ -6,22 +6,95 @@ months: - name: Good Friday regions: [ca] function: lambda { |year| easter(year)-2 } - - name: Easter Sunday - regions: [ca] - function: lambda { |year| easter(year) } + - name: Easter Monday + regions: [ca_qc] + function: lambda { |year| easter(year)+1 } 1: - name: New Year's Day regions: [ca] mday: 1 + - name: New Year's + regions: [ca_qc] + mday: 2 + 2: + - name: Family Day + regions: [ca_ab, ca_on, ca_sk] + wday: 1 + week: 3 + - name: Louis Riel Day + regions: [ca_mb] + wday: 1 + week: 3 + 3: + - name: St. Patrick's Day + regions: [ca_nf] + mday: 17 + 3: + - name: St. George's Day + regions: [ca_nf] + mday: 23 + 5: + - name: Victoria Day + regions: [ca] + function: lambda { |year| ca_victoria_day(year) } + - name: National Patriotes Day + regions: [ca_qc] + function: lambda { |year| ca_victoria_day(year) } + 6: + - name: Discovery Day + regions: [ca_nf] + mday: 24 + - name: Fête Nationale + regions: [ca_qc] + mday: 24 + - name: National Aboriginal Day + regions: [ca_nt] + mday: 21 7: - name: Canada Day regions: [ca] mday: 1 + - name: Orangemen's Day + regions: [ca] + mday: 12 + - name: Nunavut Day + regions: [ca_nu] + mday: 9 + 8: + - name: BC Day + week: 1 + regions: [ca_bc] + wday: 1 + - name: Saskatchewan Day + week: 1 + regions: [ca_sk] + wday: 1 + - name: Heritage Day + week: 1 + regions: [ca_ab] + wday: 1 + - name: Natal Day + week: 1 + regions: [ca_ns] + wday: 1 + - name: Civic Holiday + week: 1 + regions: [ca_on] + wday: 1 + - name: Discovery Day + week: 3 + regions: [ca_yk] + wday: 1 9: - name: Labour Day week: 1 regions: [ca] wday: 1 + 10: + - name: Thanksgiving + week: 2 + regions: [ca] + wday: 1 11: - name: Rememberance Day regions: [ca] @@ -35,10 +108,17 @@ months: mday: 26 methods: easter: | - def easter(year) + def self.easter(year) Date.civil(2008,1,1) end - tester: | - def tester(year) - Date.civil(2005,1,1) + ca_victoria_day: | + # 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 \ No newline at end of file diff --git a/lib/holidays.rb b/lib/holidays.rb index e79eb86..f02a40d 100644 --- a/lib/holidays.rb +++ b/lib/holidays.rb @@ -12,7 +12,7 @@ module Holidays 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] - DEFINED_REGIONS = [:ca, :us, :au, :gr, :fr, :christian] + DEFINED_REGIONS = [:us, :au] HOLIDAYS_TYPES = [:bank, :statutory, :religious, :informal] @@ -22,35 +22,8 @@ module Holidays # The month 0 is used to store events that require lambda calculations # and may occur in more than one month. HOLIDAYS_BY_MONTH = { - 0 => [{:function => lambda { |year| easter(year) }, :name => 'Easter Sunday', :regions => [:christian, :ca, :us]}, - {:function => lambda { |year| easter(year)-2 }, :name => 'Good Friday', :regions => [:christian, :ca, :us]}], - 1 => [{:mday => 1, :name => 'New Year\'s Day', :regions => [:us, :ca, :au]}, - {:mday => 1, :name => 'Australia Day', :regions => [:au]}, - {:mday => 6, :name => 'Epiphany', :regions => [:christian]}, - {:wday => 1, :week => :third, :name => 'Martin Luther King, Jr. Day', :regions => [:us]}], - 3 => [{:wday => 1, :week => :third, :name => 'George Washington\'s Birthday', :regions => [:us]}, - {:mday => 25, :name => 'Independence Day', :regions => [:gr]}], - 4 => [{:mday => 25, :name => 'ANZAC Day', :regions => [:au]}], - 5 => [{:mday => 1, :name => 'Labour Day', :regions => [:fr,:gr]}, - {:mday => 8, :name => 'Victoria 1945', :regions => [:fr]}, - {:wday => 6, :week => :third, :name => 'Armed Forces Day', :regions => [:us]}, - {:wday => 1, :week => :last, :name => 'Memorial Day', :regions => [:us]}], - 6 => [{:mday => 14, :name => 'Flag Day', :regions => [:us]}, - {:wday => 1, :week => :second, :name => 'Queen\'s Birthday', :regions => [:au]}], - 7 => [{:mday => 1, :name => 'Canada Day', :regions => [:ca]}, - {:mday => 4, :name => 'Independence Day', :regions => [:us]}, - {:mday => 14, :name => 'Ascension Day', :regions => [:fr]}], - 8 => [{:mday => 15, :name => 'Assumption of Mary', :regions => [:fr, :gr, :christian]}], - 9 => [{:wday => 1, :week => :first,:name => 'Labor Day', :regions => [:us]}, - {:wday => 1, :week => :first,:name => 'Labour Day', :regions => [:ca]}], - 10 => [{:wday => 1, :week => :second, :name => 'Columbus Day', :regions => [:us]}, - {:mday => 28, :name => 'National Day', :regions => [:gr]}], - 11 => [{:wday => 4, :week => :fourth, :name => 'Thanksgiving Day', :regions => [:us]}, - {:mday => 11, :name => 'Rememberance Day', :regions => [:ca,:au]}, - {:mday => 11, :name => 'Armistice 1918', :regions => [:fr]}, - {:mday => 1, :name => 'Touissant', :regions => [:fr]}], - 12 => [{:mday => 25, :name => 'Christmas Day', :regions => [:us,:ca,:christian,:au]}, - {:mday => 26, :name => 'Boxing Day', :regions => [:ca,:gr,:au]}] + 1 => [{:mday => 1, :name => 'New Year\'s Day', :regions => [:ca, :au]}, + {:mday => 1, :name => 'Australia Day', :regions => [:au]}] } @@ -130,7 +103,16 @@ module Holidays hbm.each do |h| next unless h[:regions].any?{ |reg| regions.include?(reg) } - unless h[:function] + if h[:function] + result = h[:function].call(year) + if result.kind_of?(Date) and result.mon == month + holidays << h.merge({:day => result.mday}) + else #if result == mday + holidays << h + end + + + else day = 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 diff --git a/rakefile.rb b/rakefile.rb index 9968700..ee7b3f1 100644 --- a/rakefile.rb +++ b/rakefile.rb @@ -21,6 +21,7 @@ Rake::RDocTask.new(:rdoc) do |rdoc| rdoc.title = 'Ruby Holidays Gem' rdoc.options << '--all' << '--inline-source' << '--line-numbers' rdoc.rdoc_files.include('README') + rdoc.rdoc_files.include('CUSTOM DATES') rdoc.rdoc_files.include('REFERENCES') rdoc.rdoc_files.include('LICENSE') rdoc.rdoc_files.include('lib/*.rb') @@ -41,7 +42,7 @@ spec = Gem::Specification.new do |s| s.files = FileList["{lib}/**/*"].to_a s.test_files = Dir.glob('test/test_*.rb') s.has_rdoc = true - s.extra_rdoc_files = ['README', 'REFERENCES', 'LICENSE'] + s.extra_rdoc_files = ['README', 'CUSTOM DATES', 'REFERENCES', 'LICENSE'] s.rdoc_options << '--all' << '--inline-source' << '--line-numbers' end diff --git a/test/fixtures/ca.rb b/test/fixtures/ca.rb new file mode 100644 index 0000000..e83fc8e --- /dev/null +++ b/test/fixtures/ca.rb @@ -0,0 +1,82 @@ +# 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/testmodule' +# +# More definitions are available at http://code.dunae.ca/holidays. +# +# Definitions loaded: data/ca.yaml +module Holidays + module TestModule + DEFINED_REGIONS = [:ca] + + 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]}, + {: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]}] + } + +def self.easter(year) + Date.civil(2008,1,1) +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::TestModule::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::TestModule::HOLIDAYS_BY_MONTH)) + const_set(:HOLIDAYS_BY_MONTH, Holidays::TestModule::HOLIDAYS_BY_MONTH) + + include Holidays::TestModule +end diff --git a/test/fixtures/mixin_module.rb b/test/fixtures/mixin_module.rb deleted file mode 100644 index c524a63..0000000 --- a/test/fixtures/mixin_module.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Holidays - module MixinModule - DEFINED_REGIONS = [:merged_a,:merged_b] - - def test_lambda(year) - 28 - end - end -end - -Holidays.class_eval do - # merge regions and holidays - new_regions = [] - if const_defined?(:DEFINED_REGIONS) - new_regions = const_get(:DEFINED_REGIONS) - remove_const(:DEFINED_REGIONS) - end - const_set(:DEFINED_REGIONS, new_regions | Holidays::MixinModule::DEFINED_REGIONS) - - include Holidays::MixinModule -end \ No newline at end of file diff --git a/test/test_mixins.rb b/test/test_mixins.rb index 26c7124..be9918b 100644 --- a/test/test_mixins.rb +++ b/test/test_mixins.rb @@ -1,15 +1,11 @@ require File.dirname(__FILE__) + '/test_helper' -require 'fixtures/mixin_module' +require 'fixtures/ca' -class MixinTests < Test::Unit::TestCase - def test_tester - #Holidays.append_features(Holidays::MixinModule) - puts Holidays.constants - puts Holidays::DEFINED_REGIONS.join(',') - assert Holidays.method_defined?(:test_lambda) - end - - def test_adding_region_constants - +class CATests < Test::Unit::TestCase + def test_ca_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.by_day(date, :ca)[0][:name] + end end end