holidays 7.0.0 → 8.3.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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/README.md +5 -6
  4. data/doc/CONTRIBUTING.md +2 -1
  5. data/holidays.gemspec +5 -5
  6. data/lib/generated_definitions/MANIFEST +3 -0
  7. data/lib/generated_definitions/REGIONS.rb +2 -2
  8. data/lib/generated_definitions/au.rb +1 -1
  9. data/lib/generated_definitions/ca.rb +14 -12
  10. data/lib/generated_definitions/ch.rb +13 -1
  11. data/lib/generated_definitions/cl.rb +5 -5
  12. data/lib/generated_definitions/de.rb +6 -4
  13. data/lib/generated_definitions/es.rb +4 -2
  14. data/lib/generated_definitions/europe.rb +83 -22
  15. data/lib/generated_definitions/federalreservebanks.rb +1 -1
  16. data/lib/generated_definitions/gb.rb +6 -4
  17. data/lib/generated_definitions/hr.rb +8 -6
  18. data/lib/generated_definitions/hu.rb +3 -2
  19. data/lib/generated_definitions/it.rb +14 -5
  20. data/lib/generated_definitions/jp.rb +24 -20
  21. data/lib/generated_definitions/lu.rb +2 -0
  22. data/lib/generated_definitions/lv.rb +52 -0
  23. data/lib/generated_definitions/mx.rb +3 -3
  24. data/lib/generated_definitions/ng.rb +33 -0
  25. data/lib/generated_definitions/northamerica.rb +21 -20
  26. data/lib/generated_definitions/ro.rb +5 -2
  27. data/lib/generated_definitions/southamerica.rb +5 -5
  28. data/lib/generated_definitions/th.rb +36 -0
  29. data/lib/generated_definitions/tr.rb +5 -3
  30. data/lib/generated_definitions/ua.rb +6 -6
  31. data/lib/generated_definitions/us.rb +5 -6
  32. data/lib/holidays.rb +2 -0
  33. data/lib/holidays/definition/context/generator.rb +20 -34
  34. data/lib/holidays/definition/repository/holidays_by_month.rb +9 -1
  35. data/lib/holidays/finder/context/search.rb +34 -31
  36. data/lib/holidays/finder/rules/year_range.rb +30 -54
  37. data/lib/holidays/version.rb +1 -1
  38. data/test/coverage_report.rb +23 -5
  39. data/test/data/test_custom_year_range_holiday_defs.yaml +6 -10
  40. data/test/data/test_multiple_regions_with_conflicts_region_1.yaml +38 -0
  41. data/test/data/test_multiple_regions_with_conflicts_region_2.yaml +38 -0
  42. data/test/defs/test_defs_ca.rb +31 -1
  43. data/test/defs/test_defs_ch.rb +4 -0
  44. data/test/defs/test_defs_co.rb +3 -3
  45. data/test/defs/test_defs_de.rb +6 -0
  46. data/test/defs/test_defs_es.rb +2 -0
  47. data/test/defs/test_defs_europe.rb +160 -11
  48. data/test/defs/test_defs_federalreservebanks.rb +137 -3
  49. data/test/defs/test_defs_gb.rb +16 -0
  50. data/test/defs/test_defs_hr.rb +6 -6
  51. data/test/defs/test_defs_hu.rb +12 -4
  52. data/test/defs/test_defs_it.rb +20 -0
  53. data/test/defs/test_defs_lu.rb +6 -0
  54. data/test/defs/test_defs_lv.rb +90 -0
  55. data/test/defs/test_defs_ng.rb +29 -0
  56. data/test/defs/test_defs_northamerica.rb +37 -11
  57. data/test/defs/test_defs_ro.rb +14 -0
  58. data/test/defs/test_defs_southamerica.rb +3 -3
  59. data/test/defs/test_defs_th.rb +33 -0
  60. data/test/defs/test_defs_tr.rb +7 -0
  61. data/test/defs/test_defs_us.rb +6 -10
  62. data/test/holidays/core_extensions/test_date.rb +3 -2
  63. data/test/holidays/definition/context/test_generator.rb +5 -18
  64. data/test/holidays/definition/repository/test_holidays_by_month.rb +121 -1
  65. data/test/holidays/finder/rules/test_year_range.rb +43 -47
  66. data/test/integration/test_available_regions.rb +1 -1
  67. data/test/integration/test_custom_year_range_holidays.rb +0 -7
  68. data/test/integration/test_holidays.rb +1 -23
  69. data/test/integration/test_holidays_between.rb +10 -0
  70. data/test/integration/test_multiple_regions_with_conflict.rb +29 -0
  71. data/test/integration/test_nonstandard_regions.rb +1 -1
  72. metadata +27 -13
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module Holidays
3
+ # This file is generated by the Ruby Holidays gem.
4
+ #
5
+ # Definitions loaded: definitions/th.yaml
6
+ #
7
+ # All the definitions are available at https://github.com/holidays/holidays
8
+ module TH # :nodoc:
9
+ def self.defined_regions
10
+ [:th]
11
+ end
12
+
13
+ def self.holidays_by_month
14
+ {
15
+ 1 => [{:mday => 1, :name => "วันขึ้นปีใหม่", :regions => [:th]}],
16
+ 4 => [{:mday => 6, :name => "วันจักรี", :regions => [:th]},
17
+ {:mday => 13, :name => "วันสงกรานต์", :regions => [:th]},
18
+ {:mday => 14, :name => "วันสงกรานต์", :regions => [:th]},
19
+ {:mday => 15, :name => "วันสงกรานต์", :regions => [:th]}],
20
+ 7 => [{:mday => 28, :name => "วันเฉลิมพระชนมพรรษาสมเด็จพระเจ้าอยู่หัวมหาวชิราลงกรณ บดินทรเทพยวรางกูร", :regions => [:th]}],
21
+ 8 => [{:mday => 12, :name => "วันเฉลิมพระชนมพรรษาสมเด็จพระนางเจ้าสิริกิติ์ พระบรมราชินีนาถในรัชกาลที่ ๙", :regions => [:th]}],
22
+ 10 => [{:mday => 13, :name => "วันคล้ายวันสวรรคตพระบาทสมเด็จพระปรมินทรมหาภูมิพลอดุลยเดช บรมนาถบพิตร", :regions => [:th]},
23
+ {:mday => 23, :name => "วันปิยมหาราช", :regions => [:th]}],
24
+ 12 => [{:mday => 5, :name => "วันคล้ายวันเฉลิมพระชนมพรรษาพระบาทสมเด็จพระปรมินทรมหาภูมิพลอดุลยเดช บรมนาถบพิตร", :regions => [:th]},
25
+ {:mday => 10, :name => "วันรัฐธรรมนูญ", :regions => [:th]},
26
+ {:mday => 31, :name => "วันสิ้นปี", :regions => [:th]}]
27
+ }
28
+ end
29
+
30
+ def self.custom_methods
31
+ {
32
+
33
+ }
34
+ end
35
+ end
36
+ end
@@ -23,7 +23,7 @@ module Holidays
23
23
  4 => [{:mday => 23, :name => "Ulusal Egemenlik ve Çocuk Bayramı", :regions => [:tr]}],
