holidays 4.2.0 → 8.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (330) hide show
  1. checksums.yaml +5 -5
  2. data/.gitmodules +3 -0
  3. data/CHANGELOG.md +195 -0
  4. data/Makefile +45 -0
  5. data/README.md +253 -77
  6. data/Rakefile +10 -11
  7. data/bin/console +0 -0
  8. data/bin/setup +1 -0
  9. data/doc/CONTRIBUTING.md +72 -0
  10. data/doc/MAINTAINERS.md +81 -0
  11. data/{REFERENCES → doc/REFERENCES} +0 -0
  12. data/holidays.gemspec +9 -9
  13. data/lib/generated_definitions/MANIFEST +26 -4
  14. data/lib/generated_definitions/REGIONS.rb +3 -1
  15. data/lib/generated_definitions/ar.rb +4 -10
  16. data/lib/generated_definitions/at.rb +3 -9
  17. data/lib/generated_definitions/au.rb +32 -25
  18. data/lib/generated_definitions/be_fr.rb +2 -8
  19. data/lib/generated_definitions/be_nl.rb +2 -8
  20. data/lib/generated_definitions/bg.rb +2 -8
  21. data/lib/generated_definitions/br.rb +2 -8
  22. data/lib/generated_definitions/ca.rb +33 -32
  23. data/lib/generated_definitions/ch.rb +17 -11
  24. data/lib/generated_definitions/cl.rb +38 -12
  25. data/lib/generated_definitions/co.rb +121 -0
  26. data/lib/generated_definitions/cr.rb +2 -8
  27. data/lib/generated_definitions/cz.rb +2 -8
  28. data/lib/generated_definitions/de.rb +11 -14
  29. data/lib/generated_definitions/dk.rb +2 -8
  30. data/lib/generated_definitions/{ecb_target.rb → ecbtarget.rb} +10 -16
  31. data/lib/generated_definitions/ee.rb +36 -0
  32. data/lib/generated_definitions/el.rb +2 -8
  33. data/lib/generated_definitions/es.rb +8 -11
  34. data/lib/generated_definitions/europe.rb +121 -51
  35. data/lib/generated_definitions/federalreserve.rb +34 -0
  36. data/lib/generated_definitions/{federal_reserve.rb → federalreservebanks.rb} +14 -20
  37. data/lib/generated_definitions/fedex.rb +3 -9
  38. data/lib/generated_definitions/fi.rb +2 -8
  39. data/lib/generated_definitions/fr.rb +6 -10
  40. data/lib/generated_definitions/gb.rb +12 -16
  41. data/lib/generated_definitions/ge.rb +41 -0
  42. data/lib/generated_definitions/hk.rb +106 -0
  43. data/lib/generated_definitions/hr.rb +10 -14
  44. data/lib/generated_definitions/hu.rb +4 -9
  45. data/lib/generated_definitions/ie.rb +3 -17
  46. data/lib/generated_definitions/is.rb +3 -9
  47. data/lib/generated_definitions/it.rb +16 -13
  48. data/lib/generated_definitions/jp.rb +47 -36
  49. data/lib/generated_definitions/kr.rb +40 -0
  50. data/lib/generated_definitions/kz.rb +38 -0
  51. data/lib/generated_definitions/li.rb +2 -8
  52. data/lib/generated_definitions/lt.rb +2 -8
  53. data/lib/generated_definitions/lu.rb +35 -0
  54. data/lib/generated_definitions/lv.rb +52 -0
  55. data/lib/generated_definitions/ma.rb +2 -8
  56. data/lib/generated_definitions/mt_en.rb +38 -0
  57. data/lib/generated_definitions/mt_mt.rb +38 -0
  58. data/lib/generated_definitions/mx.rb +6 -12
  59. data/lib/generated_definitions/my.rb +30 -0
  60. data/lib/generated_definitions/nerc.rb +2 -8
  61. data/lib/generated_definitions/ng.rb +33 -0
  62. data/lib/generated_definitions/nl.rb +2 -8
  63. data/lib/generated_definitions/no.rb +2 -8
  64. data/lib/generated_definitions/northamerica.rb +223 -0
  65. data/lib/generated_definitions/nyse.rb +3 -9
  66. data/lib/generated_definitions/nz.rb +2 -9
  67. data/lib/generated_definitions/pe.rb +43 -0
  68. data/lib/generated_definitions/ph.rb +4 -10
  69. data/lib/generated_definitions/pl.rb +2 -8
  70. data/lib/generated_definitions/pt.rb +12 -12
  71. data/lib/generated_definitions/ro.rb +6 -9
  72. data/lib/generated_definitions/rs_cyrl.rb +39 -0
  73. data/lib/generated_definitions/rs_la.rb +39 -0
  74. data/lib/generated_definitions/ru.rb +37 -0
  75. data/lib/generated_definitions/scandinavia.rb +3 -9
  76. data/lib/generated_definitions/se.rb +2 -8
  77. data/lib/generated_definitions/sg.rb +2 -8
  78. data/lib/generated_definitions/si.rb +2 -8
  79. data/lib/generated_definitions/sk.rb +2 -8
  80. data/lib/generated_definitions/southamerica.rb +228 -0
  81. data/lib/generated_definitions/th.rb +36 -0
  82. data/lib/generated_definitions/tn.rb +32 -0
  83. data/lib/generated_definitions/tr.rb +64 -0
  84. data/lib/generated_definitions/ua.rb +37 -0
  85. data/lib/generated_definitions/{united_nations.rb → unitednations.rb} +61 -67
  86. data/lib/generated_definitions/ups.rb +3 -9
  87. data/lib/generated_definitions/us.rb +113 -23
  88. data/lib/generated_definitions/ve.rb +2 -8
  89. data/lib/generated_definitions/vi.rb +6 -11
  90. data/lib/generated_definitions/za.rb +2 -8
  91. data/lib/holidays/core_extensions/date.rb +21 -3
  92. data/lib/holidays/core_extensions/time.rb +23 -0
  93. data/lib/holidays/date_calculator/day_of_month.rb +3 -3
  94. data/lib/holidays/date_calculator/lunar_date.rb +371 -0
  95. data/lib/holidays/date_calculator/weekend_modifier.rb +0 -7
  96. data/lib/holidays/definition/context/function_processor.rb +91 -0
  97. data/lib/holidays/definition/context/generator.rb +37 -96
  98. data/lib/holidays/definition/context/load.rb +29 -0
  99. data/lib/holidays/definition/context/merger.rb +0 -4
  100. data/lib/holidays/definition/decorator/test.rb +37 -0
  101. data/lib/holidays/definition/entity/test.rb +11 -0
  102. data/lib/holidays/definition/generator/module.rb +54 -0
  103. data/lib/holidays/definition/generator/regions.rb +55 -0
  104. data/lib/holidays/definition/generator/test.rb +51 -0
  105. data/lib/holidays/definition/parser/custom_method.rb +3 -5
  106. data/lib/holidays/definition/parser/test.rb +86 -0
  107. data/lib/holidays/definition/repository/cache.rb +23 -9
  108. data/lib/holidays/definition/repository/holidays_by_month.rb +10 -2
  109. data/lib/holidays/definition/repository/proc_result_cache.rb +1 -1
  110. data/lib/holidays/definition/repository/regions.rb +23 -13
  111. data/lib/holidays/definition/validator/custom_method.rb +1 -1
  112. data/lib/holidays/definition/validator/region.rb +2 -11
  113. data/lib/holidays/definition/validator/test.rb +71 -0
  114. data/lib/holidays/errors.rb +5 -0
  115. data/lib/holidays/factory/date_calculator.rb +42 -0
  116. data/lib/holidays/factory/definition.rb +143 -0
  117. data/lib/holidays/factory/finder.rb +70 -0
  118. data/lib/holidays/finder/context/between.rb +45 -0
  119. data/lib/holidays/{use_case → finder}/context/dates_driver_builder.rb +11 -15
  120. data/lib/holidays/finder/context/next_holiday.rb +57 -0
  121. data/lib/holidays/finder/context/parse_options.rb +104 -0
  122. data/lib/holidays/finder/context/search.rb +110 -0
  123. data/lib/holidays/finder/context/year_holiday.rb +57 -0
  124. data/lib/holidays/finder/rules/in_region.rb +31 -0
  125. data/lib/holidays/finder/rules/year_range.rb +58 -0
  126. data/lib/holidays/load_all_definitions.rb +16 -17
  127. data/lib/holidays/version.rb +1 -1
  128. data/lib/holidays.rb +50 -125
  129. data/test/coverage_report.rb +26 -0
  130. data/test/data/test_custom_informal_holidays_defs.yaml +11 -0
  131. data/test/data/test_custom_year_range_holiday_defs.yaml +13 -10
  132. data/test/data/test_invalid_region.rb +15 -0
  133. data/test/data/test_multiple_custom_holiday_defs.yaml +7 -4
  134. data/test/data/test_multiple_regions_with_conflicts_region_1.yaml +38 -0
  135. data/test/data/test_multiple_regions_with_conflicts_region_2.yaml +38 -0
  136. data/test/data/test_region.rb +15 -0
  137. data/test/data/test_single_custom_holiday_defs.yaml +7 -4
  138. data/test/data/test_single_custom_holiday_with_custom_procs.yaml +11 -7
  139. data/test/defs/test_defs_ar.rb +41 -19
  140. data/test/defs/test_defs_at.rb +18 -11
  141. data/test/defs/test_defs_au.rb +185 -126
  142. data/test/defs/test_defs_be_fr.rb +33 -14
  143. data/test/defs/test_defs_be_nl.rb +33 -14
  144. data/test/defs/test_defs_bg.rb +29 -17
  145. data/test/defs/test_defs_br.rb +33 -19
  146. data/test/defs/test_defs_ca.rb +246 -136
  147. data/test/defs/test_defs_ch.rb +35 -23
  148. data/test/defs/test_defs_cl.rb +57 -27
  149. data/test/defs/test_defs_co.rb +113 -0
  150. data/test/defs/test_defs_cr.rb +17 -11
  151. data/test/defs/test_defs_cz.rb +25 -15
  152. data/test/defs/test_defs_de.rb +60 -59
  153. data/test/defs/test_defs_dk.rb +31 -19
  154. data/test/defs/test_defs_ecbtarget.rb +27 -0
  155. data/test/defs/test_defs_ee.rb +41 -0
  156. data/test/defs/test_defs_el.rb +29 -17
  157. data/test/defs/test_defs_es.rb +116 -52
  158. data/test/defs/test_defs_europe.rb +1382 -701
  159. data/test/defs/test_defs_federalreserve.rb +113 -0
  160. data/test/defs/test_defs_federalreservebanks.rb +247 -0
  161. data/test/defs/test_defs_fedex.rb +19 -12
  162. data/test/defs/test_defs_fi.rb +47 -27
  163. data/test/defs/test_defs_fr.rb +31 -18
  164. data/test/defs/test_defs_gb.rb +120 -51
  165. data/test/defs/test_defs_ge.rb +53 -0
  166. data/test/defs/test_defs_hk.rb +59 -0
  167. data/test/defs/test_defs_hr.rb +32 -18
  168. data/test/defs/test_defs_hu.rb +34 -16
  169. data/test/defs/test_defs_ie.rb +41 -25
  170. data/test/defs/test_defs_is.rb +39 -22
  171. data/test/defs/test_defs_it.rb +43 -14
  172. data/test/defs/test_defs_jp.rb +141 -55
  173. data/test/defs/test_defs_kr.rb +37 -0
  174. data/test/defs/test_defs_kz.rb +39 -0
  175. data/test/defs/test_defs_li.rb +20 -20
  176. data/test/defs/test_defs_lt.rb +51 -30
  177. data/test/defs/test_defs_lu.rb +35 -0
  178. data/test/defs/test_defs_lv.rb +90 -0
  179. data/test/defs/test_defs_ma.rb +17 -11
  180. data/test/defs/test_defs_mt_en.rb +41 -0
  181. data/test/defs/test_defs_mt_mt.rb +41 -0
  182. data/test/defs/test_defs_mx.rb +34 -23
  183. data/test/defs/test_defs_my.rb +23 -0
  184. data/test/defs/test_defs_nerc.rb +17 -11
  185. data/test/defs/test_defs_ng.rb +29 -0
  186. data/test/defs/test_defs_nl.rb +21 -13
  187. data/test/defs/test_defs_no.rb +31 -18
  188. data/test/defs/test_defs_northamerica.rb +644 -0
  189. data/test/defs/test_defs_nyse.rb +27 -11
  190. data/test/defs/test_defs_nz.rb +47 -28
  191. data/test/defs/test_defs_pe.rb +47 -0
  192. data/test/defs/test_defs_ph.rb +17 -13
  193. data/test/defs/test_defs_pl.rb +200 -119
  194. data/test/defs/test_defs_pt.rb +35 -15
  195. data/test/defs/test_defs_ro.rb +53 -24
  196. data/test/defs/test_defs_rs_cyrl.rb +46 -0
  197. data/test/defs/test_defs_rs_la.rb +46 -0
  198. data/test/defs/test_defs_ru.rb +34 -0
  199. data/test/defs/test_defs_scandinavia.rb +193 -116
  200. data/test/defs/test_defs_se.rb +47 -28
  201. data/test/defs/test_defs_sg.rb +13 -9
  202. data/test/defs/test_defs_si.rb +93 -24
  203. data/test/defs/test_defs_sk.rb +29 -17
  204. data/test/defs/test_defs_southamerica.rb +307 -0
  205. data/test/defs/test_defs_th.rb +33 -0
  206. data/test/defs/test_defs_tn.rb +27 -0
  207. data/test/defs/test_defs_tr.rb +60 -0
  208. data/test/defs/test_defs_ua.rb +41 -0
  209. data/test/defs/{test_defs_united_nations.rb → test_defs_unitednations.rb} +3 -4
  210. data/test/defs/test_defs_ups.rb +19 -12
  211. data/test/defs/test_defs_us.rb +370 -30
  212. data/test/defs/test_defs_ve.rb +23 -16
  213. data/test/defs/test_defs_vi.rb +10 -6
  214. data/test/defs/test_defs_za.rb +23 -14
  215. data/test/holidays/core_extensions/test_date.rb +3 -2
  216. data/test/holidays/core_extensions/test_date_time.rb +60 -0
  217. data/test/holidays/date_calculator/test_lunar_date.rb +89 -0
  218. data/test/holidays/definition/context/test_function_processor.rb +199 -0
  219. data/test/holidays/definition/context/test_generator.rb +66 -35
  220. data/test/holidays/definition/context/test_load.rb +37 -0
  221. data/test/holidays/definition/decorator/test_test.rb +123 -0
  222. data/test/holidays/definition/generator/test_module.rb +268 -0
  223. data/test/holidays/definition/generator/test_regions.rb +97 -0
  224. data/test/holidays/definition/generator/test_test.rb +113 -0
  225. data/test/holidays/definition/parser/test_custom_method.rb +6 -6
  226. data/test/holidays/definition/parser/test_test.rb +142 -0
  227. data/test/holidays/definition/repository/test_cache.rb +47 -6
  228. data/test/holidays/definition/repository/test_holidays_by_month.rb +121 -1
  229. data/test/holidays/definition/repository/test_proc_result_cache.rb +8 -1
  230. data/test/holidays/definition/repository/test_regions.rb +31 -13
  231. data/test/holidays/definition/validator/test_custom_method.rb +5 -0
  232. data/test/holidays/definition/validator/test_region.rb +16 -12
  233. data/test/holidays/definition/validator/test_test.rb +60 -0
  234. data/test/holidays/finder/context/test_between.rb +172 -0
  235. data/test/holidays/{use_case → finder}/context/test_dates_driver_builder.rb +2 -2
  236. data/test/holidays/finder/context/test_next_holiday.rb +156 -0
  237. data/test/holidays/finder/context/test_parse_options.rb +141 -0
  238. data/test/holidays/finder/context/test_search.rb +232 -0
  239. data/test/holidays/finder/context/test_year_holiday.rb +202 -0
  240. data/test/holidays/finder/rules/test_in_region.rb +42 -0
  241. data/test/holidays/finder/rules/test_year_range.rb +166 -0
  242. data/test/integration/README.md +9 -0
  243. data/test/{test_all_regions.rb → integration/test_all_regions.rb} +18 -4
  244. data/test/integration/test_any_holidays_during_work_week.rb +90 -0
  245. data/test/integration/test_available_regions.rb +23 -0
  246. data/test/{test_custom_holidays.rb → integration/test_custom_holidays.rb} +6 -6
  247. data/test/integration/test_custom_informal_holidays.rb +15 -0
  248. data/test/{test_custom_year_range_holidays.rb → integration/test_custom_year_range_holidays.rb} +1 -9
  249. data/test/{test_holidays.rb → integration/test_holidays.rb} +100 -69
  250. data/test/{test_holidays_between.rb → integration/test_holidays_between.rb} +19 -17
  251. data/test/integration/test_multiple_regions.rb +71 -0
  252. data/test/integration/test_multiple_regions_with_conflict.rb +29 -0
  253. data/test/integration/test_nonstandard_regions.rb +25 -0
  254. data/test/test_helper.rb +7 -3
  255. metadata +204 -132
  256. data/CONTRIBUTING.md +0 -41
  257. data/benchmark.rb +0 -8
  258. data/definitions/README.md +0 -353
  259. data/definitions/ar.yaml +0 -93
  260. data/definitions/at.yaml +0 -72
  261. data/definitions/au.yaml +0 -375
  262. data/definitions/be_fr.yaml +0 -69
  263. data/definitions/be_nl.yaml +0 -69
  264. data/definitions/bg.yaml +0 -127
  265. data/definitions/br.yaml +0 -77
  266. data/definitions/ca.yaml +0 -302
  267. data/definitions/ch.yaml +0 -193
  268. data/definitions/cl.yaml +0 -94
  269. data/definitions/cr.yaml +0 -65
  270. data/definitions/cz.yaml +0 -73
  271. data/definitions/de.yaml +0 -209
  272. data/definitions/dk.yaml +0 -130
  273. data/definitions/ecb_target.yaml +0 -44
  274. data/definitions/el.yaml +0 -84
  275. data/definitions/es.yaml +0 -203
  276. data/definitions/federal_reserve.yaml +0 -114
  277. data/definitions/fedex.yaml +0 -62
  278. data/definitions/fi.yaml +0 -115
  279. data/definitions/fr.yaml +0 -79
  280. data/definitions/gb.yaml +0 -151
  281. data/definitions/hr.yaml +0 -79
  282. data/definitions/hu.yaml +0 -63
  283. data/definitions/ie.yaml +0 -89
  284. data/definitions/index.yaml +0 -57
  285. data/definitions/is.yaml +0 -146
  286. data/definitions/it.yaml +0 -67
  287. data/definitions/jp.yaml +0 -316
  288. data/definitions/li.yaml +0 -107
  289. data/definitions/lt.yaml +0 -89
  290. data/definitions/ma.yaml +0 -52
  291. data/definitions/mx.yaml +0 -106
  292. data/definitions/nerc.yaml +0 -51
  293. data/definitions/nl.yaml +0 -73
  294. data/definitions/no.yaml +0 -90
  295. data/definitions/north_america_informal.yaml +0 -61
  296. data/definitions/nyse.yaml +0 -64
  297. data/definitions/nz.yaml +0 -163
  298. data/definitions/ph.yaml +0 -94
  299. data/definitions/pl.yaml +0 -320
  300. data/definitions/pt.yaml +0 -69
  301. data/definitions/ro.yaml +0 -78
  302. data/definitions/se.yaml +0 -120
  303. data/definitions/sg.yaml +0 -56
  304. data/definitions/si.yaml +0 -86
  305. data/definitions/sk.yaml +0 -80
  306. data/definitions/united_nations.yaml +0 -189
  307. data/definitions/ups.yaml +0 -62
  308. data/definitions/us.yaml +0 -103
  309. data/definitions/ve.yaml +0 -74
  310. data/definitions/vi.yaml +0 -29
  311. data/definitions/za.yaml +0 -80
  312. data/lib/generated_definitions/north_america.rb +0 -126
  313. data/lib/holidays/date_calculator_factory.rb +0 -35
  314. data/lib/holidays/definition_factory.rb +0 -86
  315. data/lib/holidays/option/context/parse_options.rb +0 -106
  316. data/lib/holidays/option_factory.rb +0 -15
  317. data/lib/holidays/use_case/context/between.rb +0 -45
  318. data/lib/holidays/use_case/context/context_common.rb +0 -123
  319. data/lib/holidays/use_case/context/next_holiday.rb +0 -54
  320. data/lib/holidays/use_case_factory.rb +0 -31
  321. data/test/defs/test_defs_ecb_target.rb +0 -23
  322. data/test/defs/test_defs_federal_reserve.rb +0 -69
  323. data/test/defs/test_defs_north_america.rb +0 -195
  324. data/test/holidays/option/context/test_parse_options.rb +0 -74
  325. data/test/holidays/test_date_calculator_factory.rb +0 -27
  326. data/test/holidays/test_definition_factory.rb +0 -49
  327. data/test/holidays/test_option_factory.rb +0 -9
  328. data/test/holidays/test_use_case_factory.rb +0 -13
  329. data/test/holidays/use_case/context/test_between.rb +0 -77
  330. data/test/test_multiple_regions.rb +0 -24
