holidays 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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