24
24
  5 => [{:mday => 1, :name => "Emek ve Dayanışma Günü", :regions => [:tr]},
25
25
  {:mday => 19, :name => "Atatürk'ü Anma Gençlik ve Spor Bayramı", :regions => [:tr]}],
26
- 7 => [{:mday => 15, :year_ranges => [{:after => 2016}],:name => "Demokrasi ve Milli Birlik Günü", :regions => [:tr]}],
26
+ 7 => [{:mday => 15, :year_ranges => { :from => 2016 },:name => "Demokrasi ve Milli Birlik Günü", :regions => [:tr]}],
27
27
  8 => [{:mday => 30, :name => "Zafer Bayramı", :regions => [:tr]}],
28
28
  10 => [{:mday => 29, :name => "Cumhuriyet Bayramı", :regions => [:tr]}]
29
29
  }
@@ -38,7 +38,8 @@ begin_of_ramadan_feast = {
38
38
  '2016' => Date.civil(2016, 7, 5),
39
39
  '2017' => Date.civil(2017, 6, 25),
40
40
  '2018' => Date.civil(2018, 6, 15),
41
- '2019' => Date.civil(2019, 6, 4)
41
+ '2019' => Date.civil(2019, 6, 4),
42
+ '2020' => Date.civil(2020, 5, 24)
42
43
  }