@@ -0,0 +1,58 @@
1
+ module Holidays
2
+ module Finder
3
+ module Rules
4
+ class YearRange
5
+ class << self
6
+ UNTIL = :until
7
+ FROM = :from
8
+ LIMITED = :limited
9
+ BETWEEN = :between
10
+
11
+ def call(target_year, year_range_defs)
12
+ validate!(target_year, year_range_defs)
13
+
14
+ operator = year_range_defs.keys.first
15
+ rule_value = year_range_defs[operator]
16
+
17
+ case operator
18
+ when UNTIL
19
+ matched = target_year <= rule_value
20
+ when FROM
21
+ matched = target_year >= rule_value
22
+ when LIMITED
23
+ matched = rule_value.include?(target_year)
24
+ when BETWEEN
25
+ matched = rule_value.cover?(target_year)
26
+ else
27
+ matched = false
28
+ end
29
+
30
+ matched
31
+ end
32
+
33
+ private
34
+
35
+ def validate!(target_year, year_ranges)
36
+ raise ArgumentError.new("target_year must be a number") unless target_year.is_a?(Integer)
37
+ raise ArgumentError.new("year_ranges cannot be missing") if year_ranges.nil? || year_ranges.empty?
38
+ raise ArgumentError.new("year_ranges must contain a hash with a single operator") unless year_ranges.is_a?(Hash) && year_ranges.size == 1
39
+
40
+ operator = year_ranges.keys.first
41
+ value = year_ranges[operator]
42
+
43
+ raise ArgumentError.new("Invalid operator found: '#{operator}'") unless [UNTIL, FROM, LIMITED, BETWEEN].include?(operator)
44
+
45
+ case operator
46
+ when UNTIL, FROM
47
+ raise ArgumentError.new("#{UNTIL} and #{FROM} operator value must be a number, received: '#{value}'") unless value.is_a?(Integer)
48
+ when LIMITED
49
+ raise ArgumentError.new(":limited operator value must be an array containing at least one integer value, received: '#{value}'") unless value.is_a?(Array) && value.size >= 1 && value.all? { |v| v.is_a?(Integer) }
50
+ when BETWEEN
51
+ raise ArgumentError.new(":between operator value must be a range, received: '#{value}'") unless value.is_a?(Range)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,22 +1,14 @@
1
1
  module Holidays
