holidays 2.2.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +22 -8
- data/Rakefile +26 -8
- data/bin/console +7 -0
- data/bin/setup +5 -0
- data/{data → definitions}/SYNTAX.rdoc +0 -0
- data/{data → definitions}/ar.yaml +0 -0
- data/{data → definitions}/at.yaml +1 -1
- data/{data → definitions}/au.yaml +80 -51
- data/{data → definitions}/be.yaml +0 -0
- data/{data → definitions}/br.yaml +0 -0
- data/{data → definitions}/ca.yaml +0 -0
- data/{data → definitions}/ch.yaml +0 -0
- data/{data → definitions}/cl.yaml +0 -0
- data/{data → definitions}/cr.yaml +0 -0
- data/{data → definitions}/cz.yaml +0 -0
- data/{data → definitions}/de.yaml +25 -34
- data/{data → definitions}/dk.yaml +0 -0
- data/{data → definitions}/ecb_target.yaml +2 -2
- data/{data → definitions}/el.yaml +0 -0
- data/{data → definitions}/es.yaml +37 -26
- data/{data → definitions}/federal_reserve.yaml +0 -0
- data/{data → definitions}/fedex.yaml +9 -3
- data/{data → definitions}/fi.yaml +0 -0
- data/{data → definitions}/fr.yaml +0 -0
- data/{data → definitions}/gb.yaml +19 -19
- data/{data → definitions}/hr.yaml +0 -0
- data/{data → definitions}/hu.yaml +1 -1
- data/{data → definitions}/ie.yaml +0 -0
- data/{data → definitions}/index.yaml +0 -0
- data/{data → definitions}/is.yaml +0 -0
- data/{data → definitions}/it.yaml +0 -0
- data/{data → definitions}/jp.yaml +3 -3
- data/{data → definitions}/li.yaml +3 -3
- data/{data → definitions}/lt.yaml +0 -0
- data/{data → definitions}/ma.yaml +0 -0
- data/{data → definitions}/mx.yaml +0 -0
- data/{data → definitions}/nerc.yaml +0 -0
- data/{data → definitions}/nl.yaml +0 -0
- data/{data → definitions}/no.yaml +0 -0
- data/{data → definitions}/north_america_informal.yaml +0 -0
- data/{data → definitions}/nyse.yaml +0 -0
- data/{data → definitions}/nz.yaml +21 -10
- data/{data → definitions}/ph.yaml +0 -0
- data/{data → definitions}/pl.yaml +0 -0
- data/{data → definitions}/pt.yaml +0 -0
- data/{data → definitions}/ro.yaml +1 -2
- data/{data → definitions}/se.yaml +0 -0
- data/{data → definitions}/sg.yaml +0 -0
- data/{data → definitions}/si.yaml +6 -6
- data/{data → definitions}/sk.yaml +0 -0
- data/{data → definitions}/united_nations.yaml +0 -0
- data/{data → definitions}/ups.yaml +17 -11
- data/{data → definitions}/us.yaml +20 -15
- data/{data → definitions}/ve.yaml +0 -0
- data/{data → definitions}/vi.yaml +0 -0
- data/{data → definitions}/za.yaml +0 -0
- data/holidays.gemspec +2 -1
- data/lib/generated_definitions/MANIFEST +56 -0
- data/lib/generated_definitions/REGIONS.rb +4 -0
- data/lib/{holidays → generated_definitions}/ar.rb +2 -2
- data/lib/{holidays → generated_definitions}/at.rb +2 -2
- data/lib/{holidays → generated_definitions}/au.rb +19 -10
- data/lib/{holidays → generated_definitions}/be.rb +2 -2
- data/lib/{holidays → generated_definitions}/br.rb +2 -2
- data/lib/{holidays → generated_definitions}/ca.rb +2 -2
- data/lib/{holidays → generated_definitions}/ch.rb +2 -2
- data/lib/{holidays → generated_definitions}/cl.rb +2 -2
- data/lib/{holidays → generated_definitions}/cr.rb +2 -2
- data/lib/{holidays → generated_definitions}/cz.rb +2 -2
- data/lib/{holidays → generated_definitions}/de.rb +5 -4
- data/lib/{holidays → generated_definitions}/dk.rb +2 -2
- data/lib/{holidays → generated_definitions}/ecb_target.rb +2 -2
- data/lib/{holidays → generated_definitions}/el.rb +2 -2
- data/lib/{holidays → generated_definitions}/es.rb +6 -6
- data/lib/{holidays → generated_definitions}/europe.rb +7 -6
- data/lib/{holidays → generated_definitions}/fed_ex.rb +8 -3
- data/lib/{holidays → generated_definitions}/federal_reserve.rb +2 -2
- data/lib/{holidays → generated_definitions}/fi.rb +2 -2
- data/lib/{holidays → generated_definitions}/fr.rb +2 -2
- data/lib/{holidays → generated_definitions}/gb.rb +2 -2
- data/lib/{holidays → generated_definitions}/hr.rb +2 -2
- data/lib/{holidays → generated_definitions}/hu.rb +2 -2
- data/lib/{holidays → generated_definitions}/ie.rb +2 -2
- data/lib/{holidays → generated_definitions}/is.rb +2 -2
- data/lib/{holidays → generated_definitions}/it.rb +2 -2
- data/lib/{holidays → generated_definitions}/jp.rb +5 -5
- data/lib/{holidays → generated_definitions}/li.rb +2 -2
- data/lib/{holidays → generated_definitions}/lt.rb +2 -2
- data/lib/{holidays → generated_definitions}/ma.rb +2 -2
- data/lib/{holidays → generated_definitions}/mx.rb +2 -2
- data/lib/{holidays → generated_definitions}/nerc.rb +2 -2
- data/lib/{holidays → generated_definitions}/nl.rb +2 -2
- data/lib/{holidays → generated_definitions}/no.rb +2 -2
- data/lib/{holidays → generated_definitions}/north_america.rb +8 -3
- data/lib/{holidays → generated_definitions}/nyse.rb +2 -2
- data/lib/{holidays → generated_definitions}/nz.rb +5 -5
- data/lib/{holidays → generated_definitions}/ph.rb +2 -2
- data/lib/{holidays → generated_definitions}/pl.rb +2 -2
- data/lib/{holidays → generated_definitions}/pt.rb +2 -2
- data/lib/{holidays → generated_definitions}/ro.rb +2 -2
- data/lib/{holidays → generated_definitions}/scandinavia.rb +2 -2
- data/lib/{holidays → generated_definitions}/se.rb +2 -2
- data/lib/{holidays → generated_definitions}/sg.rb +2 -2
- data/lib/{holidays → generated_definitions}/si.rb +2 -2
- data/lib/{holidays → generated_definitions}/sk.rb +2 -2
- data/lib/{holidays → generated_definitions}/united_nations.rb +2 -2
- data/lib/{holidays → generated_definitions}/ups.rb +8 -3
- data/lib/{holidays → generated_definitions}/us.rb +8 -3
- data/lib/{holidays → generated_definitions}/ve.rb +2 -2
- data/lib/{holidays → generated_definitions}/vi.rb +2 -2
- data/lib/{holidays → generated_definitions}/za.rb +2 -2
- data/lib/holidays.rb +120 -665
- data/lib/holidays/core_extensions/date.rb +39 -0
- data/lib/holidays/date_calculator/day_of_month.rb +68 -0
- data/lib/holidays/date_calculator/easter.rb +58 -0
- data/lib/holidays/date_calculator/weekend_modifier.rb +49 -0
- data/lib/holidays/date_calculator_factory.rb +21 -0
- data/lib/holidays/definition/context/generator.rb +216 -0
- data/lib/holidays/definition/context/merger.rb +26 -0
- data/lib/holidays/definition/repository/cache.rb +33 -0
- data/lib/holidays/definition/repository/holidays_by_month.rb +49 -0
- data/lib/holidays/definition/repository/proc_cache.rb +36 -0
- data/lib/holidays/definition/repository/regions.rb +36 -0
- data/lib/holidays/definition/validator/region.rb +45 -0
- data/lib/holidays/definition_factory.rb +50 -0
- data/lib/holidays/option/context/parse_options.rb +96 -0
- data/lib/holidays/option_factory.rb +14 -0
- data/lib/holidays/use_case/context/between.rb +105 -0
- data/lib/holidays/use_case/context/dates_driver_builder.rb +64 -0
- data/lib/holidays/use_case_factory.rb +20 -0
- data/lib/holidays/version.rb +1 -1
- data/test/defs/test_defs_ar.rb +1 -1
- data/test/defs/test_defs_at.rb +2 -2
- data/test/defs/test_defs_au.rb +61 -43
- data/test/defs/test_defs_be.rb +1 -1
- data/test/defs/test_defs_br.rb +1 -1
- data/test/defs/test_defs_ca.rb +1 -1
- data/test/defs/test_defs_ch.rb +1 -1
- data/test/defs/test_defs_cl.rb +1 -1
- data/test/defs/test_defs_cr.rb +1 -1
- data/test/defs/test_defs_cz.rb +1 -1
- data/test/defs/test_defs_de.rb +18 -30
- data/test/defs/test_defs_dk.rb +1 -1
- data/test/defs/test_defs_ecb_target.rb +3 -3
- data/test/defs/test_defs_el.rb +1 -1
- data/test/defs/test_defs_es.rb +36 -25
- data/test/defs/test_defs_europe.rb +82 -84
- data/test/defs/test_defs_fed_ex.rb +4 -1
- data/test/defs/test_defs_federal_reserve.rb +1 -1
- data/test/defs/test_defs_fi.rb +1 -1
- data/test/defs/test_defs_fr.rb +1 -1
- data/test/defs/test_defs_gb.rb +20 -20
- data/test/defs/test_defs_hr.rb +1 -1
- data/test/defs/test_defs_hu.rb +2 -2
- data/test/defs/test_defs_ie.rb +1 -1
- data/test/defs/test_defs_is.rb +1 -1
- data/test/defs/test_defs_it.rb +1 -1
- data/test/defs/test_defs_jp.rb +1 -1
- data/test/defs/test_defs_li.rb +4 -4
- data/test/defs/test_defs_lt.rb +1 -1
- data/test/defs/test_defs_ma.rb +1 -1
- data/test/defs/test_defs_mx.rb +1 -1
- data/test/defs/test_defs_nerc.rb +1 -1
- data/test/defs/test_defs_nl.rb +1 -1
- data/test/defs/test_defs_no.rb +1 -1
- data/test/defs/test_defs_north_america.rb +5 -3
- data/test/defs/test_defs_nyse.rb +1 -1
- data/test/defs/test_defs_nz.rb +19 -9
- data/test/defs/test_defs_ph.rb +1 -1
- data/test/defs/test_defs_pl.rb +1 -1
- data/test/defs/test_defs_pt.rb +1 -1
- data/test/defs/test_defs_ro.rb +2 -3
- data/test/defs/test_defs_scandinavia.rb +1 -1
- data/test/defs/test_defs_se.rb +1 -1
- data/test/defs/test_defs_sg.rb +1 -1
- data/test/defs/test_defs_si.rb +7 -7
- data/test/defs/test_defs_sk.rb +1 -1
- data/test/defs/test_defs_united_nations.rb +1 -1
- data/test/defs/test_defs_ups.rb +5 -2
- data/test/defs/test_defs_us.rb +5 -3
- data/test/defs/test_defs_ve.rb +1 -1
- data/test/defs/test_defs_vi.rb +1 -1
- data/test/defs/test_defs_za.rb +1 -1
- data/test/{test_date.rb → holidays/core_extensions/test_date.rb} +8 -2
- data/test/holidays/date_calculator/test_day_of_month.rb +27 -0
- data/test/holidays/date_calculator/test_easter.rb +29 -0
- data/test/holidays/date_calculator/test_weekend_modifier.rb +33 -0
- data/test/holidays/definition/context/test_generator.rb +84 -0
- data/test/holidays/definition/context/test_merger.rb +22 -0
- data/test/holidays/definition/repository/test_cache.rb +82 -0
- data/test/holidays/definition/repository/test_holidays_by_month.rb +187 -0
- data/test/holidays/definition/repository/test_proc_cache.rb +29 -0
- data/test/holidays/definition/repository/test_regions.rb +86 -0
- data/test/holidays/definition/validator/test_region.rb +50 -0
- data/test/holidays/option/context/test_parse_options.rb +69 -0
- data/test/holidays/test_date_calculator_factory.rb +21 -0
- data/test/holidays/test_definition_factory.rb +34 -0
- data/test/holidays/test_option_factory.rb +9 -0
- data/test/holidays/test_use_case_factory.rb +13 -0
- data/test/holidays/use_case/context/test_between.rb +75 -0
- data/test/holidays/use_case/context/test_dates_driver_builder.rb +91 -0
- data/test/test_all_regions.rb +14 -3
- data/test/test_helper.rb +2 -1
- data/test/test_holidays.rb +19 -24
- data/test/test_holidays_between.rb +85 -0
- data/test/test_multiple_regions.rb +2 -2
- data/test/test_parse_definitions.rb +10 -4
- metadata +181 -111
- data/.coveralls.yml +0 -1
- data/lib/holidays/MANIFEST +0 -55
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Context
|
|
4
|
+
# Merge a new set of definitions into the Holidays module.
|
|
5
|
+
#
|
|
6
|
+
# This method is automatically called when including holiday definition
|
|
7
|
+
# files. This is accomplished because the Generator class generates the
|
|
8
|
+
# definition source with this class explicitly.
|
|
9
|
+
class Merger
|
|
10
|
+
def initialize(holidays_by_month_repo, regions_repo)
|
|
11
|
+
@holidays_repo = holidays_by_month_repo
|
|
12
|
+
@regions_repo = regions_repo
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(target_regions, target_holidays)
|
|
16
|
+
regions_repo.add(target_regions)
|
|
17
|
+
holidays_repo.add(target_holidays)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
attr_reader :holidays_repo, :regions_repo
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Repository
|
|
4
|
+
class Cache
|
|
5
|
+
def initialize
|
|
6
|
+
reset!
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def cache_between(start_date, end_date, cache_data, *options)
|
|
10
|
+
raise ArgumentError unless cache_data
|
|
11
|
+
|
|
12
|
+
@cache_range[options] = start_date..end_date
|
|
13
|
+
@cache[options] = cache_data
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def find(start_date, end_date, *options)
|
|
17
|
+
if range = @cache_range[options]
|
|
18
|
+
if range.begin <= start_date && range.end >= end_date
|
|
19
|
+
return @cache[options].select do |holiday|
|
|
20
|
+
holiday[:date] >= start_date && holiday[:date] <= end_date
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def reset!
|
|
27
|
+
@cache = {}
|
|
28
|
+
@cache_range = {}
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Repository
|
|
4
|
+
class HolidaysByMonth
|
|
5
|
+
def initialize
|
|
6
|
+
@holidays_by_month = {}
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def all
|
|
10
|
+
@holidays_by_month
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def find_by_month(month)
|
|
14
|
+
raise ArgumentError unless month >= 0 && month <= 12
|
|
15
|
+
@holidays_by_month[month]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def add(new_holidays)
|
|
19
|
+
new_holidays.each do |month, holiday_defs|
|
|
20
|
+
@holidays_by_month[month] = [] unless @holidays_by_month[month]
|
|
21
|
+
|
|
22
|
+
holiday_defs.each do |holiday_def|
|
|
23
|
+
exists = false
|
|
24
|
+
@holidays_by_month[month].each do |existing_def|
|
|
25
|
+
if definition_exists?(existing_def, holiday_def)
|
|
26
|
+
# append regions
|
|
27
|
+
existing_def[:regions] << holiday_def[:regions]
|
|
28
|
+
|
|
29
|
+
# Should do this once we're done
|
|
30
|
+
existing_def[:regions].flatten!
|
|
31
|
+
existing_def[:regions].uniq!
|
|
32
|
+
exists = true
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
@holidays_by_month[month] << holiday_def unless exists
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def definition_exists?(existing_def, target_def)
|
|
44
|
+
existing_def[:name] == target_def[:name] && existing_def[:wday] == target_def[:wday] && existing_def[:mday] == target_def[:mday] && existing_def[:week] == target_def[:week] && existing_def[:function_id] == target_def[:function_id] && existing_def[:type] == target_def[:type] && existing_def[:observed_id] == target_def[:observed_id]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Repository
|
|
4
|
+
# ==== Benchmarks
|
|
5
|
+
#
|
|
6
|
+
# Lookup Easter Sunday, with caching, by number of iterations:
|
|
7
|
+
#
|
|
8
|
+
# user system total real
|
|
9
|
+
# 0001 0.000000 0.000000 0.000000 ( 0.000000)
|
|
10
|
+
# 0010 0.000000 0.000000 0.000000 ( 0.000000)
|
|
11
|
+
# 0100 0.078000 0.000000 0.078000 ( 0.078000)
|
|
12
|
+
# 1000 0.641000 0.000000 0.641000 ( 0.641000)
|
|
13
|
+
# 5000 3.172000 0.015000 3.187000 ( 3.219000)
|
|
14
|
+
#
|
|
15
|
+
# Lookup Easter Sunday, without caching, by number of iterations:
|
|
16
|
+
#
|
|
17
|
+
# user system total real
|
|
18
|
+
# 0001 0.000000 0.000000 0.000000 ( 0.000000)
|
|
19
|
+
# 0010 0.016000 0.000000 0.016000 ( 0.016000)
|
|
20
|
+
# 0100 0.125000 0.000000 0.125000 ( 0.125000)
|
|
21
|
+
# 1000 1.234000 0.000000 1.234000 ( 1.234000)
|
|
22
|
+
# 5000 6.094000 0.031000 6.125000 ( 6.141000)
|
|
23
|
+
class ProcCache
|
|
24
|
+
def initialize
|
|
25
|
+
@proc_cache = {}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def lookup(function, year)
|
|
29
|
+
proc_key = Digest::MD5.hexdigest("#{function.to_s}_#{year.to_s}")
|
|
30
|
+
@proc_cache[proc_key] = function.call(year) unless @proc_cache[proc_key]
|
|
31
|
+
@proc_cache[proc_key]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Repository
|
|
4
|
+
class Regions
|
|
5
|
+
def initialize
|
|
6
|
+
@regions = []
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def all
|
|
10
|
+
@regions
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def add(regions)
|
|
14
|
+
regions = [regions] unless regions.is_a?(Array)
|
|
15
|
+
|
|
16
|
+
regions.each do |region|
|
|
17
|
+
raise ArgumentError unless region.is_a?(Symbol)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
@regions = @regions | regions
|
|
21
|
+
@regions.uniq!
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def exists?(region)
|
|
25
|
+
raise ArgumentError unless region.is_a?(Symbol)
|
|
26
|
+
@regions.include?(region)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def search(prefix)
|
|
30
|
+
raise ArgumentError unless prefix.is_a?(String)
|
|
31
|
+
@regions.select { |region| region.to_s =~ Regexp.new("^#{prefix}") }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Definition
|
|
3
|
+
module Validator
|
|
4
|
+
class Region
|
|
5
|
+
def initialize(regions_repo)
|
|
6
|
+
@regions_repo = regions_repo
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def valid?(r)
|
|
10
|
+
return false unless r.is_a?(Symbol)
|
|
11
|
+
|
|
12
|
+
region = find_wildcard_base(r)
|
|
13
|
+
|
|
14
|
+
(region == :any ||
|
|
15
|
+
regions_repo.exists?(region) ||
|
|
16
|
+
region_in_static_definitions?(region))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
attr_reader :regions_repo
|
|
22
|
+
|
|
23
|
+
# Ex: :gb_ transformed to :gb
|
|
24
|
+
def find_wildcard_base(region)
|
|
25
|
+
r = region.to_s
|
|
26
|
+
|
|
27
|
+
if r =~ /_$/
|
|
28
|
+
base = r.split('_').first
|
|
29
|
+
else
|
|
30
|
+
base = r
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
base.to_sym
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def region_in_static_definitions?(region)
|
|
37
|
+
static_regions_definition = "#{DEFINITIONS_PATH}/REGIONS.rb"
|
|
38
|
+
require static_regions_definition
|
|
39
|
+
|
|
40
|
+
Holidays::REGIONS.include?(region)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'holidays/definition/context/generator'
|
|
2
|
+
require 'holidays/definition/context/merger'
|
|
3
|
+
require 'holidays/definition/repository/holidays_by_month'
|
|
4
|
+
require 'holidays/definition/repository/regions'
|
|
5
|
+
require 'holidays/definition/repository/cache'
|
|
6
|
+
require 'holidays/definition/repository/proc_cache'
|
|
7
|
+
require 'holidays/definition/validator/region'
|
|
8
|
+
|
|
9
|
+
module Holidays
|
|
10
|
+
module DefinitionFactory
|
|
11
|
+
class << self
|
|
12
|
+
def file_parser
|
|
13
|
+
Definition::Context::Generator.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def source_generator
|
|
17
|
+
Definition::Context::Generator.new
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def merger
|
|
21
|
+
Definition::Context::Merger.new(
|
|
22
|
+
holidays_by_month_repository,
|
|
23
|
+
regions_repository
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def region_validator
|
|
28
|
+
Definition::Validator::Region.new(
|
|
29
|
+
regions_repository
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def holidays_by_month_repository
|
|
34
|
+
@holidays_repo ||= Definition::Repository::HolidaysByMonth.new
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def regions_repository
|
|
38
|
+
@regions_repo ||= Definition::Repository::Regions.new
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cache_repository
|
|
42
|
+
@cache_repo ||= Definition::Repository::Cache.new
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def proc_cache_repository
|
|
46
|
+
@proc_cache_repo ||= Definition::Repository::ProcCache.new
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module Option
|
|
3
|
+
module Context
|
|
4
|
+
class ParseOptions
|
|
5
|
+
def initialize(regions_repo, region_validator)
|
|
6
|
+
@regions_repo = regions_repo
|
|
7
|
+
@region_validator = region_validator
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Returns [(arr)regions, (bool)observed, (bool)informal]
|
|
11
|
+
def call(*options)
|
|
12
|
+
options.flatten!
|
|
13
|
+
|
|
14
|
+
#TODO This is garbage. These two deletes MUST come before the
|
|
15
|
+
# parse_regions call, otherwise it thinks that :observed and :informal
|
|
16
|
+
# are regions to parse. We should be splitting these things out.
|
|
17
|
+
observed = options.delete(:observed) ? true : false
|
|
18
|
+
informal = options.delete(:informal) ? true : false
|
|
19
|
+
regions = parse_regions!(options)
|
|
20
|
+
|
|
21
|
+
return regions, observed, informal
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
attr_reader :regions_repo, :region_validator
|
|
27
|
+
|
|
28
|
+
# Check regions against list of supported regions and return an array of
|
|
29
|
+
# symbols.
|
|
30
|
+
#
|
|
31
|
+
# If a wildcard region is found (e.g. <tt>:ca_</tt>) it is expanded into all
|
|
32
|
+
# of its available sub regions.
|
|
33
|
+
def parse_regions!(regions)
|
|
34
|
+
regions = [regions] unless regions.kind_of?(Array)
|
|
35
|
+
return [:any] if regions.empty?
|
|
36
|
+
|
|
37
|
+
regions = regions.collect { |r| r.to_sym }
|
|
38
|
+
|
|
39
|
+
validate!(regions)
|
|
40
|
+
|
|
41
|
+
# Found sub region wild-card
|
|
42
|
+
regions.delete_if do |r|
|
|
43
|
+
if r.to_s =~ /_$/
|
|
44
|
+
load_containing_region(r.to_s)
|
|
45
|
+
regions << regions_repo.search(r.to_s)
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
regions.flatten!
|
|
51
|
+
|
|
52
|
+
require "#{DEFINITIONS_PATH}/north_america" if regions.include?(:us) # special case for north_america/US cross-linking
|
|
53
|
+
|
|
54
|
+
regions.each do |region|
|
|
55
|
+
unless region == :any or regions_repo.exists?(region)
|
|
56
|
+
region_definition = "#{DEFINITIONS_PATH}/#{region.to_s}"
|
|
57
|
+
begin
|
|
58
|
+
require region_definition #FIXME This is unacceptable, we can't be loading external files while parsing options
|
|
59
|
+
rescue LoadError => e
|
|
60
|
+
# This could be a sub region that does not have any holiday
|
|
61
|
+
# definitions of its own; try to load the containing region instead.
|
|
62
|
+
if region.to_s =~ /_/
|
|
63
|
+
load_containing_region(region.to_s)
|
|
64
|
+
else
|
|
65
|
+
raise UnknownRegionError, "Could not load #{region_definition}"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
regions
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def validate!(regions)
|
|
75
|
+
regions.each do |r|
|
|
76
|
+
raise UnknownRegionError unless region_validator.valid?(r)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Derive the containing region from a sub region wild-card or a sub region
|
|
81
|
+
# and load its definition. (Common code factored out from parse_regions)
|
|
82
|
+
def load_containing_region(sub_reg)
|
|
83
|
+
prefix = sub_reg.split('_').first
|
|
84
|
+
region_definition = "#{DEFINITIONS_PATH}/#{prefix}"
|
|
85
|
+
unless regions_repo.exists?(prefix.to_sym)
|
|
86
|
+
begin
|
|
87
|
+
require region_definition #FIXME This is not acceptable, we can't be loading external things while parsing options.
|
|
88
|
+
rescue LoadError
|
|
89
|
+
raise UnknownRegionError, "Could not load #{region_definition}"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'holidays/option/context/parse_options'
|
|
2
|
+
|
|
3
|
+
module Holidays
|
|
4
|
+
module OptionFactory
|
|
5
|
+
class << self
|
|
6
|
+
def parse_options
|
|
7
|
+
Option::Context::ParseOptions.new(
|
|
8
|
+
DefinitionFactory.regions_repository,
|
|
9
|
+
DefinitionFactory.region_validator,
|
|
10
|
+
)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
module Holidays
|
|
2
|
+
module UseCase
|
|
3
|
+
module Context
|
|
4
|
+
class Between
|
|
5
|
+
def initialize(holidays_by_month_repo, day_of_month_calculator, proc_cache_repo)
|
|
6
|
+
@holidays_by_month_repo = holidays_by_month_repo
|
|
7
|
+
@day_of_month_calculator = day_of_month_calculator
|
|
8
|
+
@proc_cache_repo = proc_cache_repo
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def call(start_date, end_date, dates_driver, regions, observed, informal)
|
|
12
|
+
validate!(start_date, end_date, dates_driver, regions)
|
|
13
|
+
|
|
14
|
+
holidays = []
|
|
15
|
+
|
|
16
|
+
dates_driver.each do |year, months|
|
|
17
|
+
months.each do |month|
|
|
18
|
+
next unless hbm = holidays_by_month_repo.find_by_month(month)
|
|
19
|
+
|
|
20
|
+
hbm.each do |h|
|
|
21
|
+
next unless in_region?(regions, h[:regions])
|
|
22
|
+
|
|
23
|
+
# Skip informal holidays unless they have been requested
|
|
24
|
+
next if h[:type] == :informal and not informal
|
|
25
|
+
|
|
26
|
+
if h[:function]
|
|
27
|
+
# Holiday definition requires a calculation
|
|
28
|
+
result = call_proc(h[:function], year)
|
|
29
|
+
|
|
30
|
+
# Procs may return either Date or an integer representing mday
|
|
31
|
+
if result.kind_of?(Date)
|
|
32
|
+
month = result.month
|
|
33
|
+
mday = result.mday
|
|
34
|
+
else
|
|
35
|
+
mday = result
|
|
36
|
+
end
|
|
37
|
+
else
|
|
38
|
+
# Calculate the mday
|
|
39
|
+
mday = h[:mday] || day_of_month_calculator.call(year, month, h[:week], h[:wday])
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Silently skip bad mdays
|
|
43
|
+
begin
|
|
44
|
+
date = Date.civil(year, month, mday)
|
|
45
|
+
rescue; next; end
|
|
46
|
+
|
|
47
|
+
# If the :observed option is set, calculate the date when the holiday
|
|
48
|
+
# is observed.
|
|
49
|
+
if observed and h[:observed]
|
|
50
|
+
date = call_proc(h[:observed], date)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
if date.between?(start_date, end_date)
|
|
54
|
+
holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
holidays.sort{|a, b| a[:date] <=> b[:date] }
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
attr_reader :holidays_by_month_repo, :day_of_month_calculator, :proc_cache_repo
|
|
66
|
+
|
|
67
|
+
def validate!(start_date, end_date, dates_driver, regions)
|
|
68
|
+
raise ArgumentError unless start_date
|
|
69
|
+
raise ArgumentError unless end_date
|
|
70
|
+
|
|
71
|
+
raise ArgumentError if dates_driver.nil? || dates_driver.empty?
|
|
72
|
+
|
|
73
|
+
dates_driver.each do |year, months|
|
|
74
|
+
raise ArgumentError if months.nil? || months.empty?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
raise ArgumentError if regions.nil? || regions.empty?
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def call_proc(function, year)
|
|
81
|
+
proc_cache_repo.lookup(function, year)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Check sub regions.
|
|
85
|
+
#
|
|
86
|
+
# When request :any, all holidays should be returned.
|
|
87
|
+
# When requesting :ca_bc, holidays in :ca or :ca_bc should be returned.
|
|
88
|
+
# When requesting :ca, holidays in :ca but not its subregions should be returned.
|
|
89
|
+
def in_region?(requested, available) # :nodoc:
|
|
90
|
+
return true if requested.include?(:any)
|
|
91
|
+
|
|
92
|
+
# When an underscore is encountered, derive the parent regions
|
|
93
|
+
# symbol and include both in the requested array.
|
|
94
|
+
requested = requested.collect do |r|
|
|
95
|
+
r.to_s =~ /_/ ? [r, r.to_s.gsub(/_[\w]*$/, '').to_sym] : r
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
requested = requested.flatten.uniq
|
|
99
|
+
|
|
100
|
+
available.any? { |avail| requested.include?(avail) }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|