43
44
  begin_of_ramadan_feast[year.to_s]
44
45
  },
@@ -50,7 +51,8 @@ begin_of_sacrifice_feast = {
50
51
  '2016' => Date.civil(2016, 9, 12),
51
52
  '2017' => Date.civil(2017, 9, 1),
52
53
  '2018' => Date.civil(2018, 8, 21),
53
- '2019' => Date.civil(2019, 8, 11)
54
+ '2019' => Date.civil(2019, 8, 11),
55
+ '2020' => Date.civil(2020, 7, 31)
54
56
  }
55
57
  begin_of_sacrifice_feast[year.to_s]
56
58
  },
@@ -18,13 +18,13 @@ module Holidays
18
18
  {:mday => 7, :observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "Різдво Христове", :regions => [:ua]}],
19
19
  3 => [{:mday => 8, :observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "Міжнародний жіночий день", :regions => [:ua]}],
20
20
  5 => [{:mday => 1, :observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День праці", :regions => [:ua]},
21
- {:mday => 2, :year_ranges => [{:before => 2017}],:name => "День міжнародної солідарності трудящих", :regions => [:ua]},
21
+ {:mday => 2, :year_ranges => { :until => 2017 },:name => "День міжнародної солідарності трудящих", :regions => [:ua]},
22
22
  {:mday => 9, :observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День перемоги над нацизмом у Другій світовій війні", :regions => [:ua]}],
23
- 6 => [{:mday => 28, :year_ranges => [{:after => 1997}],:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День Конституції", :regions => [:ua]}],
24
- 7 => [{:mday => 16, :year_ranges => [{:limited => [1991]}],:name => "День Незалежності України", :regions => [:ua]}],
25
- 8 => [{:mday => 24, :year_ranges => [{:after => 1992}],:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День Незалежності", :regions => [:ua]}],
26
- 10 => [{:mday => 14, :year_ranges => [{:after => 2015}],:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День захисника України", :regions => [:ua]}],
27
- 12 => [{:mday => 25, :year_ranges => [{:after => 2017}],:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "Різдво Христове", :regions => [:ua]}]
23
+ 6 => [{:mday => 28, :year_ranges => { :from => 1997 },:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День Конституції", :regions => [:ua]}],
24
+ 7 => [{:mday => 16, :year_ranges => { :limited => [1991] },:name => "День Незалежності України", :regions => [:ua]}],
25
+ 8 => [{:mday => 24, :year_ranges => { :from => 1992 },:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День Незалежності", :regions => [:ua]}],
26
+ 10 => [{:mday => 14, :year_ranges => { :from => 2015 },:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "День захисника України", :regions => [:ua]}],
27
+ 12 => [{:mday => 25, :year_ranges => { :from => 2017 },:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "Різдво Христове", :regions => [:ua]}]
28
28
  }
29
29
  end
30
30
 
@@ -7,7 +7,7 @@ module Holidays
7
7
  # All the definitions are available at https://github.com/holidays/holidays
8
8
  module US # :nodoc:
9
9
  def self.defined_regions
10
- [:us_fl, :us_la, :us, :us_ct, :us_de, :us_gu, :us_hi, :us_in, :us_ky, :us_nj, :us_nc, :us_nd, :us_pr, :us_tn, :us_ms, :us_id, :us_ar, :us_tx, :us_dc, :us_md, :us_va, :us_il, :us_vt, :us_ak, :us_ca, :us_me, :us_ma, :us_al, :us_ga, :us_ne, :us_mo, :us_sc, :us_wv, :us_vi, :us_ut, :us_ri, :us_az, :us_co, :us_mt, :us_nm, :us_ny, :us_oh, :us_pa, :us_mi, :us_mn, :us_nv, :us_or, :us_sd, :us_wa, :us_wi, :us_wy, :us_ia, :us_ks, :us_nh, :us_ok, :ca]
10
+ [:us_fl, :us_la, :us, :us_ct, :us_de, :us_gu, :us_hi, :us_in, :us_ky, :us_nj, :us_nc, :us_nd, :us_pr, :us_tn, :us_ms, :us_id, :us_ar, :us_tx, :us_dc, :us_md, :us_va, :us_vt, :us_ak, :us_ca, :us_me, :us_ma, :us_al, :us_ga, :us_ne, :us_mo, :us_sc, :us_wv, :us_vi, :us_ut, :us_ri, :us_az, :us_co, :us_il, :us_mt, :us_nm, :us_ny, :us_oh, :us_pa, :us_mi, :us_mn, :us_nv, :us_or, :us_sd, :us_wa, :us_wi, :us_wy, :us_ia, :us_ks, :us_nh, :us_ok, :ca]
11
11
  end
12
12
 
13
13
  def self.holidays_by_month
@@ -28,14 +28,13 @@ module Holidays
28
28
  2 => [{:wday => 1, :week => 3, :name => "Presidents' Day", :regions => [:us]},
29
29
  {:mday => 2, :type => :informal, :name => "Groundhog Day", :regions => [:us, :ca]},
30
30
  {:mday => 14, :type => :informal, :name => "Valentine's Day", :regions => [:us, :ca]}],
31
- 3 => [{:wday => 1, :week => 1, :name => "Casimir Pulaski Day", :regions => [:us_il]},
32
- {:wday => 2, :week => 1, :name => "Town Meeting Day", :regions => [:us_vt]},
31
+ 3 => [{:wday => 2, :week => 1, :name => "Town Meeting Day", :regions => [:us_vt]},
33
32
  {:mday => 2, :name => "Texas Independence Day", :regions => [:us_tx]},
34
33
  {:mday => 26, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Prince Jonah Kuhio Kalanianaole Day", :regions => [:us_hi]},
35
34
  {:wday => 1, :week => -1, :name => "Seward's Day", :regions => [:us_ak]},
36
35
  {:mday => 31, :name => "César Chávez Day", :regions => [:us_ca]},
37
36
  {:mday => 17, :type => :informal, :name => "St. Patrick's Day", :regions => [:us, :ca]}],
38
- 4 => [{:mday => 16, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Emancipation Day", :regions => [:us_dc, :us_ca]},
37
+ 4 => [{:mday => 16, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Emancipation Day", :regions => [:us_dc]},
39
38
  {:wday => 1, :week => 3, :name => "Patriots' Day", :regions => [:us_me, :us_ma]},
40
39
  {:mday => 21, :name => "San Jacinto Day", :regions => [:us_tx]},
41
40
  {:wday => 1, :week => -1, :name => "Confederate Memorial Day", :regions => [:us_al, :us_ms]},
@@ -71,7 +70,7 @@ module Holidays
71
70
  {:mday => 18, :name => "Alaska Day", :regions => [:us_ak]},
72
71
  {:wday => 5, :week => -1, :name => "Nevada Day", :regions => [:us_nv]},
73
72
  {:mday => 31, :type => :informal, :name => "Halloween", :regions => [:us, :ca]}],
74
- 11 => [{:function => "election_day(year)", :function_arguments => [:year], :name => "Election Day", :regions => [:us_de, :us_hi, :us_il, :us_in, :us_mt, :us_nj, :us_ny, :us_pa, :us_ri]},
73
+ 11 => [{:function => "election_day(year)", :function_arguments => [:year], :name => "Election Day", :regions => [:us_de, :us_hi, :us_in, :us_mt, :us_nj, :us_ny, :us_pa, :us_ri]},
75
74
  {:mday => 11, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Veterans Day", :regions => [:us]},
76
75
  {:wday => 4, :week => 4, :name => "Thanksgiving", :regions => [:us]},
77
76
  {:function => "day_after_thanksgiving(year)", :function_arguments => [:year], :name => "Family Day", :regions => [:us_nv]},
@@ -82,7 +81,7 @@ module Holidays
82
81
  {:function => "day_after_thanksgiving(year)", :function_arguments => [:year], :name => "Day after Thanksgiving (Black Friday)", :regions => [:us_ca, :us_de, :us_fl, :us_ia, :us_il, :us_ks, :us_ky, :us_me, :us_mi, :us_mn, :us_ms, :us_ne, :us_nh, :us_nc, :us_pa, :us_sc, :us_ok, :us_tn, :us_tx, :us_va, :us_wa, :us_wv]}],
83
82
  12 => [{:mday => 24, :name => "Christmas Eve", :regions => [:us_ar, :us_mi, :us_nc, :us_sc, :us_tx, :us_wi]},
84
83
  {:mday => 24, :function => "christmas_eve_holiday(date)", :function_arguments => [:date], :name => "Christmas Eve (Holiday)", :regions => [:us_mi, :us_sc, :us_va]},
85
- {:mday => 25, :observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "Christmas Day", :regions => [:us]},
84
+ {:mday => 25, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Christmas Day", :regions => [:us]},
86
85
  {:mday => 26, :name => "Day after Christmas", :regions => [:us_ar, :us_nc, :us_ok, :us_sc, :us_tn, :us_tx]},
87
86
  {:mday => 31, :name => "New Year's Eve", :regions => [:us_mi, :us_wi]}]
88
87
  }
@@ -40,6 +40,8 @@ module Holidays
40
40
 
41
41
  start_date, end_date = get_date(start_date), get_date(end_date)
42
42
 
43
+ raise ArgumentError if end_date < start_date
44
+
43
45
  if cached_holidays = Factory::Definition.cache_repository.find(start_date, end_date, options)
44
46
  return cached_holidays
45
47
  end
@@ -87,16 +87,27 @@ module Holidays
87
87
  rule = {}
88
88
 
89
89
  definition.each do |key, val|
90
+ # Ruby 2.4 doesn't have the `transform_keys` method. Once we drop 2.4 support we can
91
+ # use `val.transform_keys!(&:to_sym) if val.is_a?(Hash)` instead of this `if` statement.
92
+ if val.is_a?(Hash)
93
+ val = val.keys.each_with_object({}) do |k, result|
94
+ result[k.to_sym] = val[k]
95
+ end
96
+ end
97
+
90
98
  rule[key.to_sym] = val
91
99
  end
92
100
 
93
- rule[:regions] = rule[:regions].collect { |r| r.to_sym }
94
- regions << rule[:regions]
101
+ if rule[:year_ranges] && rule[:year_ranges].key?(:between)
102
+ start_year = rule[:year_ranges][:between]["start"].to_i
103
+ end_year = rule[:year_ranges][:between]["end"].to_i
95
104
 
96
- if rule[:year_ranges]
97
- rule[:year_ranges] = clean_year_ranges(rule[:year_ranges])
105
+ rule[:year_ranges][:between] = Range.new(start_year, end_year)
98
106
  end
99
107
 
108
+ rule[:regions] = rule[:regions].collect { |r| r.to_sym }
109
+ regions << rule[:regions]
110
+
100
111
  exists = false
101
112
  rules_by_month[month].each do |ex|
102
113
  if ex[:name] == rule[:name] and ex[:wday] == rule[:wday] and ex[:mday] == rule[:mday] and ex[:week] == rule[:week] and ex[:type] == rule[:type] and ex[:function] == rule[:function] and ex[:observed] == rule[:observed] and ex[:year_ranges] == rule[:year_ranges]
@@ -121,23 +132,6 @@ module Holidays
121
132
  [regions, rules_by_month]
122
133
  end
123
134
 
124
- # In this case we end up parsing a range as "2006..2008" a string. This is codifying
125
- # what we already do...today we parse as a string but when writing out to our final
126
- # generated files it comes out as a range that Ruby interprets. This just puts it in stone
127
- # what we want to do.
128
- def clean_year_ranges(year_ranges)
129
- year_ranges.collect do |year_range|
130
- if year_range["between"]
131
- range = year_range["between"]
132
- if range.is_a?(String)
133
- year_range["between"] = Range.new(*range.split("..").map(&:to_i))
134
- end
135
- end
136
-
137
- year_range
138
- end
139
- end
140
-
141
135
  #FIXME This should really be split out and tested with its own unit tests.
142
136
  def generate_month_definition_strings(rules_by_month, parsed_custom_methods)
143
137
  month_strings = []
@@ -170,19 +164,11 @@ module Holidays
170
164
  string << ":wday => #{rule[:wday]}, :week => #{rule[:week]}, "
171
165
  end
172
166
 
173
- #FIXME I think this should be split out into its own file.
174
- if rule[:year_ranges] && rule[:year_ranges].kind_of?(Array)
175
- year_string = " :year_ranges => ["
176
- len = rule[:year_ranges].length
177
- rule[:year_ranges].each_with_index do |year,index|
178
- year_string << "{:#{year.keys.first} => #{year.values.first}}"
179
- if len == index + 1
180
- year_string << "],"
181
- else
182
- year_string << ","
183
- end
184
- end
185
- string << year_string
167
+ if rule[:year_ranges] && rule[:year_ranges].is_a?(Hash)
168
+ selector = rule[:year_ranges].keys.first
169
+ value = rule[:year_ranges][selector]
170
+
171
+ string << ":year_ranges => { :#{selector} => #{value} },"
186
172
  end
187
173
 
188
174
  if rule[:observed]
@@ -41,7 +41,15 @@ module Holidays
41
41
  private
42
42
 
43
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] == target_def[:function] && existing_def[:type] == target_def[:type] && existing_def[:observed] == target_def[:observed] && existing_def[:year_ranges] == target_def[:year_ranges]
44
+ existing_def[:name] == target_def[:name] &&
45
+ existing_def[:wday] == target_def[:wday] &&
46
+ existing_def[:mday] == target_def[:mday] &&
47
+ existing_def[:week] == target_def[:week] &&
48
+ existing_def[:function] == target_def[:function] &&
49
+ existing_def[:function_modifier] == target_def[:function_modifier] &&
50
+ existing_def[:type] == target_def[:type] &&
51
+ existing_def[:observed] == target_def[:observed] &&
52
+ existing_def[:year_ranges] == target_def[:year_ranges]
45
53
  end
46
54
  end
47
55
  end
@@ -24,39 +24,11 @@ module Holidays
24
24
  next unless @rules[:year_range].call(year, h[:year_ranges])
25
25
  end
26
26
 
27
- current_month = month
28
- current_day = h[:mday]
29
-
30
- if h[:function]
31
- result = @custom_method_processor.call(
32
- build_custom_method_input(year, current_month, current_day, h[:regions]),
33
- h[:function], h[:function_arguments], h[:function_modifier],
34
- )
35
-
36
- #FIXME The result should always be present, see https://github.com/holidays/holidays/issues/204 for more information
37
- if result
38
- current_month = result.month
39
- current_day = result.mday
40
- else
41
- current_month = nil
42
- current_day = nil
43
- end
44
- else
45
- current_day = h[:mday] || @day_of_month_calculator.call(year, current_month, h[:week], h[:wday])
46
- end
47
-
48
- # Silently skip bad mdays
49
- #TODO Should we be doing something different here? We have no concept of logging right now. Maybe we should add it?
50
- begin
51
- date = Date.civil(year, current_month, current_day)
52
- rescue; next; end
27
+ date = build_date(year, month, h)
28
+ next unless date
53
29
 
54
30
  if observed_set?(options) && h[:observed]
55
- date = @custom_method_processor.call(
56
- build_custom_method_input(date.year, date.month, date.day, regions),
57
- h[:observed],
58
- [:date],
59
- )
31
+ date = build_observed_date(date, regions, h)
60
32
  end
61
33
 
62
34
  holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
@@ -93,6 +65,29 @@ module Holidays
93
65
  options && options.include?(:observed) == true
94
66
  end
95
67
 
68
+ def build_date(year, month, h)
69
+ if h[:function]
70
+ holiday = custom_holiday(year, month, h)
71
+ #FIXME The result should always be present, see https://github.com/holidays/holidays/issues/204 for more information
72
+ current_month = holiday&.month
73
+ current_day = holiday&.mday
74
+ else
75
+ current_month = month
76
+ current_day = h[:mday] || @day_of_month_calculator.call(year, month, h[:week], h[:wday])
77
+ end
78
+
79
+ # Silently skip bad mdays
80
+ #TODO Should we be doing something different here? We have no concept of logging right now. Maybe we should add it?
81
+ Date.civil(year, current_month, current_day) rescue nil
82
+ end
83
+
84
+ def custom_holiday(year, month, h)
85
+ @custom_method_processor.call(
86
+ build_custom_method_input(year, month, h[:mday], h[:regions]),
87
+ h[:function], h[:function_arguments], h[:function_modifier],
88
+ )
89
+ end
90
+
96
91
  def build_custom_method_input(year, month, day, regions)
97
92
  {
98
93
  year: year,
@@ -101,6 +96,14 @@ module Holidays
101
96
  region: regions.first, #FIXME This isn't ideal but will work for our current use case...
102
97
  }
103
98
  end
99
+
100
+ def build_observed_date(date, regions, h)
101
+ @custom_method_processor.call(
102
+ build_custom_method_input(date.year, date.month, date.day, regions),
103
+ h[:observed],
104
+ [:date],
105
+ )
106
+ end
104
107
  end
105
108
  end
106
109
  end
@@ -1,49 +1,30 @@
1
- # Please note that only one condition needs to match in order for `call` to return `true.
2
- # See the test file for this class for specific examples.
3
1
  module Holidays
4
2
  module Finder
5
3
  module Rules
6
4
  class YearRange
7
5
  class << self
8
- BEFORE = :before
9
- AFTER = :after
6
+ UNTIL = :until
7
+ FROM = :from
10
8
  LIMITED = :limited
11
9
  BETWEEN = :between
12
10
 
13
- #TODO Can we just accept symbols here? Why accept strings?
14
- VALID_OPERATORS = [
15
- BEFORE, BEFORE.to_s,
16
- AFTER, AFTER.to_s,
17
- LIMITED, LIMITED.to_s,
18
- BETWEEN, BETWEEN.to_s
19
- ]
20
-
21
- def call(target_year, year_range_definitions)
22
- validate!(target_year, year_range_definitions)
23
-
24
- matched = false
25
- year_range_definitions.each do |range_defs|
26
- next unless range_defs.is_a?(Hash) && range_defs.length == 1
27
-
28
- operator = range_defs.keys.first
29
- year_range = range_defs.values.first
30
-
31
- case operator
32
- when BEFORE, BEFORE.to_s
33
- matched = target_year <= year_range
34
- when AFTER, AFTER.to_s
35
- matched = target_year >= year_range
36
- when LIMITED, LIMITED.to_s
37
- if year_range.is_a?(Array)
38
- matched = year_range.include?(target_year)
39
- else
40
- matched = year_range == target_year
41
- end
42
- when BETWEEN, BETWEEN.to_s
43
- matched = year_range.cover?(target_year)
44
- end
45
-
46
- break if matched == true
11
+ def call(target_year, year_range_defs)
12
+ validate!(target_year, year_range_defs)
13
+
14
+ operator = year_range_defs.keys.first
15
+ rule_value = year_range_defs[operator]
16
+
17
+ case operator
18
+ when UNTIL
19
+ matched = target_year <= rule_value
20
+ when FROM
21
+ matched = target_year >= rule_value
22
+ when LIMITED
23
+ matched = rule_value.include?(target_year)
24
+ when BETWEEN
25
+ matched = rule_value.cover?(target_year)
26
+ else
27
+ matched = false
47
28
  end
48
29
 
49
30
  matched
@@ -54,25 +35,20 @@ module Holidays
54
35
  def validate!(target_year, year_ranges)
55
36
  raise ArgumentError.new("target_year must be a number") unless target_year.is_a?(Integer)
56
37
  raise ArgumentError.new("year_ranges cannot be missing") if year_ranges.nil? || year_ranges.empty?
38
+ raise ArgumentError.new("year_ranges must contain a hash with a single operator") unless year_ranges.is_a?(Hash) && year_ranges.size == 1
57
39
 
58
- year_ranges.each do |range|
59
- raise ArgumentError.new("year_ranges must include only hashes") unless range.is_a?(Hash)
60
- raise ArgumentError.new("year_ranges cannot include empty hashes") if range.empty?
61
- raise ArgumentError.new("year_ranges entries can only include one operator") unless range.count == 1
62
-
63
- operator = range.keys.first
64
- range = range.values.first
40
+ operator = year_ranges.keys.first
41
+ value = year_ranges[operator]
65
42
 
66
- raise ArgumentError.new("Invalid operator found: '#{operator}'") unless VALID_OPERATORS.include?(operator)
43
+ raise ArgumentError.new("Invalid operator found: '#{operator}'") unless [UNTIL, FROM, LIMITED, BETWEEN].include?(operator)
67
44
 
68
- case operator
69
- when BEFORE, BEFORE.to_s, AFTER, AFTER.to_s
70
- raise ArgumentError.new(":before and :after operator value must be a number, received: '#{range}'") unless range.is_a?(Integer)
71
- when LIMITED, LIMITED.to_s
72
- raise ArgumentError.new(":limited operator value must be an array, received: '#{range}'") unless range.is_a?(Array) || range.is_a?(Integer)
73
- when BETWEEN, BETWEEN.to_s
74
- raise ArgumentError.new(":between operator value must be a range, received: '#{range}'") unless range.is_a?(Range)
75
- end
45
+ case operator
46
+ when UNTIL, FROM
47
+ raise ArgumentError.new("#{UNTIL} and #{FROM} operator value must be a number, received: '#{value}'") unless value.is_a?(Integer)
48
+ when LIMITED
49
+ raise ArgumentError.new(":limited operator value must be an array containing at least one integer value, received: '#{value}'") unless value.is_a?(Array) && value.size >= 1 && value.all? { |v| v.is_a?(Integer) }
50
+ when BETWEEN
51
+ raise ArgumentError.new(":between operator value must be a range, received: '#{value}'") unless value.is_a?(Range)
76
52
  end
77
53
  end
78
54
  end
@@ -1,3 +1,3 @@
1
1
  module Holidays
2
- VERSION = '7.0.0'
2
+ VERSION = '8.3.0'
3
3
  end
@@ -1,8 +1,26 @@
1
1
  require 'simplecov'
2
2
 
3
- SimpleCov.minimum_coverage 99
3
+ # For reasons I don't understand jruby implementations report lower coverage
4
+ # than other ruby versions. Ruby 2.5.3, for instance, is at 92%.
5
+ #
6
+ # We set the floor based on jruby so that all automated tests pass on Travis CI.
7
+ SimpleCov.minimum_coverage 89
8
+
9
+ SimpleCov.add_filter [
10
+ # Apparently simplecov doesn't automatically filter 'spec' or 'test' so we
11
+ # have to do it manually.
12
+ 'test',
13
+
14
+ # Only filtered because I tend to not see value in testing factories.
15
+ 'lib/holidays/factory/',
16
+
17
+ # jruby coverage flips out here and doesn't count much of the large date
18
+ # arrays used by this class. This results in an extremely low reported
19
+ # coverage for this specific file but only in jruby, not other ruby versions.
20
+ # Since it obliterates coverage percentages I'll filter it until I can come
21
+ # up with a solution.
22
+ 'lib/holidays/date_calculator/lunar_date.rb',
23
+ ]
24
+
4
25
  SimpleCov.coverage_dir 'reports/coverage'
5
- SimpleCov.start do
6
- add_filter 'lib/generated_definitions/'
7
- add_filter 'lib/holidays/factory/'
8
- end
26
+ SimpleCov.start