2
+ #TODO This file should be renamed. It's no longer about definitions, really.
2
3
  class LoadAllDefinitions
3
4
  class << self
4
5
  def call
5
- path = File.expand_path(File.dirname(__FILE__)) + "/../#{Holidays::DEFINITIONS_PATH}/"
6
-
7
- Dir.foreach(path) do |item|
8
- next if item == '.' or item == '..'
9
-
10
- target = path+item
11
- next if File.extname(target) != '.rb'
12
-
13
- require target
14
- end
15
-
16
6
  #FIXME I need a better way to do this. I'm thinking of putting these 'common' methods
17
7
  # into some kind of definition file so it can be loaded automatically but I'm afraid
18
8
  # of making that big of a breaking API change since these are public. For the time
19
9
  # being I'll load them manually like this.
10
+ #
11
+ # NOTE: These are no longer public! We can do whatever we want here!
20
12
  global_methods = {
21
13
  "easter(year)" => gregorian_easter.method(:calculate_easter_for).to_proc,
22
14
  "orthodox_easter(year)" => gregorian_easter.method(:calculate_orthodox_easter_for).to_proc,
@@ -28,29 +20,36 @@ module Holidays
28
20
  "to_weekday_if_weekend(date)" => weekend_modifier.method(:to_weekday_if_weekend).to_proc,
29
21
  "calculate_day_of_month(year, month, day, wday)" => day_of_month_calculator.method(:call).to_proc,
30
22
  "to_weekday_if_boxing_weekend_from_year_or_to_tuesday_if_monday(year)" => weekend_modifier.method(:to_weekday_if_boxing_weekend_from_year_or_to_tuesday_if_monday).to_proc,
31
- "xmas_to_weekday_if_weekend(year)" => weekend_modifier.method(:xmas_to_weekday_if_weekend).to_proc,
32
23
  "to_tuesday_if_sunday_or_monday_if_saturday(date)" => weekend_modifier.method(:to_tuesday_if_sunday_or_monday_if_saturday).to_proc,
24
+ "lunar_to_solar(year, month, day, region)" => lunar_date.method(:to_solar).to_proc,
33
25
  }
34
26
 
35
- Holidays::DefinitionFactory.custom_methods_repository.add(global_methods)
27
+ Factory::Definition.custom_methods_repository.add(global_methods)
28
+
29
+ static_regions_definition = "#{Holidays::DEFINITIONS_PATH}/REGIONS.rb"
30
+ require static_regions_definition
36
31
  end
37
32
 
38
33
  private
39
34
 
40
35
  def gregorian_easter
41
- DateCalculatorFactory::Easter::Gregorian.easter_calculator
36
+ Factory::DateCalculator::Easter::Gregorian.easter_calculator
42
37
  end
43
38
 
44
39
  def julian_easter
45
- DateCalculatorFactory::Easter::Julian.easter_calculator
40
+ Factory::DateCalculator::Easter::Julian.easter_calculator
46
41
  end
47
42
 
48
43
  def weekend_modifier
49
- DateCalculatorFactory.weekend_modifier
44
+ Factory::DateCalculator.weekend_modifier
50
45
  end
51
46
 
52
47
  def day_of_month_calculator
53
- DateCalculatorFactory.day_of_month_calculator
48
+ Factory::DateCalculator.day_of_month_calculator
49
+ end
50
+
51
+ def lunar_date
52
+ Factory::DateCalculator.lunar_date
54
53
  end
