Skip to content

Instantly share code, notes, and snippets.

@gregory
Created July 5, 2012 13:15
Show Gist options
  • Save gregory/3053591 to your computer and use it in GitHub Desktop.
Save gregory/3053591 to your computer and use it in GitHub Desktop.
Date computation for holidays
module Holidays
module DateComputation
def holiday?(iso='fr')
require_or_load "holidays/#{iso}/computation"
holidays = Holidays::const_get(iso.camelize)::Settings.holidays[iso].merge(Holidays::Config::Settings.holidays)
holidays.each do |name, settings|
holiday_period = Date.from_settings(name, holidays, iso)
today_is_holiday = holiday_period.is_a?(Range) ? holiday_period.include?(self) : holiday_period == self
return true if today_is_holiday
end
false
end
def in_the_holiday_period?(period)
period.cover? self
end
module ClassMethods
# Public: Compute a date through Computation module
#
# name - the name of the method
# iso - the iso namespace
#
# Examples
#
# from_compute('easter', 'fr')
# # => '2012-04-08'
#
# Returns a Date.
def from_compute(name, iso)
require_or_load "holidays/#{iso}/computation"
Holidays::const_get(iso.camelize)::Computation.send name
end
# Public: Compute a date from a hash
#
# options - Hash:
# - 'month': Integer for the month to compute
# - 'day': Integer for the day to compute
#
# Examples
#
# options =
# {
# 'month' => 4,
# 'day' => 8
# }
# from_day(options)
# # => '2012-04-08'
#
# Returns a Date
def from_day(options)
Date.strptime("#{options['month']}-#{options['day']}", '%m-%d')
end
# Public: Compute a date from a structured hash
#
# holiday_name - String of the holiday's name
# settings - Hash containing all the holidays
#
# Examples
#
# options =
# {
# 'sunday_of_pentecost' =>
# {
# 'type' => :referred,
# 'references' => 'other',
# 'delta' => 7.weeks
# },
# 'other' =>
# {
# 'type' => :day,
# 'month' => 4,
# 'day' => 8
# }
# }
# from_settings('sunday_of_pentecost', options, 'fr')
# # => '2012-04-08'
#
# Returns the duplicated String.
def from_settings(holiday_name, settings, iso, stack=[])
options = settings[holiday_name]
return case options['type']
when :computed then
from_compute(holiday_name, iso)
when :day then
from_day(options)
when :period then
date_begin = Date.parse("#{Date.today.year}-#{options['month']}-#{options['day']}")
(date_begin..(date_begin + options['delta']))
when :referred then
raise Holidays::CircularDependencyException if stack.include? options['references']
stack << options['references']
Date.from_settings(options['references'], settings, iso, stack) + options['delta']
end
end
end
def self.included(klass)
klass.extend ClassMethods
end
end
end
require 'spec_helper'
describe Holidays::DateComputation do
let(:iso) { 'fr' }
let(:settings) do
{
'easter' =>
{
'type' => :computed
},
'sunday_of_pentecost' =>
{
'type' => :referred,
'references' => 'easter',
'delta' => 7.weeks
},
'other' =>
{
'type' => :referred,
'references' => 'easter',
'delta' => - 7.weeks
},
'national_day' =>
{
'type' => :day,
'month' => 7,
'day' => 14
},
'fukuoka' =>
{
'type' => :period,
'month' => 05,
'day' => 03,
'delta' => 1.month
},
'fukushima' =>
{
'type' => :reffered,
'references' => 'fukuoka',
'delta' => 1.week
},
'xmass' =>
{
'type' => :day,
'month' => 12,
'day' => 25
}
}
end
let(:today) { Date.parse("#{Date.today.year}-01-01") }
let(:easter) { Date.parse("#{Date.today.year}-04-08") }
describe '#holiday?' do
context 'when today is in a holiday period' do
let(:today) { Date.parse("#{Date.today.year}-05-6") }
it 'return true' do
today.holiday?.should == true
end
end
context 'when today is a holiday day' do
let(:today) { Date.parse("#{Date.today.year}-12-25") }
it 'return true' do
today.holiday?.should == true
end
end
context 'when today is a holiday reffered to another' do
let(:today) { easter + 7.weeks }
it 'return true' do
today.holiday?.should == true
end
end
context 'when today is a computed holiday ' do
let(:today) { easter }
it 'return true' do
today.holiday?.should == true
end
end
end
describe '#in_the_holiday_period?(period)' do
let (:holiday_period) do
(easter..(easter + 7.weeks))
end
it 'return true if today is in the period' do
easter.send(:in_the_holiday_period?, holiday_period).should == true
end
it 'return true if today the last day of the period' do
(easter + 7.weeks).send(:in_the_holiday_period?, holiday_period).should == true
end
it 'return false if today is not in the period' do
today.send(:in_the_holiday_period?, holiday_period).should == false
end
end
describe '.from_compute(name)' do
context 'when name is easter' do
let (:name) { 'easter' }
it 'return the easter holiday date' do
Date.from_compute(name, iso).should == easter
end
end
end
describe '.from_day(options)' do
context 'when today is xmass' do
it 'return the date of today' do
Date.from_day(settings['xmass']).should == Date.strptime("12-25", '%m-%d')
end
end
end
describe '.from_settings(name, settings)' do
context 'when setting type is period' do
it 'return a period' do
date_begin = Date.from_day(settings['fukuoka'])
Date.from_settings('fukuoka', settings, iso).should == (date_begin..(date_begin + settings['fukuoka']['delta']))
end
end
context 'when setting type is computed' do
it 'return date from compute' do
Date.from_settings('easter', settings, iso).should == Date.from_compute('easter', 'fr')
end
end
context 'when setting type is day' do
it 'return date from day' do
Date.from_settings('national_day', settings, iso).should == Date.from_day(settings['national_day'])
end
end
context 'when setting type is referred' do
it 'return date with a delta from refered date' do
options = settings['sunday_of_pentecost']
Date.from_settings('sunday_of_pentecost', settings, iso).should ==
Date.from_settings(options['references'], settings, iso) + options['delta']
end
context 'when days_before is an option set to 7' do
it 'returns 7 weeks before easter' do
options = settings['other']
Date.stub(:from_settings, settings).and_return(Date.from_compute(options['references'], iso))
(Date.from_settings(options['references'], settings, iso) + options['delta']).should == easter - 7.weeks
end
end
context 'when days_after is an options set to 7' do
it 'returns 7 weeks after easter' do
options = settings['sunday_of_pentecost']
Date.stub(:from_settings, settings).and_return(Date.from_compute(options['references'], iso))
(Date.from_settings(options['references'], settings, iso) + options['delta']).should == easter + 7.weeks
end
end
context 'when reference is a period' do
it 'return the the range shifted from delta?'
end
context 'when reference is included more than once' do
let(:options) do
{
'sunday_of_pentecost' =>
{
'type' => :referred,
'references' => 'other',
'delta' => 7.weeks
},
'other' =>
{
'type' => :referred,
'references' => 'sunday_of_pentecost',
'delta' => - 7.weeks
}
}
end
it 'raise Holidays::CircularDependencyException' do
lambda { Date.from_settings('sunday_of_pentecost', options, iso) }.should raise_exception(Holidays::CircularDependencyException)
end
end
end
end
end
holidays:
fr:
easter:
type: :computed
sunday_of_pentecost:
type: :referred
references: 'easter'
delta: <%= 7.weeks %>
other:
type: :referred
references: 'easter'
delta: <%= - 1.weeks %>
national_day:
type: :day
month: 7
day: 14
fukuoka:
type: :period
month: 5
day: 3
delta: <%= 1.month %>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment