holidays 11.7.0 → 11.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +11 -8
  4. data/lib/generated_definitions/REGIONS.rb +3 -3
  5. data/lib/generated_definitions/ar.rb +1 -1
  6. data/lib/generated_definitions/at.rb +1 -1
  7. data/lib/generated_definitions/au.rb +2 -2
  8. data/lib/generated_definitions/be_fr.rb +1 -1
  9. data/lib/generated_definitions/be_nl.rb +1 -1
  10. data/lib/generated_definitions/bg.rb +1 -1
  11. data/lib/generated_definitions/br.rb +1 -1
  12. data/lib/generated_definitions/by.rb +1 -1
  13. data/lib/generated_definitions/ca.rb +1 -1
  14. data/lib/generated_definitions/ch.rb +1 -1
  15. data/lib/generated_definitions/cl.rb +3 -2
  16. data/lib/generated_definitions/cn.rb +1 -1
  17. data/lib/generated_definitions/co.rb +1 -1
  18. data/lib/generated_definitions/cr.rb +1 -1
  19. data/lib/generated_definitions/cy.rb +1 -1
  20. data/lib/generated_definitions/cz.rb +1 -1
  21. data/lib/generated_definitions/de.rb +1 -1
  22. data/lib/generated_definitions/dk.rb +1 -1
  23. data/lib/generated_definitions/ecbtarget.rb +1 -1
  24. data/lib/generated_definitions/ee.rb +1 -1
  25. data/lib/generated_definitions/es.rb +1 -1
  26. data/lib/generated_definitions/europe.rb +1 -1
  27. data/lib/generated_definitions/federalreserve.rb +1 -1
  28. data/lib/generated_definitions/federalreservebanks.rb +1 -1
  29. data/lib/generated_definitions/fedex.rb +1 -1
  30. data/lib/generated_definitions/fi.rb +1 -1
  31. data/lib/generated_definitions/fr.rb +1 -1
  32. data/lib/generated_definitions/gb.rb +1 -1
  33. data/lib/generated_definitions/ge.rb +1 -1
  34. data/lib/generated_definitions/gr.rb +1 -1
  35. data/lib/generated_definitions/hk.rb +1 -1
  36. data/lib/generated_definitions/hr.rb +1 -1
  37. data/lib/generated_definitions/hu.rb +1 -1
  38. data/lib/generated_definitions/ie.rb +1 -1
  39. data/lib/generated_definitions/il.rb +1 -1
  40. data/lib/generated_definitions/in.rb +1 -1
  41. data/lib/generated_definitions/is.rb +1 -1
  42. data/lib/generated_definitions/it.rb +1 -1
  43. data/lib/generated_definitions/jp.rb +1 -1
  44. data/lib/generated_definitions/ke.rb +1 -1
  45. data/lib/generated_definitions/kr.rb +1 -1
  46. data/lib/generated_definitions/kz.rb +1 -1
  47. data/lib/generated_definitions/li.rb +1 -1
  48. data/lib/generated_definitions/lt.rb +1 -1
  49. data/lib/generated_definitions/lu.rb +1 -1
  50. data/lib/generated_definitions/lv.rb +1 -1
  51. data/lib/generated_definitions/ma.rb +1 -1
  52. data/lib/generated_definitions/mc.rb +1 -1
  53. data/lib/generated_definitions/mt_en.rb +1 -1
  54. data/lib/generated_definitions/mt_mt.rb +1 -1
  55. data/lib/generated_definitions/mx.rb +1 -1
  56. data/lib/generated_definitions/my.rb +1 -1
  57. data/lib/generated_definitions/nerc.rb +1 -1
  58. data/lib/generated_definitions/ng.rb +1 -1
  59. data/lib/generated_definitions/nl.rb +1 -1
  60. data/lib/generated_definitions/no.rb +1 -1
  61. data/lib/generated_definitions/northamerica.rb +4 -3
  62. data/lib/generated_definitions/nyse.rb +1 -1
  63. data/lib/generated_definitions/nz.rb +1 -1
  64. data/lib/generated_definitions/pe.rb +1 -1
  65. data/lib/generated_definitions/ph.rb +1 -1
  66. data/lib/generated_definitions/pl.rb +1 -1
  67. data/lib/generated_definitions/pt.rb +1 -1
  68. data/lib/generated_definitions/ro.rb +1 -1
  69. data/lib/generated_definitions/rs_cyrl.rb +1 -1
  70. data/lib/generated_definitions/rs_la.rb +1 -1
  71. data/lib/generated_definitions/ru.rb +1 -1
  72. data/lib/generated_definitions/scandinavia.rb +1 -1
  73. data/lib/generated_definitions/se.rb +1 -1
  74. data/lib/generated_definitions/sg.rb +1 -1
  75. data/lib/generated_definitions/si.rb +1 -1
  76. data/lib/generated_definitions/sk.rb +1 -1
  77. data/lib/generated_definitions/southamerica.rb +2 -1
  78. data/lib/generated_definitions/th.rb +1 -1
  79. data/lib/generated_definitions/tn.rb +1 -1
  80. data/lib/generated_definitions/tr.rb +4 -1
  81. data/lib/generated_definitions/tsx.rb +1 -1
  82. data/lib/generated_definitions/ua.rb +1 -1
  83. data/lib/generated_definitions/unitednations.rb +1 -1
  84. data/lib/generated_definitions/ups.rb +1 -1
  85. data/lib/generated_definitions/us.rb +4 -3
  86. data/lib/generated_definitions/ve.rb +1 -1
  87. data/lib/generated_definitions/vn.rb +1 -1
  88. data/lib/generated_definitions/za.rb +1 -1
  89. data/lib/holidays/core_extensions/date.rb +11 -3
  90. data/lib/holidays/date_calculator/hijri_date.rb +10 -7
  91. data/lib/holidays/date_calculator/lunar_date.rb +269 -83
  92. data/lib/holidays/definition/context/generator.rb +27 -2
  93. data/lib/holidays/definition/custom_methods/cl.rb +8 -0
  94. data/lib/holidays/definition/custom_methods/cn.rb +7 -1
  95. data/lib/holidays/definition/custom_methods/registry.rb +2 -0
  96. data/lib/holidays/definition/custom_methods/tr.rb +20 -2
  97. data/lib/holidays/definition/generator/module.rb +1 -1
  98. data/lib/holidays/definition/repository/holidays_by_month.rb +2 -1
  99. data/lib/holidays/errors.rb +5 -0
  100. data/lib/holidays/finder/context/search.rb +67 -23
  101. data/lib/holidays/finder/context/year_holiday.rb +6 -8
  102. data/lib/holidays/finder/rules/in_region.rb +19 -14
  103. data/lib/holidays/version.rb +1 -1
  104. metadata +1 -1
@@ -12,6 +12,10 @@ module Holidays
12
12
  # day (2016 Ramazan Bayramı, two days) before the arithmetic result;
13
13
  # DIYANET_OVERRIDES pins those years to the proclaimed date. Years with
14
14
  # no entry fall back to the calculation. See definitions#377.
15
+ #
16
+ # ramadan_feast only returns the first occurrence of a Gregorian year;
17
+ # ramadan_feast_second_in_year returns the second one in the rare years
18
+ # that have two (2033), and nil otherwise.
15
19
  module TR
16
20
  SHAWWAL = 10
17
21
  DHU_AL_HIJJAH = 12
@@ -19,7 +23,7 @@ module Holidays
19
23
  # Proclaimed first days that differ from the arithmetic calendar,
20
24
  # keyed by [feast, gregorian_year]. Sourced from the Diyanet dates
21
25
  # that the definition file previously carried as a fixed table, plus
22
- # https://vakithesaplama.diyanet.gov.tr/resmitatiller.php for 2021-2030.
26
+ # https://vakithesaplama.diyanet.gov.tr/resmitatiller.php for 2021-2035.
23
27
  DIYANET_OVERRIDES = {
24
28
  [:ramadan_feast, 2014] => Date.new(2014, 7, 28),
25
29
  [:ramadan_feast, 2015] => Date.new(2015, 7, 17),
@@ -32,6 +36,9 @@ module Holidays
32
36
  [:ramadan_feast, 2027] => Date.new(2027, 3, 9),
33
37
  [:ramadan_feast, 2028] => Date.new(2028, 2, 26),
34
38
  [:ramadan_feast, 2030] => Date.new(2030, 2, 4),
39
+ [:ramadan_feast, 2031] => Date.new(2031, 1, 24),
40
+ [:ramadan_feast, 2033] => Date.new(2033, 1, 2),
41
+ [:ramadan_feast, 2035] => Date.new(2035, 12, 1),
35
42
 
36
43
  [:sacrifice_feast, 2014] => Date.new(2014, 10, 4),
37
44
  [:sacrifice_feast, 2016] => Date.new(2016, 9, 12),
@@ -44,6 +51,8 @@ module Holidays
44
51
  [:sacrifice_feast, 2025] => Date.new(2025, 6, 6),
45
52
  [:sacrifice_feast, 2027] => Date.new(2027, 5, 16),
46
53
  [:sacrifice_feast, 2030] => Date.new(2030, 4, 13),
54
+ [:sacrifice_feast, 2031] => Date.new(2031, 4, 2),
55
+ [:sacrifice_feast, 2033] => Date.new(2033, 3, 11),
47
56
  }.freeze
48
57
 
49
58
  class << self
@@ -51,6 +60,11 @@ module Holidays
51
60
  feast(:ramadan_feast, year, SHAWWAL, 1)
52
61
  end
53
62
 
63
+ def ramadan_feast_second_in_year(year)
64
+ occurrences = hijri.gregorian_year_occurrences(year, SHAWWAL, 1)
65
+ occurrences.last if occurrences.size > 1
66
+ end
67
+
54
68
  def sacrifice_feast(year)
55
69
  feast(:sacrifice_feast, year, DHU_AL_HIJJAH, 10)
56
70
  end
@@ -59,7 +73,11 @@ module Holidays
59
73
 
60
74
  def feast(name, year, hijri_month, hijri_day)
61
75
  DIYANET_OVERRIDES[[name, year]] ||
62
- Holidays::Factory::DateCalculator.hijri_date.gregorian_year_occurrence(year, hijri_month, hijri_day)
76
+ hijri.gregorian_year_occurrence(year, hijri_month, hijri_day)
77
+ end
78
+
79
+ def hijri
80
+ Holidays::Factory::DateCalculator.hijri_date
63
81
  end
64
82
  end
65
83
  end
@@ -38,7 +38,7 @@ module Holidays
38
38
 
39
39
  def self.holidays_by_month
40
40
  {
41
- #{month_strings.join(",\n")}
41
+ #{month_strings.join(",\n")}
42
42
  }
43
43
  end
44
44
 
@@ -49,7 +49,8 @@ module Holidays
49
49
  existing_def[:function_modifier] == target_def[:function_modifier] &&
50
50
  existing_def[:type] == target_def[:type] &&
51
51
  existing_def[:observed] == target_def[:observed] &&
52
- existing_def[:year_ranges] == target_def[:year_ranges]
52
+ existing_def[:year_ranges] == target_def[:year_ranges] &&
53
+ existing_def[:excluded_regions] == target_def[:excluded_regions]
53
54
  end
54
55
  end
55
56
  end
@@ -8,4 +8,9 @@ module Holidays
8
8
  class InvalidRegion < Error; end
9
9
 
10
10
  class DefinitionTestError < Error; end
11
+
12
+ # Raised when a calculation needs data for a year outside the range its
13
+ # tables or formula support, e.g. a lunar date past the end of the lunar
14
+ # calendar tables.
15
+ class UnsupportedYearError < Error; end
11
16
  end
@@ -1,3 +1,5 @@
1
+ require 'holidays/errors'
2
+
1
3
  module Holidays
2
4
  module Finder
3
5
  module Context
@@ -14,29 +16,11 @@ module Holidays
14
16
 
15
17
  holidays = []
16
18
  dates_driver.each do |year, months|
17
- months.each do |month|
18
- next unless hbm = @holidays_by_month_repo.find_by_month(month)
19
- hbm.each do |h|
20
- next if informal_type?(h[:type]) && !informal_set?(options)
21
- next unless @rules[:in_region].call(regions, h[:regions])
22
-
23
- if h[:year_ranges]
24
- next unless @rules[:year_range].call(year, h[:year_ranges])
25
- end
26
-
27
- dates = if h[:function]
28
- custom_holidays(year, month, h, regions)
29
- else
30
- date = build_date(year, month, h, regions)
31
- date ? [date] : []
32
- end
33
-
34
- dates.each do |d|
35
- if observed_set?(options) && h[:observed]
36
- d = build_observed_date(d, regions, h)
37
- end
38
- holidays << {:date => d, :name => h[:name], :regions => h[:regions]}
39
- end
19
+ candidates(months).each do |month, h|
20
+ next unless applicable?(year, h, regions, options)
21
+
22
+ year_dates(year, months, month, h, regions, options).each do |d|
23
+ holidays << {:date => d, :name => h[:name], :regions => h[:regions]}
40
24
  end
41
25
  end
42
26
  end
@@ -61,6 +45,66 @@ module Holidays
61
45
  end
62
46
  end
63
47
 
48
+ # Returns [month, definition] pairs to evaluate for one year of the dates
49
+ # driver. Fixed-date definitions only matter in the driver's months, but a
50
+ # function (e.g. a lunar date filed under its lunar month) can land several
51
+ # months away from the month it is filed under, so every function definition
52
+ # is evaluated, once, regardless of its month.
53
+ # See https://github.com/holidays/holidays/issues/519
54
+ def candidates(months)
55
+ by_month = @holidays_by_month_repo.all
56
+
57
+ in_driver = months.flat_map do |month|
58
+ (by_month[month] || []).map { |h| [month, h] }
59
+ end
60
+
61
+ outside_driver = (by_month.keys - months).sort.flat_map do |month|
62
+ by_month[month].select { |h| h[:function] }.map { |h| [month, h] }
63
+ end
64
+
65
+ in_driver + outside_driver
66
+ end
67
+
68
+ # A year that only pads the query with a border month (see
69
+ # DatesDriverBuilder#add_border_months) may lie past the data a
70
+ # calculation supports, e.g. lunar year 2101. Dates of such a year are
71
+ # outside the query anyway, so they are skipped rather than failing
72
+ # the query. A year the query actually covers still raises.
73
+ def year_dates(year, months, month, h, regions, options)
74
+ holiday_dates(year, month, h, regions, options)
75
+ rescue Holidays::UnsupportedYearError
76
+ raise unless border_padding?(months)
77
+
78
+ []
79
+ end
80
+
81
+ # Every date in the query adds its own month and a neighbouring one to
82
+ # its year, so a year holding a single month (besides the variable
83
+ # month 0) is only there as border padding.
84
+ def border_padding?(months)
85
+ (months - [0]).size == 1
86
+ end
87
+
88
+ def applicable?(year, h, regions, options)
89
+ return false if informal_type?(h[:type]) && !informal_set?(options)
90
+ return false unless @rules[:in_region].call(regions, h[:regions], h[:excluded_regions])
91
+ return true unless h[:year_ranges]
92
+
93
+ @rules[:year_range].call(year, h[:year_ranges])
94
+ end
95
+
96
+ def holiday_dates(year, month, h, regions, options)
97
+ dates = if h[:function]
98
+ custom_holidays(year, month, h, regions)
99
+ else
100
+ [build_date(year, month, h, regions)].compact
101
+ end
102
+
103
+ return dates unless observed_set?(options) && h[:observed]
104
+
105
+ dates.map { |d| build_observed_date(d, regions, h) }
106
+ end
107
+
64
108
  def informal_type?(type)
65
109
  type && [:informal, 'informal'].include?(type)
66
110
  end
@@ -13,15 +13,13 @@ module Holidays
13
13
 
14
14
  regions, observed, informal = @options_parser.call(options)
15
15
 
16
- # This could be smarter but I don't have any evidence that just checking for
17
- # the next 12 months will cause us issues. If it does we can implement something
18
- # smarter here to check in smaller increments.
19
- #
20
- #FIXME Could this be until the to_date instead? Save us some processing?
21
- # This is matching what was in holidays.rb currently so I'm keeping it. -pp
22
- dates_driver = @dates_driver_builder.call(from_date, from_date >> 12)
23
-
16
+ # The driver stops at to_date; the dates driver still adds January of
17
+ # the next year as a border month. Searching a full 12 months from
18
+ # from_date would make the next year a searched year rather than
19
+ # border padding, so e.g. year_holidays for 2100 would need lunar
20
+ # data for 2101.
24
21
  to_date = Date.civil(from_date.year, 12, 31)
22
+ dates_driver = @dates_driver_builder.call(from_date, to_date)
25
23
  holidays = []
26
24
  ret_holidays = []
27
25
  opts = gather_options(observed, informal)
@@ -3,26 +3,31 @@ module Holidays
3
3
  module Rules
4
4
  class InRegion
5
5
  class << self
6
- def call(requested, available)
6
+ # Each requested region is checked on its own. It qualifies when one
7
+ # of the available regions is the region itself or one of its
8
+ # ancestors (us for us_ca) and none of the excluded regions is. The
9
+ # rule matches when any requested region qualifies. :any always
10
+ # matches, whatever is excluded.
11
+ def call(requested, available, excluded = nil)
7
12
  return true if requested.include?(:any)
8
13
 
9
- # When an underscore is encountered, derive the parent regions
10
- # symbol and check for both.
11
- requested = requested.collect do |r|
12
- if r.to_s.include?('_')
13
- chunks = r.to_s.split('_')
14
+ excluded ||= []
14
15
 
15
- chunks.length.downto(1).map do |num|
16
- chunks[0..-num].join('_').to_sym
17
- end
18
- else
19
- r
20
- end
16
+ requested.any? do |region|
17
+ lineage = lineage(region)
18
+
19
+ (available & lineage).any? && (excluded & lineage).empty?
21
20
  end
21
+ end
22
+
23
+ private
22
24
 
23
- requested = requested.flatten.uniq
25
+ # The region and all of its parents, e.g. :us_ca_xx gives
26
+ # [:us, :us_ca, :us_ca_xx].
27
+ def lineage(region)
28
+ chunks = region.to_s.split('_')
24
29
 
25
- available.any? { |avail| requested.include?(avail) }
30
+ 1.upto(chunks.length).map { |n| chunks.first(n).join('_').to_sym }
26
31
  end
27
32
  end
28
33
  end
@@ -1,3 +1,3 @@
1
1
  module Holidays
2
- VERSION = '11.7.0'
2
+ VERSION = '11.8.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: holidays
3
3
  version: !ruby/object:Gem::Version
4
- version: 11.7.0
4
+ version: 11.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dunae