55
54
  end
56
55
  end
@@ -1,3 +1,3 @@
1
1
  module Holidays
2
- VERSION = '4.2.0'
2
+ VERSION = '8.4.1'
3
3
  end
data/lib/holidays.rb CHANGED
@@ -3,51 +3,12 @@ $:.unshift File.dirname(__FILE__)
3
3
 
4
4
  require 'date'
5
5
  require 'digest/md5'
6
- require 'holidays/definition_factory'
7
- require 'holidays/date_calculator_factory'
8
- require 'holidays/option_factory'
9
- require 'holidays/use_case_factory'
6
+ require 'holidays/factory/definition'
7
+ require 'holidays/factory/date_calculator'
8
+ require 'holidays/factory/finder'
10
9
  require 'holidays/errors'
11
10
  require 'holidays/load_all_definitions'
12
11
 
13
- # == Region options
14
- # Holidays can be defined as belonging to one or more regions and sub regions.
15
- # The Holidays#on, Holidays#between, Date#holidays and Date#holiday? methods
16
- # each allow you to specify a specific region.
17
- #
18
- # There are several different ways that you can specify a region:
19
- #
20
- # [<tt>:region</tt>]
21
- # By region. For example, return holidays in the Canada with <tt>:ca</tt>.
22
- # [<tt>:region_</tt>]
23
- # By region and sub regions. For example, return holidays in Germany
24
- # and all its sub regions with <tt>:de_</tt>.
25
- # [<tt>:region_sub</tt>]
26
- # By sub region. Return national holidays in Spain plus holidays in Spain's
27
- # Valencia region with <tt>:es_v</tt>.
28
- # [<tt>:any</tt>]
29
- # Any region. Return holidays from any loaded region.
30
- #
31
- #
32
- # You can load all the available holiday definition sets by running
33
- # Holidays.load_all
34
- # == Other options
35
- # [<tt>:observed</tt>] Return holidays on the day they are observed (e.g. on a Monday if they fall on a Sunday).
36
- # [<tt>:informal</tt>] Include informal holidays (e.g. Valentine's Day)
37
- #
38
- # == Examples
39
- # Return all holidays in the <tt>:ca</tt> and <tt>:us</tt> regions on the day that they are
40
- # observed.
41
- #
42
- # Holidays.between(from, to, :ca, :us, :observed)
43
- #
44
- # Return all holidays in <tt>:ca</tt> and any <tt>:ca</tt> sub-region.
45
- #
46
- # Holidays.between(from, to, :ca_)
47
- #
48
- # Return all holidays in <tt>:ca_bc</tt> sub-region (which includes the <tt>:ca</tt>), including informal holidays.
49
- #
50
- # Holidays.between(from, to, :ca_bc, :informal)
51
12
  module Holidays
