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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +22 -8
  4. data/Rakefile +26 -8
  5. data/bin/console +7 -0
  6. data/bin/setup +5 -0
  7. data/{data → definitions}/SYNTAX.rdoc +0 -0
  8. data/{data → definitions}/ar.yaml +0 -0
  9. data/{data → definitions}/at.yaml +1 -1
  10. data/{data → definitions}/au.yaml +80 -51
  11. data/{data → definitions}/be.yaml +0 -0
  12. data/{data → definitions}/br.yaml +0 -0
  13. data/{data → definitions}/ca.yaml +0 -0
  14. data/{data → definitions}/ch.yaml +0 -0
  15. data/{data → definitions}/cl.yaml +0 -0
  16. data/{data → definitions}/cr.yaml +0 -0
  17. data/{data → definitions}/cz.yaml +0 -0
  18. data/{data → definitions}/de.yaml +25 -34
  19. data/{data → definitions}/dk.yaml +0 -0
  20. data/{data → definitions}/ecb_target.yaml +2 -2
  21. data/{data → definitions}/el.yaml +0 -0
  22. data/{data → definitions}/es.yaml +37 -26
  23. data/{data → definitions}/federal_reserve.yaml +0 -0
  24. data/{data → definitions}/fedex.yaml +9 -3
  25. data/{data → definitions}/fi.yaml +0 -0
  26. data/{data → definitions}/fr.yaml +0 -0
  27. data/{data → definitions}/gb.yaml +19 -19
  28. data/{data → definitions}/hr.yaml +0 -0
  29. data/{data → definitions}/hu.yaml +1 -1
  30. data/{data → definitions}/ie.yaml +0 -0
  31. data/{data → definitions}/index.yaml +0 -0
  32. data/{data → definitions}/is.yaml +0 -0
  33. data/{data → definitions}/it.yaml +0 -0
  34. data/{data → definitions}/jp.yaml +3 -3
  35. data/{data → definitions}/li.yaml +3 -3
  36. data/{data → definitions}/lt.yaml +0 -0
  37. data/{data → definitions}/ma.yaml +0 -0
  38. data/{data → definitions}/mx.yaml +0 -0
  39. data/{data → definitions}/nerc.yaml +0 -0
  40. data/{data → definitions}/nl.yaml +0 -0
  41. data/{data → definitions}/no.yaml +0 -0
  42. data/{data → definitions}/north_america_informal.yaml +0 -0
  43. data/{data → definitions}/nyse.yaml +0 -0
  44. data/{data → definitions}/nz.yaml +21 -10
  45. data/{data → definitions}/ph.yaml +0 -0
  46. data/{data → definitions}/pl.yaml +0 -0
  47. data/{data → definitions}/pt.yaml +0 -0
  48. data/{data → definitions}/ro.yaml +1 -2
  49. data/{data → definitions}/se.yaml +0 -0
  50. data/{data → definitions}/sg.yaml +0 -0
  51. data/{data → definitions}/si.yaml +6 -6
  52. data/{data → definitions}/sk.yaml +0 -0
  53. data/{data → definitions}/united_nations.yaml +0 -0
  54. data/{data → definitions}/ups.yaml +17 -11
  55. data/{data → definitions}/us.yaml +20 -15
  56. data/{data → definitions}/ve.yaml +0 -0
  57. data/{data → definitions}/vi.yaml +0 -0
  58. data/{data → definitions}/za.yaml +0 -0
  59. data/holidays.gemspec +2 -1
  60. data/lib/generated_definitions/MANIFEST +56 -0
  61. data/lib/generated_definitions/REGIONS.rb +4 -0
  62. data/lib/{holidays → generated_definitions}/ar.rb +2 -2
  63. data/lib/{holidays → generated_definitions}/at.rb +2 -2
  64. data/lib/{holidays → generated_definitions}/au.rb +19 -10
  65. data/lib/{holidays → generated_definitions}/be.rb +2 -2
  66. data/lib/{holidays → generated_definitions}/br.rb +2 -2
  67. data/lib/{holidays → generated_definitions}/ca.rb +2 -2
  68. data/lib/{holidays → generated_definitions}/ch.rb +2 -2
  69. data/lib/{holidays → generated_definitions}/cl.rb +2 -2
  70. data/lib/{holidays → generated_definitions}/cr.rb +2 -2
  71. data/lib/{holidays → generated_definitions}/cz.rb +2 -2
  72. data/lib/{holidays → generated_definitions}/de.rb +5 -4
  73. data/lib/{holidays → generated_definitions}/dk.rb +2 -2
  74. data/lib/{holidays → generated_definitions}/ecb_target.rb +2 -2
  75. data/lib/{holidays → generated_definitions}/el.rb +2 -2
  76. data/lib/{holidays → generated_definitions}/es.rb +6 -6
  77. data/lib/{holidays → generated_definitions}/europe.rb +7 -6
  78. data/lib/{holidays → generated_definitions}/fed_ex.rb +8 -3
  79. data/lib/{holidays → generated_definitions}/federal_reserve.rb +2 -2
  80. data/lib/{holidays → generated_definitions}/fi.rb +2 -2
  81. data/lib/{holidays → generated_definitions}/fr.rb +2 -2
  82. data/lib/{holidays → generated_definitions}/gb.rb +2 -2
  83. data/lib/{holidays → generated_definitions}/hr.rb +2 -2
  84. data/lib/{holidays → generated_definitions}/hu.rb +2 -2
  85. data/lib/{holidays → generated_definitions}/ie.rb +2 -2
  86. data/lib/{holidays → generated_definitions}/is.rb +2 -2
  87. data/lib/{holidays → generated_definitions}/it.rb +2 -2
  88. data/lib/{holidays → generated_definitions}/jp.rb +5 -5
  89. data/lib/{holidays → generated_definitions}/li.rb +2 -2
  90. data/lib/{holidays → generated_definitions}/lt.rb +2 -2
  91. data/lib/{holidays → generated_definitions}/ma.rb +2 -2
  92. data/lib/{holidays → generated_definitions}/mx.rb +2 -2
  93. data/lib/{holidays → generated_definitions}/nerc.rb +2 -2
  94. data/lib/{holidays → generated_definitions}/nl.rb +2 -2
  95. data/lib/{holidays → generated_definitions}/no.rb +2 -2
  96. data/lib/{holidays → generated_definitions}/north_america.rb +8 -3
  97. data/lib/{holidays → generated_definitions}/nyse.rb +2 -2
  98. data/lib/{holidays → generated_definitions}/nz.rb +5 -5
  99. data/lib/{holidays → generated_definitions}/ph.rb +2 -2
  100. data/lib/{holidays → generated_definitions}/pl.rb +2 -2
  101. data/lib/{holidays → generated_definitions}/pt.rb +2 -2
  102. data/lib/{holidays → generated_definitions}/ro.rb +2 -2
  103. data/lib/{holidays → generated_definitions}/scandinavia.rb +2 -2
  104. data/lib/{holidays → generated_definitions}/se.rb +2 -2
  105. data/lib/{holidays → generated_definitions}/sg.rb +2 -2
  106. data/lib/{holidays → generated_definitions}/si.rb +2 -2
  107. data/lib/{holidays → generated_definitions}/sk.rb +2 -2
  108. data/lib/{holidays → generated_definitions}/united_nations.rb +2 -2
  109. data/lib/{holidays → generated_definitions}/ups.rb +8 -3
  110. data/lib/{holidays → generated_definitions}/us.rb +8 -3
  111. data/lib/{holidays → generated_definitions}/ve.rb +2 -2
  112. data/lib/{holidays → generated_definitions}/vi.rb +2 -2
  113. data/lib/{holidays → generated_definitions}/za.rb +2 -2
  114. data/lib/holidays.rb +120 -665
  115. data/lib/holidays/core_extensions/date.rb +39 -0
  116. data/lib/holidays/date_calculator/day_of_month.rb +68 -0
  117. data/lib/holidays/date_calculator/easter.rb +58 -0
  118. data/lib/holidays/date_calculator/weekend_modifier.rb +49 -0
  119. data/lib/holidays/date_calculator_factory.rb +21 -0
  120. data/lib/holidays/definition/context/generator.rb +216 -0
  121. data/lib/holidays/definition/context/merger.rb +26 -0
  122. data/lib/holidays/definition/repository/cache.rb +33 -0
  123. data/lib/holidays/definition/repository/holidays_by_month.rb +49 -0
  124. data/lib/holidays/definition/repository/proc_cache.rb +36 -0
  125. data/lib/holidays/definition/repository/regions.rb +36 -0
  126. data/lib/holidays/definition/validator/region.rb +45 -0
  127. data/lib/holidays/definition_factory.rb +50 -0
  128. data/lib/holidays/option/context/parse_options.rb +96 -0
  129. data/lib/holidays/option_factory.rb +14 -0
  130. data/lib/holidays/use_case/context/between.rb +105 -0
  131. data/lib/holidays/use_case/context/dates_driver_builder.rb +64 -0
  132. data/lib/holidays/use_case_factory.rb +20 -0
  133. data/lib/holidays/version.rb +1 -1
  134. data/test/defs/test_defs_ar.rb +1 -1
  135. data/test/defs/test_defs_at.rb +2 -2
  136. data/test/defs/test_defs_au.rb +61 -43
  137. data/test/defs/test_defs_be.rb +1 -1
  138. data/test/defs/test_defs_br.rb +1 -1
  139. data/test/defs/test_defs_ca.rb +1 -1
  140. data/test/defs/test_defs_ch.rb +1 -1
  141. data/test/defs/test_defs_cl.rb +1 -1
  142. data/test/defs/test_defs_cr.rb +1 -1
  143. data/test/defs/test_defs_cz.rb +1 -1
  144. data/test/defs/test_defs_de.rb +18 -30
  145. data/test/defs/test_defs_dk.rb +1 -1
  146. data/test/defs/test_defs_ecb_target.rb +3 -3
  147. data/test/defs/test_defs_el.rb +1 -1
  148. data/test/defs/test_defs_es.rb +36 -25
  149. data/test/defs/test_defs_europe.rb +82 -84
  150. data/test/defs/test_defs_fed_ex.rb +4 -1
  151. data/test/defs/test_defs_federal_reserve.rb +1 -1
  152. data/test/defs/test_defs_fi.rb +1 -1
  153. data/test/defs/test_defs_fr.rb +1 -1
  154. data/test/defs/test_defs_gb.rb +20 -20
  155. data/test/defs/test_defs_hr.rb +1 -1
  156. data/test/defs/test_defs_hu.rb +2 -2
  157. data/test/defs/test_defs_ie.rb +1 -1
  158. data/test/defs/test_defs_is.rb +1 -1
  159. data/test/defs/test_defs_it.rb +1 -1
  160. data/test/defs/test_defs_jp.rb +1 -1
  161. data/test/defs/test_defs_li.rb +4 -4
  162. data/test/defs/test_defs_lt.rb +1 -1
  163. data/test/defs/test_defs_ma.rb +1 -1
  164. data/test/defs/test_defs_mx.rb +1 -1
  165. data/test/defs/test_defs_nerc.rb +1 -1
  166. data/test/defs/test_defs_nl.rb +1 -1
  167. data/test/defs/test_defs_no.rb +1 -1
  168. data/test/defs/test_defs_north_america.rb +5 -3
  169. data/test/defs/test_defs_nyse.rb +1 -1
  170. data/test/defs/test_defs_nz.rb +19 -9
  171. data/test/defs/test_defs_ph.rb +1 -1
  172. data/test/defs/test_defs_pl.rb +1 -1
  173. data/test/defs/test_defs_pt.rb +1 -1
  174. data/test/defs/test_defs_ro.rb +2 -3
  175. data/test/defs/test_defs_scandinavia.rb +1 -1
  176. data/test/defs/test_defs_se.rb +1 -1
  177. data/test/defs/test_defs_sg.rb +1 -1
  178. data/test/defs/test_defs_si.rb +7 -7
  179. data/test/defs/test_defs_sk.rb +1 -1
  180. data/test/defs/test_defs_united_nations.rb +1 -1
  181. data/test/defs/test_defs_ups.rb +5 -2
  182. data/test/defs/test_defs_us.rb +5 -3
  183. data/test/defs/test_defs_ve.rb +1 -1
  184. data/test/defs/test_defs_vi.rb +1 -1
  185. data/test/defs/test_defs_za.rb +1 -1
  186. data/test/{test_date.rb → holidays/core_extensions/test_date.rb} +8 -2
  187. data/test/holidays/date_calculator/test_day_of_month.rb +27 -0
  188. data/test/holidays/date_calculator/test_easter.rb +29 -0
  189. data/test/holidays/date_calculator/test_weekend_modifier.rb +33 -0
  190. data/test/holidays/definition/context/test_generator.rb +84 -0
  191. data/test/holidays/definition/context/test_merger.rb +22 -0
  192. data/test/holidays/definition/repository/test_cache.rb +82 -0
  193. data/test/holidays/definition/repository/test_holidays_by_month.rb +187 -0
  194. data/test/holidays/definition/repository/test_proc_cache.rb +29 -0
  195. data/test/holidays/definition/repository/test_regions.rb +86 -0
  196. data/test/holidays/definition/validator/test_region.rb +50 -0
  197. data/test/holidays/option/context/test_parse_options.rb +69 -0
  198. data/test/holidays/test_date_calculator_factory.rb +21 -0
  199. data/test/holidays/test_definition_factory.rb +34 -0
  200. data/test/holidays/test_option_factory.rb +9 -0
  201. data/test/holidays/test_use_case_factory.rb +13 -0
  202. data/test/holidays/use_case/context/test_between.rb +75 -0
  203. data/test/holidays/use_case/context/test_dates_driver_builder.rb +91 -0
  204. data/test/test_all_regions.rb +14 -3
  205. data/test/test_helper.rb +2 -1
  206. data/test/test_holidays.rb +19 -24
  207. data/test/test_holidays_between.rb +85 -0
  208. data/test/test_multiple_regions.rb +2 -2
  209. data/test/test_parse_definitions.rb +10 -4
  210. metadata +181 -111
  211. data/.coveralls.yml +0 -1
  212. data/lib/holidays/MANIFEST +0 -55
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/nl.yaml
5
+ # Definitions loaded: definitions/nl.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/nl'
11
+ # require 'generated_definitions/nl'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module NL # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/no.yaml
5
+ # Definitions loaded: definitions/no.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/no'
11
+ # require 'generated_definitions/no'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module NO # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/ca.yaml, data/mx.yaml, data/us.yaml, data/north_america_informal.yaml
5
+ # Definitions loaded: definitions/ca.yaml, definitions/mx.yaml, definitions/us.yaml, definitions/north_america_informal.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/north_america'
11
+ # require 'generated_definitions/north_america'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module NORTH_AMERICA # :nodoc:
@@ -83,7 +83,7 @@ module Holidays
83
83
  {:wday => 1, :week => 3, :name => "Día de la Revolución", :regions => [:mx]},
