Initial import
This commit is contained in:
commit
7268ece923
6 changed files with 308 additions and 0 deletions
48
README
Normal file
48
README
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
holidays
|
||||||
|
by FIX (your name)
|
||||||
|
FIX (url)
|
||||||
|
|
||||||
|
== DESCRIPTION:
|
||||||
|
|
||||||
|
FIX (describe your package)
|
||||||
|
|
||||||
|
== FEATURES/PROBLEMS:
|
||||||
|
|
||||||
|
* FIX (list of features or problems)
|
||||||
|
|
||||||
|
== SYNOPSIS:
|
||||||
|
|
||||||
|
FIX (code sample of usage)
|
||||||
|
|
||||||
|
== REQUIREMENTS:
|
||||||
|
|
||||||
|
* FIX (list of requirements)
|
||||||
|
|
||||||
|
== INSTALL:
|
||||||
|
|
||||||
|
* FIX (sudo gem install, anything else)
|
||||||
|
|
||||||
|
== LICENSE:
|
||||||
|
|
||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2007 FIX
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
149
lib/holidays.rb
Normal file
149
lib/holidays.rb
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
|
||||||
|
# === References
|
||||||
|
# ==== Calculations
|
||||||
|
# * http://michaelthompson.org/technikos/holidays.php
|
||||||
|
# ==== World
|
||||||
|
# * http://en.wikipedia.org/wiki/List_of_holidays_by_country
|
||||||
|
# ==== US
|
||||||
|
# * http://www.opm.gov/Operating_Status_Schedules/fedhol/index.asp
|
||||||
|
# * http://www.smart.net/~mmontes/ushols.html
|
||||||
|
module Holidays
|
||||||
|
# Exception class for dealing with unknown regions.
|
||||||
|
class UnkownRegionError < StandardError; end
|
||||||
|
|
||||||
|
VERSION = '1.0.0'
|
||||||
|
|
||||||
|
HOLIDAY_REGIONS = {:ca => 'Canada', :us => 'United States'}
|
||||||
|
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]
|
||||||
|
|
||||||
|
|
||||||
|
# :wday: Day of the week (0 is Sunday, 6 is Saturday)
|
||||||
|
HOLIDAYS_BY_MONTH = {
|
||||||
|
1 => [{:mday => 1, :name => 'New Year\'s Day', :regions => [:us, :ca]},
|
||||||
|
{:mday => 6, :name => 'Epiphany Day', :regions => [:gr]},
|
||||||
|
{: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]}],
|
||||||
|
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]}],
|
||||||
|
7 => [{:mday => 4, :name => 'Independence Day', :regions => [:us]},
|
||||||
|
{:mday => 14, :name => 'Ascension Day', :regions => [:fr]}],
|
||||||
|
8 => [{:mday => 15, :name => 'Assumption of Mary', :regions => [:fr, :gr, :christ]}],
|
||||||
|
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]},
|
||||||
|
{:mday => 11, :name => 'Armistice 1918', :regions => [:fr]},
|
||||||
|
{:mday => 1, :name => 'Touissant', :regions => [:fr]}],
|
||||||
|
12 => [{:mday => 25, :name => 'Christmas Day', :regions => [:us,:ca,:christ]},
|
||||||
|
{:mday => 26, :name => 'Boxing Day', :regions => [:ca,:gr]}]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get all holidays on a certain date
|
||||||
|
def self.lookup_holidays(date, regions = [:ca, :us])
|
||||||
|
#raise(UnkownRegionError, "No holiday information is available for region '#{region}'") unless known_region?(region)
|
||||||
|
|
||||||
|
regions = [regions] unless regions.kind_of?(Array)
|
||||||
|
hbm = HOLIDAYS_BY_MONTH[date.mon]
|
||||||
|
|
||||||
|
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 calculate_mday(year, month, h[:week], h[:wday]) == mday
|
||||||
|
holidays << h
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
holidays
|
||||||
|
end
|
||||||
|
|
||||||
|
#def self.calculate_mday(yr, mo, wday, int)
|
||||||
|
# earliest = 1 + 7 * (int - 1)
|
||||||
|
|
||||||
|
# wd = Date.civil(yr, mo, earliest).wday
|
||||||
|
# if wday == earliest
|
||||||
|
# off = 0
|
||||||
|
# else
|
||||||
|
# if wday < wd
|
||||||
|
# off = wday + (7 - wd)
|
||||||
|
# else
|
||||||
|
# off = (wday + (7 - wd)) - 7
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
|
||||||
|
# earliest + off
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Calculate the day of the month based on week and day of the week.
|
||||||
|
#
|
||||||
|
# First Monday of Jan, 2008
|
||||||
|
# calculate_mday(2008, 1, :first, :monday)
|
||||||
|
#
|
||||||
|
# Third Thursday of Dec, 2008
|
||||||
|
# calculate_mday(2008, 12, :third, 4)
|
||||||
|
#
|
||||||
|
# Last Monday of Jan, 2008
|
||||||
|
# calculate_mday(2008, 1, :last, 1)
|
||||||
|
#--
|
||||||
|
# see http://www.irt.org/articles/js050/index.htm
|
||||||
|
def self.calculate_mday(year, month, week, wday)
|
||||||
|
raise ArgumentError, "Week paramater must be one of Holidays::WEEKS" unless WEEKS.include?(week)
|
||||||
|
|
||||||
|
nth = WEEKS[week]
|
||||||
|
|
||||||
|
if nth > 0
|
||||||
|
return (nth-1)*7 + 1 + (7 + wday - Date.civil(year, month,(nth-1)*7 + 1).wday)%7
|
||||||
|
end
|
||||||
|
|
||||||
|
days = MONTH_LENGTHS[month]
|
||||||
|
if month == 2 and Date.civil(year,1,1).leap?
|
||||||
|
days = 29
|
||||||
|
end
|
||||||
|
|
||||||
|
return days - (Date.civil(year, month, days).wday - wday + 8)%7;
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.holidays_in_range(from, to, regions = [:ca,:us])
|
||||||
|
regions = [regions] unless regions.kind_of?(Array)
|
||||||
|
holidays = []
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Date
|
||||||
|
include Holidays
|
||||||
|
|
||||||
|
# Date.civil('2008-01-01').is_holiday?(:us)
|
||||||
|
def is_holiday?(region = 'us')
|
||||||
|
region = region.to_sym
|
||||||
|
|
||||||
|
holidays = Holidays.lookup_holidays(self, region)
|
||||||
|
if holidays
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
15
rakefile.rb
Normal file
15
rakefile.rb
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
require 'rake'
|
||||||
|
require 'rake/testtask'
|
||||||
|
require 'rake/rdoctask'
|
||||||
|
require 'rake/gempackagetask'
|
||||||
|
require 'fileutils'
|
||||||
|
require 'lib/holidays'
|
||||||
|
|
||||||
|
desc 'Run the unit tests.'
|
||||||
|
Rake::TestTask.new do |t|
|
||||||
|
t.libs << 'lib'
|
||||||
|
t.libs << 'lib/test'
|
||||||
|
t.test_files = FileList['test/test*.rb'].exclude('test_helper.rb')
|
||||||
|
t.verbose = false
|
||||||
|
end
|
||||||
|
|
19
test/benchmark.rb
Normal file
19
test/benchmark.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
require File.dirname(__FILE__) + '/test_helper'
|
||||||
|
require 'date'
|
||||||
|
require 'benchmark'
|
||||||
|
|
||||||
|
|
||||||
|
n = 10000
|
||||||
|
Benchmark.bm do |x|
|
||||||
|
x.report('calculate_mday') do
|
||||||
|
n.times do
|
||||||
|
|
||||||
|
Holidays.calculate_mday(2008, 1, 1, 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
x.report('calculate_mdaya') do
|
||||||
|
n.times do
|
||||||
|
Holidays.calculate_mdaya(:third, 1, 1, 2008)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
5
test/test_helper.rb
Normal file
5
test/test_helper.rb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../'))
|
||||||
|
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__), '../lib/'))
|
||||||
|
require 'rubygems'
|
||||||
|
require 'test/unit'
|
||||||
|
require 'holidays'
|
72
test/test_holidays.rb
Normal file
72
test/test_holidays.rb
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
require File.dirname(__FILE__) + '/test_helper'
|
||||||
|
require 'date'
|
||||||
|
# Test cases for reading and generating CSS shorthand properties
|
||||||
|
class HolidayTests < Test::Unit::TestCase
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@date = Date.civil(2008,1,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_extending_date_class
|
||||||
|
assert @date.respond_to?('is_holiday?')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_calculating_mdays
|
||||||
|
assert_equal 21, Holidays.calculate_mday(2008, 1, :third, 1)
|
||||||
|
assert_equal 1, Holidays.calculate_mday(2007, 1, :first, 1)
|
||||||
|
assert_equal 2, Holidays.calculate_mday(2007, 3, :first, 5)
|
||||||
|
assert_equal 25, Holidays.calculate_mday(2008, 5, :last, 1)
|
||||||
|
|
||||||
|
|
||||||
|
# Labour day
|
||||||
|
assert_equal 3, Holidays.calculate_mday(2007, 9, :first, 1)
|
||||||
|
assert_equal 1, Holidays.calculate_mday(2008, 9, :first, 1)
|
||||||
|
assert_equal 7, Holidays.calculate_mday(2009, 9, :first, 1)
|
||||||
|
assert_equal 5, Holidays.calculate_mday(2011, 9, :first, 1)
|
||||||
|
assert_equal 5, Holidays.calculate_mday(2050, 9, :first, 1)
|
||||||
|
assert_equal 4, Holidays.calculate_mday(2051, 9, :first, 1)
|
||||||
|
|
||||||
|
# Canadian thanksgiving
|
||||||
|
assert_equal 8, Holidays.calculate_mday(2007, 10, :second, 1)
|
||||||
|
assert_equal 13, Holidays.calculate_mday(2008, 10, :second, 1)
|
||||||
|
assert_equal 12, Holidays.calculate_mday(2009, 10, :second, 1)
|
||||||
|
assert_equal 11, Holidays.calculate_mday(2010, 10, :second, 1)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_region_params
|
||||||
|
holidays = Holidays.lookup_holidays(@date, :us)
|
||||||
|
assert_equal 1, holidays.length
|
||||||
|
|
||||||
|
holidays = Holidays.lookup_holidays(@date, [:us,:ca])
|
||||||
|
assert_equal 1, holidays.length
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_lookup_holidays_spot_checks
|
||||||
|
h = Holidays.lookup_holidays(Date.civil(2008,5,1), :gr)
|
||||||
|
assert_equal 'Labour Day', h[0][:name]
|
||||||
|
|
||||||
|
h = Holidays.lookup_holidays(Date.civil(2045,11,1), :fr)
|
||||||
|
assert_equal 'Touissant', h[0][:name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_lookup_holidays_and_iterate
|
||||||
|
holidays = Holidays.lookup_holidays(@date, :ca)
|
||||||
|
holidays.each do |h|
|
||||||
|
puts h[:name]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_lookup_holiday
|
||||||
|
holidays = Holidays.lookup_holidays(Date.civil(2008,1,21), :ca)
|
||||||
|
assert_equal 0, holidays.length
|
||||||
|
|
||||||
|
holidays = Holidays.lookup_holidays(Date.civil(2008,1,21), :us)
|
||||||
|
assert_equal 1, holidays.length
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_basic
|
||||||
|
assert Date.civil(2008,1,1).is_holiday?('ca')
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Add table
Reference in a new issue