52
13
  WEEKS = {:first => 1, :second => 2, :third => 3, :fourth => 4, :fifth => 5, :last => -1, :second_last => -2, :third_last => -3}
53
14
  MONTH_LENGTHS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
@@ -57,52 +18,19 @@ module Holidays
57
18
  FULL_DEFINITIONS_PATH = File.expand_path(File.dirname(__FILE__) + "/#{DEFINITIONS_PATH}")
58
19
 
59
20
  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)
21
+ def any_holidays_during_work_week?(date, *options)
22
+ monday = date - (date.wday - 1)
23
+ friday = date + (5 - date.wday)
24
+
25
+ holidays = between(monday, friday, *options)
26
+
27
+ holidays && holidays.count > 0
71
28
  end
72
29
 
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 any_holidays_during_work_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?
30
+ def on(date, *options)
31
+ between(date, date, *options)
88
32
  end
89
33
 
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
34
  def between(start_date, end_date, *options)
107
35
  raise ArgumentError unless start_date && end_date
108
36
 
@@ -112,35 +40,18 @@ module Holidays
112
40
 
113
41
  start_date, end_date = get_date(start_date), get_date(end_date)
114
42
 
115
- if cached_holidays = definition_cache_repository.find(start_date, end_date, options)
43
+ raise ArgumentError if end_date < start_date
44
+
45
+ if cached_holidays = Factory::Definition.cache_repository.find(start_date, end_date, options)
116
46
  return cached_holidays
117
47
  end
118
48
 
119
- regions, observed, informal = OptionFactory.parse_options.call(options)
120
- date_driver_hash = UseCaseFactory.dates_driver_builder.call(start_date, end_date)
121
-
122
- UseCaseFactory.between.call(start_date, end_date, date_driver_hash, regions, observed, informal)
49
+ Factory::Finder.between.call(start_date, end_date, options)
123
50
  end
124
51
 
125
- # Get next holidays occuring from date, inclusively.
126
- #
127
- # Returns an array of hashes or nil.
128
- #
129
- # Incoming arguments are below:
130
- # [<tt>holidays_count</tt>] Ruby Numeric object. This is the number of holidays to return
131
- # [<tt>options</tt>] One or more region symbols, <tt>:informal</tt> and/or <tt>:observed</tt>.
132
- # [<tt>from_date</tt>] Ruby Date object. This is an optional param, defaulted today.
133
- #
134
- # ==== Example
135
- # Date.today
136
- # => Tue, 23 Feb 2016
137
- #
138
- # regions = [:us, :informal]
139
- #
140
- # Holidays.next_holidays(3, regions)
141
- # => [{:name => "St. Patrick's Day",...},
142
- # {:name => "Good Friday",...},
143
- # {:name => "Easter Sunday",...}]
52
+ #FIXME All other methods start with a date and require a date. For the next
53
+ # major version bump we should take the opportunity to change this
54
+ # signature to match, e.g. next_holidays(from_date, count, options)
144
55
  def next_holidays(holidays_count, options, from_date = Date.today)
145
56
  raise ArgumentError unless holidays_count
146
57
  raise ArgumentError if options.empty?
@@ -150,42 +61,60 @@ module Holidays
150
61
  from_date = from_date.new_offset(0) + from_date.offset if from_date.respond_to?(:new_offset)
151
62
 
152
63
  from_date = get_date(from_date)
153
- regions, observed, informal = OptionFactory.parse_options.call(options)
154
64
 
155
- # This could be smarter but I don't have any evidence that just checking for
156
- # the next 12 months will cause us issues. If it does we can implement something
157
- # smarter here to check in smaller increments.
158
- date_driver_hash = UseCaseFactory.dates_driver_builder.call(from_date, from_date >> 12)
65
+ Factory::Finder.next_holiday.call(holidays_count, from_date, options)
66
+ end
67
+
68
+ #FIXME All other methods start with a date and require a date. For the next
69
+ # major version bump we should take the opportunity to change this
70
+ # signature to match, e.g. year_holidays(from_date, options)
71
+ def year_holidays(options, from_date = Date.today)
72
+ raise ArgumentError if options.empty?
73
+ raise ArgumentError unless options.is_a?(Array)
74
+
75
+ # remove the timezone
76
+ from_date = from_date.new_offset(0) + from_date.offset if from_date.respond_to?(:new_offset)
77
+ from_date = get_date(from_date)
159
78
 