84
84
  {:mday => 11, :observed => lambda { |date| Holidays.to_weekday_if_weekend(date) }, :observed_id => "to_weekday_if_weekend", :name => "Veterans Day", :regions => [:us]},
85
85
  {:wday => 4, :week => 4, :name => "Thanksgiving", :regions => [:us]},
86
- {:wday => 5, :week => 4, :name => "Day after Thanksgiving", :regions => [:us_ca]}],
86
+ {:function => lambda { |year| Holidays.day_after_thanksgiving(year) }, :function_id => "day_after_thanksgiving(year)", :name => "Day after Thanksgiving", :regions => [:us_ca]}],
87
87
  12 => [{:mday => 25, :name => "Christmas Day", :regions => [:ca]},
88
88
  {:mday => 26, :name => "Boxing Day", :regions => [:ca]},
89
89
  {:mday => 12, :type => :informal, :name => "Día de la Virgen de Guadalupe", :regions => [:mx]},
@@ -116,6 +116,11 @@ def self.us_inauguration_day(year)
116
116
  end
117
117
 
118
118
 
119
+ def self.day_after_thanksgiving(year)
120
+ Date.calculate_mday(year, 11, 4, 4) + 1
121
+ end
122
+
123
+
119
124
 
120
125
  end
121
126
 
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/nyse.yaml
5
+ # Definitions loaded: definitions/nyse.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/nyse'
11
+ # require 'generated_definitions/nyse'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module NYSE # :nodoc:
@@ -2,18 +2,18 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/nz.yaml
5
+ # Definitions loaded: definitions/nz.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/nz'
11
+ # require 'generated_definitions/nz'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module NZ # :nodoc:
15
15
  def self.defined_regions
16
- [:nz, :nz_sl, :nz_we, :nz_ak, :nz_nl, :nz_ot, :nz_sc, :nz_hb, :nz_mb, :nz_ca, :nz_ch, :nz_wl]
16
+ [:nz, :nz_sl, :nz_we, :nz_ak, :nz_nl, :nz_ne, :nz_ot, :nz_ta, :nz_sc, :nz_hb, :nz_mb, :nz_ca, :nz_ch, :nz_wl]
17
17
  end
18
18
 
19
19
  def self.holidays_by_month
@@ -27,10 +27,10 @@ module Holidays
27
27
  {:mday => 22, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Wellington Anniversary Day", :regions => [:nz_we]},
28
28
  {:mday => 29, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Auckland Anniversary Day", :regions => [:nz_ak]},
29
29
  {:mday => 29, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Northland Anniversary Day", :regions => [:nz_nl]}],
30
- 2 => [{:mday => 1, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Nelson Anniversary Day", :regions => [:nz_ak]},
30
+ 2 => [{:mday => 1, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Nelson Anniversary Day", :regions => [:nz_ne]},
31
31
  {:mday => 6, :observed => lambda { |date| Holidays.to_monday_if_weekend(date) }, :observed_id => "to_monday_if_weekend", :name => "Waitangi Day", :regions => [:nz]}],
32
32
  3 => [{:mday => 23, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Otago Anniversary Day", :regions => [:nz_ot]},
33
- {:wday => 1, :week => 2, :name => "Taranaki Anniversary Day", :regions => [:nz_ak]}],
33
+ {:wday => 1, :week => 2, :observed => lambda { |date| Holidays.closest_monday(date) }, :observed_id => "closest_monday", :name => "Taranaki Anniversary Day", :regions => [:nz_ta]}],
34
34
  4 => [{:mday => 25, :observed => lambda { |date| Holidays.to_monday_if_weekend(date) }, :observed_id => "to_monday_if_weekend", :name => "ANZAC Day", :regions => [:nz]}],
35
35
  6 => [{:wday => 1, :week => 1, :name => "Queen's Birthday", :regions => [:nz]}],
36
36
  9 => [{:wday => 1, :week => 4, :name => "Dominion Day", :regions => [:nz_sc]}],
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/ph.yaml
5
+ # Definitions loaded: definitions/ph.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/ph'
11
+ # require 'generated_definitions/ph'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module PH # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/pl.yaml
5
+ # Definitions loaded: definitions/pl.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/pl'
11
+ # require 'generated_definitions/pl'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module PL # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/pt.yaml
5
+ # Definitions loaded: definitions/pt.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/pt'
11
+ # require 'generated_definitions/pt'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module PT # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/ro.yaml
5
+ # Definitions loaded: definitions/ro.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/ro'
11
+ # require 'generated_definitions/ro'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module RO # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/dk.yaml, data/is.yaml, data/no.yaml, data/se.yaml, data/fi.yaml
5
+ # Definitions loaded: definitions/dk.yaml, definitions/is.yaml, definitions/no.yaml, definitions/se.yaml, definitions/fi.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/scandinavia'
11
+ # require 'generated_definitions/scandinavia'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module SCANDINAVIA # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/se.yaml
5
+ # Definitions loaded: definitions/se.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/se'
11
+ # require 'generated_definitions/se'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module SE # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/sg.yaml
5
+ # Definitions loaded: definitions/sg.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/sg'
11
+ # require 'generated_definitions/sg'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module SG # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/si.yaml
5
+ # Definitions loaded: definitions/si.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/si'
11
+ # require 'generated_definitions/si'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module SI # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/sk.yaml
5
+ # Definitions loaded: definitions/sk.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/sk'
11
+ # require 'generated_definitions/sk'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module SK # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/united_nations.yaml
5
+ # Definitions loaded: definitions/united_nations.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/united_nations'
11
+ # require 'generated_definitions/united_nations'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module UNITED_NATIONS # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/ups.yaml
5
+ # Definitions loaded: definitions/ups.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/ups'
11
+ # require 'generated_definitions/ups'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module UPS # :nodoc:
@@ -23,13 +23,18 @@ module Holidays
23
23
  7 => [{:mday => 4, :observed => lambda { |date| Holidays.to_weekday_if_weekend(date) }, :observed_id => "to_weekday_if_weekend", :name => "Independence Day", :regions => [:ups]}],
24
24
  9 => [{:wday => 1, :week => 1, :name => "Labor Day", :regions => [:ups]}],
25
25
  11 => [{:wday => 4, :week => 4, :name => "Thanksgiving", :regions => [:ups]},
26
- {:wday => 5, :week => 4, :name => "Day After Thanksgiving", :regions => [:ups]}],
26
+ {:function => lambda { |year| Holidays.day_after_thanksgiving(year) }, :function_id => "day_after_thanksgiving(year)", :name => "Day After Thanksgiving", :regions => [:ups]}],
27
27
  12 => [{:mday => 25, :observed => lambda { |date| Holidays.to_weekday_if_weekend(date) }, :observed_id => "to_weekday_if_weekend", :name => "Christmas Day", :regions => [:ups]},
28
28
  {:mday => 31, :name => "New Year's Eve", :regions => [:ups]}]
29
29
  }
30
30
  end
31
31
  end
32
32
 
33
+ def self.day_after_thanksgiving(year)
34
+ Date.calculate_mday(year, 11, 4, 4) + 1
35
+ end
36
+
37
+
33
38
 
34
39
  end
35
40
 
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/us.yaml, data/north_america_informal.yaml
5
+ # Definitions loaded: definitions/us.yaml, definitions/north_america_informal.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/us'
11
+ # require 'generated_definitions/us'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module US # :nodoc:
@@ -37,7 +37,7 @@ module Holidays
37
37
  {:mday => 31, :type => :informal, :name => "Halloween", :regions => [:us, :ca]}],
38
38
  11 => [{:mday => 11, :observed => lambda { |date| Holidays.to_weekday_if_weekend(date) }, :observed_id => "to_weekday_if_weekend", :name => "Veterans Day", :regions => [:us]},
39
39
  {:wday => 4, :week => 4, :name => "Thanksgiving", :regions => [:us]},
40
- {:wday => 5, :week => 4, :name => "Day after Thanksgiving", :regions => [:us_ca]}],
40
+ {:function => lambda { |year| Holidays.day_after_thanksgiving(year) }, :function_id => "day_after_thanksgiving(year)", :name => "Day after Thanksgiving", :regions => [:us_ca]}],
41
41
  12 => [{:mday => 25, :observed => lambda { |date| Holidays.to_weekday_if_weekend(date) }, :observed_id => "to_weekday_if_weekend", :name => "Christmas Day", :regions => [:us]}],
42
42
  4 => [{:mday => 1, :type => :informal, :name => "April Fool's Day", :regions => [:us, :ca]},
43
43
  {:mday => 22, :type => :informal, :name => "Earth Day", :regions => [:us, :ca]}],
@@ -52,6 +52,11 @@ def self.us_inauguration_day(year)
52
52
  end
53
53
 
54
54
 
55
+ def self.day_after_thanksgiving(year)
56
+ Date.calculate_mday(year, 11, 4, 4) + 1
57
+ end
58
+
59
+
55
60
 
56
61
  end
57
62
 
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/ve.yaml
5
+ # Definitions loaded: definitions/ve.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/ve'
11
+ # require 'generated_definitions/ve'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module VE # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/vi.yaml
5
+ # Definitions loaded: definitions/vi.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/vi'
11
+ # require 'generated_definitions/vi'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module VI # :nodoc:
@@ -2,13 +2,13 @@
2
2
  module Holidays
3
3
  # This file is generated by the Ruby Holidays gem.
4
4
  #
5
- # Definitions loaded: data/za.yaml
5
+ # Definitions loaded: definitions/za.yaml
6
6
  #
7
7
  # To use the definitions in this file, load it right after you load the
8
8
  # Holiday gem:
9
9
  #
10
10
  # require 'holidays'
11
- # require 'holidays/za'
11
+ # require 'generated_definitions/za'
12
12
  #
13
13
  # All the definitions are available at https://github.com/alexdunae/holidays
14
14
  module ZA # :nodoc:
data/lib/holidays.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  # encoding: utf-8
2
2
  $:.unshift File.dirname(__FILE__)
3
3
 
4
- require 'digest/md5'
5
4
  require 'date'
6
- require 'yaml'
5
+ require 'holidays/definition_factory'
6
+ require 'holidays/date_calculator_factory'
7
+ require 'holidays/option_factory'
8
+ require 'holidays/use_case_factory'
7
9
 
8
10
  # == Region options
9
11
  # Holidays can be defined as belonging to one or more regions and sub regions.
@@ -47,712 +49,165 @@ module Holidays
47
49
  # Exception thrown when an unknown region is requested.
48
50
  class UnknownRegionError < ArgumentError; end
49
51
 
50
- @@regions = []
51
- @@holidays_by_month = {}
52
- @@proc_cache = {}
53
-
54
- @@cache = {}
55
- @@cache_range = {}
56
- class << self
57
- def cache_range; @@cache_range; end
58
- def cache; @@cache; end
59
- end
60
-
61
52
  WEEKS = {:first => 1, :second => 2, :third => 3, :fourth => 4, :fifth => 5, :last => -1, :second_last => -2, :third_last => -3}
62
53
  MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
63
54
  DAY_SYMBOLS = Date::DAYNAMES.collect { |n| n.downcase.intern }
64
- DEFINITION_PATH = File.expand_path(File.dirname(__FILE__) + '/holidays/')
65
-
66
- # Get all holidays on a given date.
67
- #
68
- # [<tt>date</tt>] A Date object.
69
- # [<tt>:options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
70
- #
71
- # Returns an array of hashes or nil. See Holidays#between for the output
72
- # format.
73
- #
74
- # Also available via Date#holidays.
75
- def self.on(date, *options)
76
- self.between(date, date, options)
77
- end
78
-
79
- # Does the given work-week have any holidays?
80
- #
81
- # [<tt>date</tt>] A Date object.
82
- # [<tt>:options</tt>] One or more region symbols, and/or <tt>:informal</tt>. Automatically includes <tt>:observed</tt>. If you don't want this, pass <tt>:no_observed</tt>
83
- #
84
- # The given Date can be any day of the week.
85
- # Returns true if any holidays fall on Monday - Friday of the given week.
86
- def self.full_week?(date, *options)
87
- days_to_monday = date.wday - 1
88
- days_to_friday = 5 - date.wday
89
- start_date = date - days_to_monday
90
- end_date = date + days_to_friday
91
- options += [:observed] unless options.include?(:no_observed)
92
- options.delete(:no_observed)
93
- self.between(start_date, end_date, options).empty?
94
- end
95
55
 
96
- # Get all holidays occuring between two dates, inclusively.
97
- #
98
- # Returns an array of hashes or nil.
99
- #
100
- # Each holiday is returned as a hash with the following fields:
101
- # [<tt>start_date</tt>] Ruby Date object.
102
- # [<tt>end_date</tt>] Ruby Date object.
103
- # [<tt>options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
104
- #
105
- # ==== Example
106
- # from = Date.civil(2008,7,1)
107
- # to = Date.civil(2008,7,31)
108
- #
109
- # Holidays.between(from, to, :ca, :us)
110
- # => [{:name => 'Canada Day', :regions => [:ca]...}
111
- # {:name => 'Independence Day'', :regions => [:us], ...}]
112
- def self.between(start_date, end_date, *options)
113
- # remove the timezone
114
- start_date = start_date.new_offset(0) + start_date.offset if start_date.respond_to?(:new_offset)
115
- end_date = end_date.new_offset(0) + end_date.offset if end_date.respond_to?(:new_offset)
56
+ DEFINITIONS_PATH = 'generated_definitions'
57
+ FULL_DEFINITIONS_PATH = File.expand_path(File.dirname(__FILE__) + "/#{DEFINITIONS_PATH}")
116
58
 
117
- # get simple dates
118
- start_date, end_date = get_date(start_date), get_date(end_date)
119
-
120
- if range = @@cache_range[options]
121
- if range.begin < start_date && range.end > end_date
122
- return @@cache[options].select do |holiday|
123
- holiday[:date] >= start_date && holiday[:date] <= end_date
124
- end
59
+ class << self
60
+ # Get all holidays on a given date.
61
+ #
62
+ # [<tt>date</tt>] A Date object.
63
+ # [<tt>:options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
64
+ #
65
+ # Returns an array of hashes or nil. See Holidays#between for the output
66
+ # format.
67
+ #
68
+ # Also available via Date#holidays.
69
+ def on(date, *options)
70
+ between(date, date, options)
71
+ end
72
+
73
+ # Does the given work-week have any holidays?
74
+ #
75
+ # [<tt>date</tt>] A Date object.
76
+ # [<tt>:options</tt>] One or more region symbols, and/or <tt>:informal</tt>. Automatically includes <tt>:observed</tt>. If you don't want this, pass <tt>:no_observed</tt>
77
+ #
78
+ # The given Date can be any day of the week.
79
+ # Returns true if any holidays fall on Monday - Friday of the given week.
80
+ def full_week?(date, *options)
81
+ days_to_monday = date.wday - 1
82
+ days_to_friday = 5 - date.wday
83
+ start_date = date - days_to_monday
84
+ end_date = date + days_to_friday
85
+ options += [:observed] unless options.include?(:no_observed)
86
+ options.delete(:no_observed)
87
+ between(start_date, end_date, options).empty?
88
+ end
89
+
90
+ # Get all holidays occuring between two dates, inclusively.
91
+ #
92
+ # Returns an array of hashes or nil.
93
+ #
94
+ # Each holiday is returned as a hash with the following fields:
95
+ # [<tt>start_date</tt>] Ruby Date object.
96
+ # [<tt>end_date</tt>] Ruby Date object.
97
+ # [<tt>options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
98
+ #
99
+ # ==== Example
100
+ # from = Date.civil(2008,7,1)
101
+ # to = Date.civil(2008,7,31)
102
+ #
103
+ # Holidays.between(from, to, :ca, :us)
104
+ # => [{:name => 'Canada Day', :regions => [:ca]...}
105
+ # {:name => 'Independence Day'', :regions => [:us], ...}]
106
+ def between(start_date, end_date, *options)
107
+ raise ArgumentError unless start_date && end_date
108
+
109
+ # remove the timezone
110
+ start_date = start_date.new_offset(0) + start_date.offset if start_date.respond_to?(:new_offset)
111
+ end_date = end_date.new_offset(0) + end_date.offset if end_date.respond_to?(:new_offset)
112
+
113
+ start_date, end_date = get_date(start_date), get_date(end_date)
114
+
115
+ if cached_holidays = DefinitionFactory.cache_repository.find(start_date, end_date, options)
116
+ return cached_holidays
125
117
  end
126
- end
127
118
 
128
- regions, observed, informal = parse_options(options)
129
- holidays = []
119
+ regions, observed, informal = OptionFactory.parse_options.call(options)
120
+ date_driver_hash = UseCaseFactory.dates_driver_builder.call(start_date, end_date)
130
121
 
131
- dates = {}
132
- (start_date..end_date).each do |date|
133
- # Always include month '0' for variable-month holidays
134
- dates[date.year] = [0] unless dates[date.year]
135
- # TODO: test this, maybe should push then flatten
136
- dates[date.year] << date.month unless dates[date.year].include?(date.month)
122
+ UseCaseFactory.between.call(start_date, end_date, date_driver_hash, regions, observed, informal)
137
123
  end
138
124
 
139
- dates.each do |year, months|
140
- months.each do |month|
141
- next unless hbm = @@holidays_by_month[month]
142
-
143
- hbm.each do |h|
144
- next unless in_region?(regions, h[:regions])
145
-
146
- # Skip informal holidays unless they have been requested
147
- next if h[:type] == :informal and not informal
148
-
149
- if h[:function]
150
- # Holiday definition requires a calculation
151
- result = call_proc(h[:function], year)
152
-
153
- # Procs may return either Date or an integer representing mday
154
- if result.kind_of?(Date)
155
- month = result.month
156
- mday = result.mday
157
- else
158
- mday = result
159
- end
160
- else
161
- # Calculate the mday
162
- mday = h[:mday] || Date.calculate_mday(year, month, h[:week], h[:wday])
163
- end
125
+ # Allows a developer to explicitly calculate and cache holidays within a given period
126
+ def cache_between(start_date, end_date, *options)
127
+ start_date, end_date = get_date(start_date), get_date(end_date)
128
+ cache_data = between(start_date, end_date, *options)
164
129
 
165
- # Silently skip bad mdays
166
- begin
167
- date = Date.civil(year, month, mday)
168
- rescue; next; end
169
-
170
- # If the :observed option is set, calculate the date when the holiday
171
- # is observed.
172
- if observed and h[:observed]
173
- date = call_proc(h[:observed], date)
174
- end
175
-
176
- if date.between?(start_date, end_date)
177
- holidays << {:date => date, :name => h[:name], :regions => h[:regions]}
178
- end
179
-
180
- end
181
- end
130
+ DefinitionFactory.cache_repository.cache_between(start_date, end_date, cache_data, options)
182
131
  end
183
132
 
184
- holidays.sort{|a, b| a[:date] <=> b[:date] }
185
- end
186
-
187
- # Allows a developer to explicitly calculate and cache holidays within a given period
188
- def self.cache_between(start_date, end_date, *options)
189
- start_date, end_date = get_date(start_date), get_date(end_date)
190
- @@cache[options] = between(start_date, end_date, *options)
191
- @@cache_range[options] = start_date..end_date
192
- end
193
-
194
- # Merge a new set of definitions into the Holidays module.
195
- #
196
- # This method is automatically called when including holiday definition
197
- # files.
198
- def self.merge_defs(regions, holidays) # :nodoc:
199
- @@regions = @@regions | regions
200
- @@regions.uniq!
201
-
202
- holidays.each do |month, holiday_defs|
203
- @@holidays_by_month[month] = [] unless @@holidays_by_month[month]
204
- holiday_defs.each do |holiday_def|
205
-
206
- exists = false
207
- @@holidays_by_month[month].each do |ex|
208
- # TODO: gross.
209
- if ex[:name] == holiday_def[:name] and ex[:wday] == holiday_def[:wday] and ex[:mday] == holiday_def[:mday] and ex[:week] == holiday_def[:week] and ex[:function_id] == holiday_def[:function_id] and ex[:type] == holiday_def[:type] and ex[:observed_id] == holiday_def[:observed_id]
210
- # append regions
211
- ex[:regions] << holiday_def[:regions]
212
-
213
- # Should do this once we're done
214
- ex[:regions].flatten!
215
- ex[:regions].uniq!
216
- exists = true
217
- end
218
- end
219
-
220
- @@holidays_by_month[month] << holiday_def unless exists
221
- end
133
+ #TODO This should not be publicly available. I need to restructure the public
134
+ # API for this class to something more sensible.
135
+ def merge_defs(regions, holidays) # :nodoc:
136
+ DefinitionFactory.merger.call(regions, holidays)
222
137
  end
223
- end
224
-
225
- # Get the date of Easter Sunday in a given year. From Easter Sunday, it is
226
- # possible to calculate many traditional holidays in Western countries.
227
- # Returns a Date object.
228
- def self.easter(year)
229
- y = year
230
- a = y % 19
231
- b = y / 100
232
- c = y % 100
233
- d = b / 4
234
- e = b % 4
235
- f = (b + 8) / 25
236
- g = (b - f + 1) / 3
237
- h = (19 * a + b - d - g + 15) % 30
238
- i = c / 4
239
- k = c % 4
240
- l = (32 + 2 * e + 2 * i - h - k) % 7
241
- m = (a + 11 * h + 22 * l) / 451
242
- month = (h + l - 7 * m + 114) / 31
243
- day = ((h + l - 7 * m + 114) % 31) + 1
244
- Date.civil(year, month, day)
245
- end
246
138
 
247
- # A method to calculate the orthodox easter date, returns date in the Gregorian (western) calendar
248
- # Safe until appr. 4100 AD, when one leap day will be removed.
249
- # Returns a Date object.
250
- def self.orthodox_easter(year)
251
- y = year
252
- g = y % 19
253
- i = (19 * g + 15) % 30
254
- j = (year + year/4 + i) % 7
255
- j_month = 3 + (i - j + 40) / 44
256
- j_day = i - j + 28 - 31 * (j_month / 4)
257
- j_date = Date.civil(year, j_month, j_day)
258
- case
259
- # up until 1582, julian and gregorian easter dates were identical
260
- when year <= 1582
261
- offset = 0
262
- # between the years 1583 and 1699 10 days are added to the julian day count
263
- when (year >= 1583 and year <= 1699)
264
- offset = 10
265
- # after 1700, 1 day is added for each century, except if the century year is exactly divisible by 400 (in which case no days are added).
266
- # Safe until 4100 AD, when one leap day will be removed.
267
- when year >= 1700
268
- offset = (year - 1700).divmod(100)[0] + ((year - year.divmod(100)[1]).divmod(400)[1] == 0 ? 0 : 1) - (year - year.divmod(100)[1] - 1700).divmod(400)[0] + 10
139
+ def easter(year)
140
+ DateCalculatorFactory.easter_calculator.calculate_easter_for(year)
269
141
  end
270
- # add offset to the julian day
271
- return Date.jd(j_date.jd + offset)
272
- end
273
142
 
274
- # Move date to Monday if it occurs on a Sunday.
275
- # Used as a callback function.
276
- def self.to_monday_if_sunday(date)
277
- date += 1 if date.wday == 0
278
- date
279
- end
280
-
281
- # Move date to Monday if it occurs on a Saturday on Sunday.
282
- # Used as a callback function.
283
- def self.to_monday_if_weekend(date)
284
- date += 1 if date.wday == 0
285
- date += 2 if date.wday == 6
286
- date
287
- end
288
-
289
- # Move Boxing Day if it falls on a weekend, leaving room for Christmas.
290
- # Used as a callback function.
291
- def self.to_weekday_if_boxing_weekend(date)
292
- if date.wday == 6 or date.wday == 0
293
- date += 2
294
- elsif date.wday == 1
295
- date += 1
143
+ def orthodox_easter(year)
144
+ DateCalculatorFactory.easter_calculator.calculate_orthodox_easter_for(year)
296
145
  end
297
- date
298
- end
299
146
 
300
- # Move date to Monday if it occurs on a Sunday or to Friday if it occurs on a
301
- # Saturday.
302
- # Used as a callback function.
303
- def self.to_weekday_if_weekend(date)
304
- date += 1 if date.wday == 0
305
- date -= 1 if date.wday == 6
306
- date
307
- end
308
-
309
- # Returns an array of symbols all the available holiday definitions.
310
- #
311
- # Optional `full_path` param is used internally for loading all the definitions.
312
- def self.available(full_path = false)
313
- paths = Dir.glob(DEFINITION_PATH + '/*.rb')
314
- full_path ? paths : paths.collect { |path| path.match(/([a-z_-]+)\.rb/i)[1].to_sym }
315
- end
316
-
317
- # Returns an array of symbols of all the available holiday regions.
318
- def self.regions
319
- @@regions
320
- end
321
-
322
- # Load all available holiday definitions
323
- def self.load_all
324
- self.available(true).each { |path| require path }
325
- end
326
-
327
- # Parses provided holiday definition file(s) and loads them so that they are immediately available.
328
- def self.load_custom(*files)
329
- regions, rules_by_month, custom_methods, tests = self.parse_definition_files(files)
330
- merge_defs(regions, rules_by_month)
331
- end
332
-
333
- # Parses provided holiday definition file(s) and returns strings containing the generated module and test source
334
- def self.parse_definition_files_and_return_source(module_name, *files)
335
- regions, rules_by_month, custom_methods, tests = self.parse_definition_files(files)
336
- module_src, test_src = self.generate_definition_source(module_name, files, regions, rules_by_month, custom_methods, tests)
337
-
338
- return module_src, test_src
339
- end
340
-
341
- private
342
- # Returns [(arr)regions, (bool)observed, (bool)informal]
343
- def self.parse_options(*options) # :nodoc:
344
- options.flatten!
345
- observed = options.delete(:observed) ? true : false
346
- informal = options.delete(:informal) ? true : false
347
- regions = parse_regions(options)
348
- return regions, observed, informal
349
- end
350
-
351
- def self.get_date(date)
352
- if date.respond_to?(:to_date)
353
- date.to_date
354
- else
355
- Date.civil(date.year, date.mon, date.mday)
147
+ def to_monday_if_sunday(date)
148
+ DateCalculatorFactory.weekend_modifier.to_monday_if_sunday(date)
356
149
  end
357
- end
358
150
 
359
- # Derive the containing region from a sub region wild-card or a sub region
360
- # and load its definition. (Common code factored out from parse_regions)
361
- def self.load_containing_region(sub_reg)
362
- prefix = sub_reg.split('_').first
363
- unless @@regions.include?(prefix.to_sym)
364
- begin
365
- require "holidays/#{prefix}"
366
- rescue LoadError
367
- raise UnknownRegionError, "Could not load holidays/#{prefix}"
368
- end
151
+ def to_monday_if_weekend(date)
152
+ DateCalculatorFactory.weekend_modifier.to_monday_if_weekend(date)
369
153
  end
370
- end
371
154
 
372
- # Check regions against list of supported regions and return an array of
373
- # symbols.
374
- #
375
- # If a wildcard region is found (e.g. <tt>:ca_</tt>) it is expanded into all
376
- # of its available sub regions.
377
- def self.parse_regions(regions) # :nodoc:
378
- regions = [regions] unless regions.kind_of?(Array)
379
- return [:any] if regions.empty?
380
-
381
- regions = regions.collect { |r| r.to_sym }
382
-
383
- # Found sub region wild-card
384
- regions.delete_if do |r|
385
- if r.to_s =~ /_$/
386
- load_containing_region(r.to_s)
387
- regions << @@regions.select { |dr| dr.to_s =~ Regexp.new("^#{r.to_s}") }
388
- true
389
- end
155
+ def to_weekday_if_boxing_weekend(date)
156
+ DateCalculatorFactory.weekend_modifier.to_weekday_if_boxing_weekend(date)
390
157
  end
391
158
 
392
- regions.flatten!
393
-
394
- require "holidays/north_america" if regions.include?(:us) # special case for north_america/US cross-linking
395
-
396
- regions.each do |r|
397
- unless r == :any or @@regions.include?(r)
398
- begin
399
- require "holidays/#{r.to_s}"
400
- rescue LoadError => e
401
- # This could be a sub region that does not have any holiday
402
- # definitions of its own; try to load the containing region instead.
403
- if r.to_s =~ /_/
404
- load_containing_region(r.to_s)
405
- else
406
- raise UnknownRegionError, "Could not load holidays/#{r.to_s}"
407
- end
408
- end
409
- end
159
+ def to_weekday_if_boxing_weekend_from_year(year)
160
+ DateCalculatorFactory.weekend_modifier.to_weekday_if_boxing_weekend_from_year(year)
410
161
  end
411
- regions
412
- end
413
162
 
414
- # Check sub regions.
415
- #
416
- # When request :any, all holidays should be returned.
417
- # When requesting :ca_bc, holidays in :ca or :ca_bc should be returned.
418
- # When requesting :ca, holidays in :ca but not its subregions should be returned.
419
- def self.in_region?(requested, available) # :nodoc:
420
- return true if requested.include?(:any)
421
-
422
- # When an underscore is encountered, derive the parent regions
423
- # symbol and include both in the requested array.
424
- requested = requested.collect do |r|
425
- r.to_s =~ /_/ ? [r, r.to_s.gsub(/_[\w]*$/, '').to_sym] : r
163
+ def to_weekday_if_weekend(date)
164
+ DateCalculatorFactory.weekend_modifier.to_weekday_if_weekend(date)
426
165
  end
427
166
 
428
- requested = requested.flatten.uniq
429
-
430
- available.any? { |avail| requested.include?(avail) }
431
- end
432
-
433
- # Call a proc function defined in a holiday definition file.
434
- #
435
- # Procs are cached.
436
- #
437
- # ==== Benchmarks
438
- #
439
- # Lookup Easter Sunday, with caching, by number of iterations:
440
- #
441
- # user system total real
442
- # 0001 0.000000 0.000000 0.000000 ( 0.000000)
443
- # 0010 0.000000 0.000000 0.000000 ( 0.000000)
444
- # 0100 0.078000 0.000000 0.078000 ( 0.078000)
445
- # 1000 0.641000 0.000000 0.641000 ( 0.641000)
446
- # 5000 3.172000 0.015000 3.187000 ( 3.219000)
447
- #
448
- # Lookup Easter Sunday, without caching, by number of iterations:
449
- #
450
- # user system total real
451
- # 0001 0.000000 0.000000 0.000000 ( 0.000000)
452
- # 0010 0.016000 0.000000 0.016000 ( 0.016000)
453
- # 0100 0.125000 0.000000 0.125000 ( 0.125000)
454
- # 1000 1.234000 0.000000 1.234000 ( 1.234000)
455
- # 5000 6.094000 0.031000 6.125000 ( 6.141000)
456
- def self.call_proc(function, year) # :nodoc:
457
- proc_key = Digest::MD5.hexdigest("#{function.to_s}_#{year.to_s}")
458
- @@proc_cache[proc_key] = function.call(year) unless @@proc_cache[proc_key]
459
- @@proc_cache[proc_key]
460
- end
461
-
462
- def self.parse_definition_files(files)
463
- raise ArgumentError, "Must have at least one file to parse" if files.empty?
464
-
465
- all_regions = []
466
- all_rules_by_month = {}
467
- all_custom_methods = {}
468
- all_tests = []
469
-
470
- files.flatten!
471
-
472
- files.each do |file|
473
- definition_file = YAML.load_file(file)
474
-
475
- regions, rules_by_month = self.parse_month_definitions(definition_file['months'])
476
-
477
- all_regions << regions.flatten
478
-
479
- all_rules_by_month.merge!(rules_by_month) { |month, existing, new|
480
- existing << new
481
- existing.flatten!
482
- }
483
-
484
- custom_methods = self.parse_method_definitions(definition_file['methods'])
485
- all_custom_methods.merge!(custom_methods)
486
-
487
- all_tests << self.parse_test_definitions(definition_file['tests'])
488
- end
489
-
490
- all_regions.flatten!.uniq!
491
-
492
- [all_regions, all_rules_by_month, all_custom_methods, all_tests]
493
- end
494
-
495
- def self.parse_month_definitions(month_definitions)
496
- regions = []
497
- rules_by_month = {}
498
-
499
- if month_definitions
500
- month_definitions.each do |month, definitions|
501
- rules_by_month[month] = [] unless rules_by_month[month]
502
- definitions.each do |definition|
503
- rule = {}
504
-
505
- definition.each do |key, val|
506
- rule[key.to_sym] = val
507
- end
508
-
509
- rule[:regions] = rule[:regions].collect { |r| r.to_sym }
510
-
511
- regions << rule[:regions]
512
-
513
- exists = false
514
- rules_by_month[month].each do |ex|
515
- 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]
516
- ex[:regions] << rule[:regions].flatten
517
- exists = true
518
- end
519
- end
520
-
521
- unless exists
522
- rules_by_month[month] << rule
523
- end
524
- end
525
- end
526
- end
527
-
528
- [regions, rules_by_month]
529
- end
530
-
531
- def self.parse_method_definitions(methods)
532
- custom_methods = {}
533
-
534
- if methods
535
- methods.each do |name, code|
536
- custom_methods[name] = code
537
- end
538
- end
539
-
540
- custom_methods
541
- end
542
-
543
- def self.parse_test_definitions(tests)
544
- test_strings = []
545
-
546
- if tests
547
- test_strings << tests
548
- end
549
-
550
- test_strings
551
- end
552
-
553
- def self.generate_definition_source(module_name, files, regions, rules_by_month, custom_methods, tests)
554
- month_strings = self.generate_month_definition_strings(rules_by_month)
555
-
556
- # Build the custom methods string
557
- custom_method_string = ''
558
- custom_methods.each do |key, code|
559
- custom_method_string << code + "\n\n"
167
+ def calculate_day_of_month(year, month, day, wday)
168
+ DateCalculatorFactory.day_of_month_calculator.call(year, month, day, wday)
560
169
  end
561
170
 
562
- module_src = self.generate_module_src(module_name, files, regions, month_strings, custom_method_string)
563
- test_src = self.generate_test_src(module_name, files, tests)
564
-
565
- return module_src, test_src || ''
566
- end
567
-
568
- def self.generate_month_definition_strings(rules_by_month)
569
- month_strings = []
570
-
571
- rules_by_month.each do |month, rules|
572
- month_string = " #{month.to_s} => ["
573
- rule_strings = []
574
- rules.each do |rule|
575
- string = '{'
576
- if rule[:mday]
577
- string << ":mday => #{rule[:mday]}, "
578
- elsif rule[:function]
579
- string << ":function => lambda { |year| Holidays.#{rule[:function]} }, "
580
- string << ":function_id => \"#{rule[:function].to_s}\", "
581
- else
582
- string << ":wday => #{rule[:wday]}, :week => #{rule[:week]}, "
583
- end
584
-
585
- if rule[:observed]
586
- string << ":observed => lambda { |date| Holidays.#{rule[:observed]}(date) }, "
587
- string << ":observed_id => \"#{rule[:observed].to_s}\", "
588
- end
589
-
590
- if rule[:type]
591
- string << ":type => :#{rule[:type]}, "
592
- end
593
-
594
- # shouldn't allow the same region twice
595
- string << ":name => \"#{rule[:name]}\", :regions => [:" + rule[:regions].uniq.join(', :') + "]}"
596
- rule_strings << string
597
- end
598
- month_string << rule_strings.join(",\n ") + "]"
599
- month_strings << month_string
171
+ # Returns an array of symbols all the available holiday definitions.
172
+ #
173
+ # Optional `full_path` param is used internally for loading all the definitions.
174
+ def available(full_path = false)
175
+ paths = Dir.glob(FULL_DEFINITIONS_PATH + '/*.rb')
176
+ full_path ? paths : paths.collect { |path| path.match(/([a-z_-]+)\.rb/i)[1].to_sym }
600
177
  end
601
178
 
602
- return month_strings
603
- end
604
-
605
- def self.generate_module_src(module_name, files, regions, month_strings, custom_methods)
606
- module_src = ""
607
-
608
- module_src =<<-EOM
609
- # encoding: utf-8
610
- module Holidays
611
- # This file is generated by the Ruby Holidays gem.
612
- #
613
- # Definitions loaded: #{files.join(', ')}
614
- #
615
- # To use the definitions in this file, load it right after you load the
616
- # Holiday gem:
617
- #
618
- # require 'holidays'
619
- # require 'holidays/#{module_name.to_s.downcase}'
620
- #
621
- # All the definitions are available at https://github.com/alexdunae/holidays
622
- module #{module_name.to_s.upcase} # :nodoc:
623
- def self.defined_regions
624
- [:#{regions.join(', :')}]
179
+ # Returns an array of symbols of all the available holiday regions.
180
+ def regions
181
+ DefinitionFactory.regions_repository.all
625
182
  end
626
183
 
627
- def self.holidays_by_month
628
- {
629
- #{month_strings.join(",\n")}
630
- }
184
+ # Load all available holiday definitions
185
+ def load_all
186
+ available(true).each { |path| require path }
631
187
  end
632
- end
633
-
634
- #{custom_methods}
635
- end
636
-
637
- Holidays.merge_defs(Holidays::#{module_name.to_s.upcase}.defined_regions, Holidays::#{module_name.to_s.upcase}.holidays_by_month)
638
- EOM
639
-
640
- return module_src
641
- end
642
-
643
- def self.generate_test_src(module_name, files, tests)
644
- unless tests.empty?
645
- test_src = ""
646
-
647
- test_src =<<-EndOfTests
648
- # encoding: utf-8
649
- require File.expand_path(File.dirname(__FILE__)) + '/../test_helper'
650
-
651
- # This file is generated by the Ruby Holiday gem.
652
- #
653
- # Definitions loaded: #{files.join(', ')}
654
- class #{module_name.to_s.capitalize}DefinitionTests < Test::Unit::TestCase # :nodoc:
655
188
 
656
- def test_#{module_name.to_s.downcase}
657
- #{tests.join("\n\n")}
658
- end
659
- end
660
- EndOfTests
189
+ # Parses provided holiday definition file(s) and loads them so that they are immediately available.
190
+ def load_custom(*files)
191
+ regions, rules_by_month, custom_methods, tests = DefinitionFactory.file_parser.parse_definition_files(files)
192
+ merge_defs(regions, rules_by_month)
661
193
  end
662
194
 
663
- return test_src
664
- end
665
- end
666
-
667
- # === Extending Ruby's Date class with the Holidays gem
668
- # The Holidays gem automatically extends Ruby's Date class and gives you access
669
- # to three new methods: holiday?, #holidays and #calculate_mday.
670
- #
671
- # ==== Examples
672
- # Lookup Canada Day in the <tt>:ca</tt> region
673
- # Date.civil(2008,7,1).holiday?(:ca)
674
- # => true
675
- #
676
- # Lookup Canada Day in the <tt>:fr</tt> region
677
- # Date.civil(2008,7,1).holiday?(:fr)
678
- # => false
679
- #
680
- # Lookup holidays on North America in January 1.
681
- # Date.civil(2008,1,1).holidays(:ca, :mx, :us, :informal, :observed)
682
- # => [{:name => 'New Year\'s Day'...}]
683
- class Date
684
- include Holidays
685
-
686
- # Get holidays on the current date.
687
- #
688
- # Returns an array of hashes or nil. See Holidays#between for options
689
- # and the output format.
690
- #
691
- # Date.civil('2008-01-01').holidays(:ca_)
692
- # => [{:name => 'New Year\'s Day',...}]
693
- #
694
- # Also available via Holidays#on.
695
- def holidays(*options)
696
- Holidays.on(self, options)
697
- end
698
-
699
- # Check if the current date is a holiday.
700
- #
701
- # Returns true or false.
702
- #
703
- # Date.civil('2008-01-01').holiday?(:ca)
704
- # => true
705
- def holiday?(*options)
706
- holidays = self.holidays(options)
707
- holidays && !holidays.empty?
708
- end
709
-
710
- # Calculate day of the month based on the week number and the day of the
711
- # week.
712
- #
713
- # ==== Parameters
714
- # [<tt>year</tt>] Integer.
715
- # [<tt>month</tt>] Integer from 1-12.
716
- # [<tt>week</tt>] One of <tt>:first</tt>, <tt>:second</tt>, <tt>:third</tt>,
717
- # <tt>:fourth</tt>, <tt>:fifth</tt> or <tt>:last</tt>.
718
- # [<tt>wday</tt>] Day of the week as an integer from 0 (Sunday) to 6
719
- # (Saturday) or as a symbol (e.g. <tt>:monday</tt>).
720
- #
721
- # Returns an integer.
722
- #
723
- # ===== Examples
724
- # First Monday of January, 2008:
725
- # Date.calculate_mday(2008, 1, :first, :monday)
726
- # => 7
727
- #
728
- # Third Thursday of December, 2008:
729
- # Date.calculate_mday(2008, 12, :third, :thursday)
730
- # => 18
731
- #
732
- # Last Monday of January, 2008:
733
- # Date.calculate_mday(2008, 1, :last, 1)
734
- # => 28
735
- #--
736
- # see http://www.irt.org/articles/js050/index.htm
737
- def self.calculate_mday(year, month, week, wday)
738
- raise ArgumentError, "Week parameter must be one of Holidays::WEEKS (provided #{week})." unless WEEKS.include?(week) or WEEKS.has_value?(week)
195
+ # Parses provided holiday definition file(s) and returns strings containing the generated module and test source
196
+ def parse_definition_files_and_return_source(module_name, *files)
197
+ regions, rules_by_month, custom_methods, tests = DefinitionFactory.file_parser.parse_definition_files(files)
198
+ module_src, test_src = DefinitionFactory.source_generator.generate_definition_source(module_name, files, regions, rules_by_month, custom_methods, tests)
739
199
 
740
- unless wday.kind_of?(Numeric) and wday.between?(0,6) or DAY_SYMBOLS.index(wday)
741
- raise ArgumentError, "Wday parameter must be an integer between 0 and 6 or one of Date::DAY_SYMBOLS."
200
+ return module_src, test_src, regions
742
201
  end
743
202
 
744
- week = WEEKS[week] if week.kind_of?(Symbol)
745
- wday = DAY_SYMBOLS.index(wday) if wday.kind_of?(Symbol)
203
+ private
746
204
 
747
- # :first, :second, :third, :fourth or :fifth
748
- if week > 0
749
- return ((week - 1) * 7) + 1 + ((wday - Date.civil(year, month,(week-1)*7 + 1).wday) % 7)
205
+ def get_date(date)
206
+ if date.respond_to?(:to_date)
207
+ date.to_date
208
+ else
209
+ Date.civil(date.year, date.mon, date.mday)
210
+ end
750
211
  end
751
-
752
- days = MONTH_LENGTHS[month-1]
753
-
754
- days = 29 if month == 2 and Date.leap?(year)
755
-
756
- return days - ((Date.civil(year, month, days).wday - wday + 7) % 7) - (7 * (week.abs - 1))
757
212
  end
758
213
  end