160
- UseCaseFactory.next_holiday.call(holidays_count, from_date, date_driver_hash, regions, observed, informal)
79
+ Factory::Finder.year_holiday.call(from_date, options)
161
80
  end
162
81
 
163
- # Allows a developer to explicitly calculate and cache holidays within a given period
164
82
  def cache_between(start_date, end_date, *options)
165
83
  start_date, end_date = get_date(start_date), get_date(end_date)
166
84
  cache_data = between(start_date, end_date, *options)
167
85
 
168
- definition_cache_repository.cache_between(start_date, end_date, cache_data, options)
86
+ Factory::Definition.cache_repository.cache_between(start_date, end_date, cache_data, options)
169
87
  end
170
88
 
171
- # Returns an array of symbols of all the available holiday regions.
172
89
  def available_regions
173
90
  Holidays::REGIONS
174
91
  end
175
92
 
176
- # Parses provided holiday definition file(s) and loads them so that they are immediately available.
177
93
  def load_custom(*files)
178
- regions, rules_by_month, custom_methods, tests = DefinitionFactory.file_parser.parse_definition_files(files)
94
+ regions, rules_by_month, custom_methods, _ = Factory::Definition.file_parser.parse_definition_files(files)
179
95
 
180
96
  custom_methods.each do |method_key, method_entity|
181
- custom_methods[method_key] = Holidays::DefinitionFactory.custom_method_proc_decorator.call(method_entity)
97
+ custom_methods[method_key] = Factory::Definition.custom_method_proc_decorator.call(method_entity)
182
98
  end
183
99
 
184
- DefinitionFactory.merger.call(regions, rules_by_month, custom_methods)
100
+ Factory::Definition.merger.call(regions, rules_by_month, custom_methods)
185
101
 
186
102
  rules_by_month
187
103
  end
188
104
 
105
+ def load_all
106
+ path = FULL_DEFINITIONS_PATH + "/"
107
+
108
+ Dir.foreach(path) do |item|
109
+ next if item == '.' or item == '..'
110
+
111
+ target = path+item
112
+ next if File.extname(target) != '.rb'
113
+
114
+ require target
115
+ end
116
+ end
117
+
189
118
  private
190
119
 
191
120
  def get_date(date)
@@ -195,10 +124,6 @@ module Holidays
195
124
  Date.civil(date.year, date.mon, date.mday)
196
125
  end
197
126
  end
198
-
199
- def definition_cache_repository
200
- DefinitionFactory.cache_repository
201
- end
202
127
  end
203
128
  end
204
129
 
@@ -0,0 +1,26 @@
1
+ require 'simplecov'
2
+
3
+ # For reasons I don't understand jruby implementations report lower coverage
4
+ # than other ruby versions. Ruby 2.5.3, for instance, is at 92%.
5
+ #
6
+ # We set the floor based on jruby so that all automated tests pass on Travis CI.
7
+ SimpleCov.minimum_coverage 89
8
+
9
+ SimpleCov.add_filter [
10
+ # Apparently simplecov doesn't automatically filter 'spec' or 'test' so we
11
+ # have to do it manually.
12
+ 'test',
13
+
14
+ # Only filtered because I tend to not see value in testing factories.
15
+ 'lib/holidays/factory/',
16
+
17
+ # jruby coverage flips out here and doesn't count much of the large date
18
+ # arrays used by this class. This results in an extremely low reported
19
+ # coverage for this specific file but only in jruby, not other ruby versions.
20
+ # Since it obliterates coverage percentages I'll filter it until I can come
21
+ # up with a solution.
22
+ 'lib/holidays/date_calculator/lunar_date.rb',
23
+ ]
24
+
25
+ SimpleCov.coverage_dir 'reports/coverage'
26
+ SimpleCov.start
@@ -0,0 +1,11 @@
1
+ ---
2
+ months:
3
+ 1:
4
+ - name: informal day with type as string
5
+ regions: [custom_informal]
6
+ mday: 1
7
+ type: informal
8
+ - name: informal day with type as symbol
9
+ regions: [custom_informal]
10
+ mday: 5
11
+ type: :informal
@@ -4,25 +4,28 @@ months:
4
4
  regions: [custom_year_range_file]
5
5
  mday: 1
6
6
  year_ranges:
7
- - after: 2016
7
+ from: 2016
8
8
  - name: before_year
9
9
  regions: [custom_year_range_file]
10
10
  mday: 2
11
11
  year_ranges:
12
- - before: 2017
12
+ until: 2017
13
13
  - name: between_year
14
14
  regions: [custom_year_range_file]
15
15
  mday: 3
16
16
  year_ranges:
17
- - between: 2016..2018
17
+ between:
18
+ start: 2016
19
+ end: 2018
18
20
  - name: limited_year
19
21
  regions: [custom_year_range_file]
20
22
  mday: 4
21
23
  year_ranges:
22
- - limited: [2016,2018,2019]
23
- - name: multiple_conditions
24
- regions: [custom_year_range_file]
25
- mday: 5
26
- year_ranges:
27
- - before: 2015
28
- - after: 2017
24
+ limited: [2016,2018,2019]
25
+
26
+ tests:
27
+ - given:
28
+ date: '2017-01-01'
29
+ regions: [custom_year_range_file]
30
+ expect:
31
+ name: "after_year"
@@ -0,0 +1,15 @@
1
+ module Holidays
2
+ module BAD_REGION_NAME
3
+ def self.defined_regions
4
+ [:test_region]
5
+ end
6
+
7
+ def self.holidays_by_month
8
+ {}
9
+ end
10
+
11
+ def self.custom_methods
12
+ {}
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,10 @@ months:
3
3
  - name: Company Founding!
4
4
  regions: [custom_multiple_files]
5
5
  mday: 5
6
- tests: |
7
- {Date.civil(2013,10,5) => 'Company Founding!'}.each do |date, name|
8
- assert_equal name, (Holidays.on(date, :custom_multiple_files)[0] || {})[:name]
9
- end
6
+
7
+ tests:
8
+ - given:
9
+ date: "2013-10-5"
10
+ regions: ["custom_multiple_files"]
11
+ expect:
12
+ name: "Company Founding!"
@@ -0,0 +1,38 @@
1
+ months:
2
+ 0:
3
+ - name: With Function Modifier
4
+ regions: [multiple_with_conflict_1]
5
+ function: easter(year)
6
+ function_modifier: 60
7
+ - name: With Function Only Different Function Name
8
+ regions: [multiple_with_conflict_1]
9
+ function: conflict_custom_method_1(year)
10
+ - name: With Function Only Same Function Name
11
+ regions: [multiple_with_conflict_1]
12
+ function: conflict_custom_method_identical_name_between_regions(year)
13
+ - name: With Function Only Same Function Name - Region 1
14
+ regions: [multiple_with_conflict_1]
15
+ function: conflict_custom_method_identical_name_between_regions_but_different_holiday_names(year)
16
+ 1:
17
+ - name: New Year's Day
18
+ regions: [multiple_with_conflict_1]
19
+ mday: 1
20
+ observed: to_monday_if_weekend(date)
21
+ 10:
22
+ - name: Testing Conflict Month 10
23
+ regions: [multiple_with_conflict_1]
24
+ mday: 5
25
+
26
+ methods:
27
+ conflict_custom_method_1:
28
+ arguments: year
29
+ ruby: |
30
+ Date.civil(year, 8, 1)
31
+ conflict_custom_method_identical_name_between_regions:
32
+ arguments: year
33
+ ruby: |
34
+ Date.civil(year, 9, 1)
35
+ conflict_custom_method_identical_name_between_regions_but_different_holiday_names:
36
+ arguments: year
37
+ ruby: |
38
+ Date.civil(year, 9, 15)
@@ -0,0 +1,38 @@
1
+ months:
2
+ 0:
3
+ - name: With Function Modifier
4
+ regions: [multiple_with_conflict_2]
5
+ function: easter(year)
6
+ function_modifier: 64
7
+ - name: With Function Only Different Function Name
8
+ regions: [multiple_with_conflict_2]
9
+ function: conflict_custom_method_2(year)
10
+ - name: With Function Only Same Function Name
11
+ regions: [multiple_with_conflict_2]
12
+ function: conflict_custom_method_identical_name_between_regions(year)
13
+ - name: With Function Only Same Function Name - Region 2
14
+ regions: [multiple_with_conflict_2]
15
+ function: conflict_custom_method_identical_name_between_regions_but_different_holiday_names(year)
16
+ 1:
17
+ - name: New Year's Day
18
+ regions: [multiple_with_conflict_2]
19
+ mday: 1
20
+ observed: to_tuesday_if_sunday_or_monday_if_saturday(date)
21
+ 10:
22
+ - name: Testing Conflict Month 10
23
+ regions: [multiple_with_conflict_2]
24
+ mday: 7
25
+
26
+ methods:
27
+ conflict_custom_method_2:
28
+ arguments: year
29
+ ruby: |
30
+ Date.civil(year, 12, 1)
31
+ conflict_custom_method_identical_name_between_regions:
32
+ arguments: year
33
+ ruby: |
34
+ Date.civil(year, 11, 1)
35
+ conflict_custom_method_identical_name_between_regions_but_different_holiday_names:
36
+ arguments: year
37
+ ruby: |
38
+ Date.civil(year, 11, 15)
@@ -0,0 +1,15 @@
1
+ module Holidays
2
+ module TEST_REGION
3
+ def self.defined_regions
4
+ [:test_region, :test_region2]
5
+ end
6
+
7
+ def self.holidays_by_month
8
+ {}
9
+ end
10
+
11
+ def self.custom_methods
12
+ {}
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,10 @@ months:
3
3
  - name: Company Founding
4
4
  regions: [custom_single_file]
5
5
  mday: 20
6
- tests: |
7
- {Date.civil(2013,6,20) => 'Company Founding'}.each do |date, name|
8
- assert_equal name, (Holidays.on(date, :custom_single_file)[0] || {})[:name]
9
- end
6
+
7
+ tests:
8
+ - given:
9
+ date: '2013-06-20'
10
+ regions: [custom_single_file]
11
+ expect:
12
+ name: "Company Founding"
@@ -14,11 +14,15 @@ methods:
14
14
  source: |
15
15
  d = Date.civil(year, month, 1)
16
16
  d + 2
17
- tests: |
18
- {Date.civil(2013,6,20) => 'Company Founding'}.each do |date, name|
19
- assert_equal name, (Holidays.on(date, :custom_single_file)[0] || {})[:name]
20
- end
21
17
 
22
- {Date.civil(2015, 1, 1) => 'Custom Holiday'}.each do |date, name|
23
- assert_equal name, (Holidays.on(date, :custom_single_file)[0] || {}[:name]
24
- end
18
+ tests:
19
+ - given:
20
+ date: '2013-06-20'
21
+ regions: [custom_single_file]
22
+ expect:
23
+ name: "Company Founding"
24
+ - given:
25
+ date: '2015-01-01'
26
+ regions: [custom_single_file]
27
+ expect:
28
+ name: "Custom Holiday"