holidays 11.5.0 → 11.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 450c972e94224caf8a44edc3865c9d7246aa7a0f0ac16c5d41156ff2ca460741
4
- data.tar.gz: 2a17f504696cdf7f345fb75b2b715f24f34218c132bb50d61265c8556475aee5
3
+ metadata.gz: a0cce6439fbf9b7dafb577ae8ea715393556a6304fd312ac626c944a1c0ba6da
4
+ data.tar.gz: 9f177f9d50d4c98d74df77f95eed42b3de5f91f1ea52e10c8252ec0f3bf0569f
5
5
  SHA512:
6
- metadata.gz: 1379f15a3ad2d6f06278d7e3806007145a58e78dadbaba0395aabc44bde996f36fc76b088be197a2101d7088b6b1107b924ed8fc850bbe91327e9c7a7bdeb947
7
- data.tar.gz: 2cce27a209357ec151b8048cdf2c5a6b532b4613693c52673d489d9c9604fe03394d8c2abb911939ecb44dafac2dfa63eb7cd2b29cd96bc113864868e9285ddf
6
+ metadata.gz: 23b54b4312a073ef3eab2ee4eb3948ebd91a9b7e6cf33706a1b3105eeb9b3112660649027fce7b6253d17f6a149954b456ab78aac8b8855a08268b2ed739a6eb
7
+ data.tar.gz: 7cc582e293f9d6b2ce9b5b92c715c2e0294eef6986ac04133be3a75cda4bb7fdd0912739794ce0361ab04d29af968983e1bdd14f777e2c85d39e56eb036a4de6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Ruby Holidays Gem CHANGELOG
2
2
 
3
+ ## 11.7.0
4
+
5
+ * Update to [v9.2.0 definitions](https://github.com/holidays/definitions/releases/tag/v9.2.0): `ma` Eid al-Fitr and Eid al-Adha, `my` Hari Raya Puasa and Hari Raya Haji.
6
+ * Add `ma` Eid al-Fitr and Eid al-Adha, calculated from the Hijri calendar. Years where the date proclaimed by the Ministry of Habous and Islamic Affairs differs from the calculation (Eid al-Fitr 2022, Eid al-Adha 2021) use the proclaimed date. [#510](https://github.com/holidays/holidays/pull/510), [#392](https://github.com/holidays/holidays/issues/392)
7
+ * Add `my` Hari Raya Puasa and Hari Raya Haji, calculated from the Hijri calendar. Years where the gazetted date differs from the calculation use the gazetted date, and a holiday falling on a Sunday is observed on the following Monday. [#511](https://github.com/holidays/holidays/pull/511), [#392](https://github.com/holidays/holidays/issues/392)
8
+ * Fix `next_holidays` returning too few holidays and skipping some. It only searched the 12 months after the start date; it now keeps searching later years until enough holidays are found. [#514](https://github.com/holidays/holidays/pull/514), [#513](https://github.com/holidays/holidays/issues/513)
9
+
10
+ ## 11.6.0
11
+
12
+ * Update to [v9.1.0 definitions](https://github.com/holidays/definitions/releases/tag/v9.1.0): `tr` feast dates calculated instead of table-driven, `vn` Giỗ Tổ Hùng Vương date fixes, and a region-aware `us` Independence Day observed shift.
13
+ * Calculate `tr` Ramazan Bayramı and Kurban Bayramı from a Hijri calendar instead of a hand-maintained per-year table, so they no longer silently disappear past 2030. [#504](https://github.com/holidays/holidays/pull/504)
14
+ * Fix `vn` Giỗ Tổ Hùng Vương dates for 2018-2023, caused by a lunar calendar leap-month bug in the 2017 table row. [#506](https://github.com/holidays/holidays/pull/506)
15
+ * Add a region-aware `independence_day` observed method for `us` Independence Day, replacing the `us_va`-only shift. Most regions shift to the nearest weekday, `us_ri` shifts to the following Monday, and `us_tx` never shifts. [#508](https://github.com/holidays/holidays/pull/508)
16
+
3
17
  ## 11.5.0
4
18
 
5
19
  * Update to [v9.0.0 definitions](https://github.com/holidays/definitions/releases/tag/v9.0.0): new `cn` region; `jp`, `kr`, `nz_sl`, `nz_hb`, `ro` date fixes; `tr` feasts through 2030; `de_be` 2025; informal Day of the Dead for `us`/`ca`. The v9.0.0 major is a definitions format change only and does not affect gem results or API.
data/README.md CHANGED
@@ -20,6 +20,7 @@ This gem is tested with the following ruby versions:
20
20
  * 3.4
21
21
  * 4.0
22
22
  * JRuby 10.0.5.0
23
+ * JRuby 10.1.0.0
23
24
 
24
25
  ## Semver
25
26
 
@@ -12,7 +12,9 @@ module Holidays
12
12
 
13
13
  def self.holidays_by_month
14
14
  {
15
- 1 => [{:mday => 1, :name => "Ras l' âm", :regions => [:ma]},
15
+ 0 => [{:function => "eid_al_fitr(year)", :function_arguments => [:year], :name => "Eid al-Fitr", :regions => [:ma]},
16
+ {:function => "eid_al_adha(year)", :function_arguments => [:year], :name => "Eid al-Adha", :regions => [:ma]}],
17
+ 1 => [{:mday => 1, :name => "Ras l' âm", :regions => [:ma]},
16
18
  {:mday => 11, :name => "Takdim watikat al-istiqlal", :regions => [:ma]}],
17
19
  5 => [{:mday => 1, :name => "Eid Ash-Shughl", :regions => [:ma]}],
18
20
  7 => [{:mday => 30, :name => "Eid Al-Ârch", :regions => [:ma]}],
@@ -12,7 +12,9 @@ module Holidays
12
12
 
13
13
  def self.holidays_by_month
14
14
  {
15
- 1 => [{:mday => 1, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "New Year's Day", :regions => [:my]}],
15
+ 0 => [{:function => "hari_raya_puasa(year)", :function_arguments => [:year], :observed => "to_monday_if_sunday(date)", :observed_arguments => [:date], :name => "Hari Raya Puasa", :regions => [:my]},
16
+ {:function => "hari_raya_haji(year)", :function_arguments => [:year], :observed => "to_monday_if_sunday(date)", :observed_arguments => [:date], :name => "Hari Raya Haji", :regions => [:my]}],
17
+ 1 => [{:mday => 1, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "New Year's Day", :regions => [:my]}],
16
18
  5 => [{:mday => 1, :name => "Labour Day", :regions => [:my]}],
17
19
  6 => [{:mday => 4, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Agong's Birthday", :regions => [:my]}],
18
20
  8 => [{:mday => 31, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Independence Day", :regions => [:my]}],
@@ -83,8 +83,7 @@ module Holidays
83
83
  {:mday => 12, :type => :informal, :name => "Orangemen's Day", :regions => [:ca_nl]},
84
84
  {:mday => 9, :year_ranges => { :from => 2020 },:observed => "to_monday_if_weekend(date)", :observed_arguments => [:date], :name => "Nunavut Day", :regions => [:ca_nu]},
85
85
  {:mday => 3, :name => "Emancipation Day", :regions => [:us_vi]},
86
- {:mday => 4, :name => "Independence Day", :regions => [:us]},
87
- {:mday => 4, :function => "to_weekday_if_weekend(date)", :function_arguments => [:date], :name => "Independence Day (Holiday)", :regions => [:us_va]},
86
+ {:mday => 4, :observed => "independence_day(region, date)", :observed_arguments => [:region, :date], :name => "Independence Day", :regions => [:us]},
88
87
  {:mday => 24, :name => "Pioneer Day", :regions => [:us_ut]}],
89
88
  8 => [{:wday => 1, :week => 1, :name => "B.C. Day", :regions => [:ca_bc]},
90
89
  {:wday => 1, :week => 1, :name => "Saskatchewan Day", :regions => [:ca_sk]},
@@ -58,8 +58,7 @@ module Holidays
58
58
  {:mday => 20, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "West Virginia Day", :regions => [:us_wv]},
59
59
  {:wday => 0, :week => 3, :type => :informal, :name => "Father's Day", :regions => [:us, :ca]}],
60
60
  7 => [{:mday => 3, :name => "Emancipation Day", :regions => [:us_vi]},
61
- {:mday => 4, :name => "Independence Day", :regions => [:us]},
62
- {:mday => 4, :function => "to_weekday_if_weekend(date)", :function_arguments => [:date], :name => "Independence Day (Holiday)", :regions => [:us_va]},
61
+ {:mday => 4, :observed => "independence_day(region, date)", :observed_arguments => [:region, :date], :name => "Independence Day", :regions => [:us]},
63
62
  {:mday => 24, :name => "Pioneer Day", :regions => [:us_ut]}],
64
63
  8 => [{:wday => 1, :week => 2, :name => "Victory Day", :regions => [:us_ri]},
65
64
  {:mday => 16, :observed => "to_weekday_if_weekend(date)", :observed_arguments => [:date], :name => "Bennington Battle Day", :regions => [:us_vt]},
@@ -0,0 +1,52 @@
1
+ require 'date'
2
+
3
+ module Holidays
4
+ module DateCalculator
5
+ # Arithmetic ("tabular", a.k.a. Kuwaiti) Islamic calendar.
6
+ #
7
+ # Months alternate 30 and 29 days; leap years add a day to the twelfth
8
+ # month on a fixed 11-of-30 cycle. This is a purely arithmetic calendar,
9
+ # so it can land a day (occasionally two) either side of a locally
10
+ # proclaimed date. Callers that need the proclaimed date layer their own
11
+ # override table on top of this (see
12
+ # Holidays::Definition::CustomMethods::TR).
13
+ class HijriDate
14
+ # Julian day number of 1 Muharram 1 AH minus one day, chosen so that
15
+ # to_jd's month/day terms are added directly. Corresponds to the civil
16
+ # epoch (Friday, 16 July 622 CE proleptic Julian).
17
+ EPOCH_JDN = 1948439
18
+
19
+ def to_gregorian(hijri_year, hijri_month, hijri_day)
20
+ Date.jd(to_jd(hijri_year, hijri_month, hijri_day))
21
+ end
22
+
23
+ # Given a Gregorian year and a fixed Hijri month/day, return the
24
+ # occurrence that falls within that Gregorian year, or nil if none does.
25
+ #
26
+ # A fixed Hijri date drifts ~11 days earlier each Gregorian year, so
27
+ # roughly once every 33 years it falls twice in the same Gregorian year
28
+ # (once in early January, once in late December). Only the earlier
29
+ # occurrence is returned.
30
+ def gregorian_year_occurrence(gregorian_year, hijri_month, hijri_day)
31
+ candidate_hijri_year = gregorian_year - 579
32
+
33
+ (candidate_hijri_year - 1..candidate_hijri_year + 1).each do |hijri_year|
34
+ date = to_gregorian(hijri_year, hijri_month, hijri_day)
35
+ return date if date.year == gregorian_year
36
+ end
37
+
38
+ nil
39
+ end
40
+
41
+ private
42
+
43
+ def to_jd(year, month, day)
44
+ day +
45
+ (29.5 * (month - 1)).ceil +
46
+ (year - 1) * 354 +
47
+ (3 + 11 * year) / 30 +
48
+ EPOCH_JDN
49
+ end
50
+ end
51
+ end
52
+ end
@@ -152,7 +152,7 @@ module Holidays
152
152
  [384, 1, 2, 1, 2, 1, 2, 1, 2, 4, 2, 1, 2].freeze,
153
153
  [354, 1, 2, 1, 1, 2, 1, 2, 2, 2, 1, 2, 1].freeze,
154
154
  [355, 2, 1, 2, 1, 1, 2, 1, 2, 2, 1, 2, 2].freeze,
155
- [355, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 2].freeze,
155
+ [384, 1, 2, 1, 2, 1, 5, 1, 2, 1, 2, 2, 2].freeze, # 2017 has a leap 6th month; previous entry omitted it, shifting every later date
156
156
  [354, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2].freeze,
157
157
  [354, 2, 1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2].freeze,
158
158
  [384, 2, 1, 2, 4, 2, 1, 1, 2, 1, 2, 1, 2].freeze,
@@ -3,10 +3,12 @@ module Holidays
3
3
  module Context
4
4
  # Merge a new set of definitions into the Holidays module.
5
5
  class Merger
6
- def initialize(holidays_by_month_repo, regions_repo, custom_methods_repo)
6
+ def initialize(holidays_by_month_repo, regions_repo, custom_methods_repo, cache_repo, proc_result_cache_repo)
7
7
  @holidays_repo = holidays_by_month_repo
8
8
  @regions_repo = regions_repo
9
9
  @custom_methods_repo = custom_methods_repo
10
+ @cache_repo = cache_repo
11
+ @proc_result_cache_repo = proc_result_cache_repo
10
12
  end
11
13
 
12
14
  def call(target_regions, target_holidays, target_custom_methods, target_custom_method_sources = {})
@@ -17,6 +19,9 @@ module Holidays
17
19
  target_custom_method_sources,
18
20
  derive_function_regions(target_holidays),
19
21
  )
22
+ ensure
23
+ @cache_repo.reset!
24
+ @proc_result_cache_repo.reset!
20
25
  end
21
26
 
22
27
  private
@@ -0,0 +1,48 @@
1
+ require 'date'
2
+
3
+ module Holidays
4
+ module Definition
5
+ module CustomMethods
6
+ # ma custom holiday calculations.
7
+ #
8
+ # Eid al-Fitr is 1 Shawwal and Eid al-Adha is 10 Dhul-Hijjah of the
9
+ # Hijri calendar. Both are derived from the arithmetic Islamic calendar
10
+ # (Holidays::DateCalculator::HijriDate). The civil holidays are
11
+ # proclaimed by Morocco's Ministry of Habous and Islamic Affairs and in
12
+ # some years land a day off from the arithmetic result; MINISTRY_OVERRIDES
13
+ # pins those years to the proclaimed date. Years with no entry fall back
14
+ # to the calculation. See holidays#392.
15
+ module MA
16
+ SHAWWAL = 10
17
+ DHU_AL_HIJJAH = 12
18
+
19
+ # Proclaimed first days that differ from the arithmetic calendar,
20
+ # keyed by [feast, gregorian_year]. Sourced from the Ministry of
21
+ # Habous and Islamic Affairs communiques reported via maroc.ma,
22
+ # Hespress, and Morocco World News.
23
+ MINISTRY_OVERRIDES = {
24
+ [:eid_al_fitr, 2022] => Date.new(2022, 5, 2),
25
+
26
+ [:eid_al_adha, 2021] => Date.new(2021, 7, 21),
27
+ }.freeze
28
+
29
+ class << self
30
+ def eid_al_fitr(year)
31
+ feast(:eid_al_fitr, year, SHAWWAL, 1)
32
+ end
33
+
34
+ def eid_al_adha(year)
35
+ feast(:eid_al_adha, year, DHU_AL_HIJJAH, 10)
36
+ end
37
+
38
+ private
39
+
40
+ def feast(name, year, hijri_month, hijri_day)
41
+ MINISTRY_OVERRIDES[[name, year]] ||
42
+ Holidays::Factory::DateCalculator.hijri_date.gregorian_year_occurrence(year, hijri_month, hijri_day)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,55 @@
1
+ require 'date'
2
+
3
+ module Holidays
4
+ module Definition
5
+ module CustomMethods
6
+ # my custom holiday calculations.
7
+ #
8
+ # Hari Raya Puasa (Eid al-Fitr) is 1 Shawwal and Hari Raya Haji (Eid
9
+ # al-Adha) is 10 Dhul-Hijjah of the Hijri calendar. Both are derived
10
+ # from the arithmetic Islamic calendar (Holidays::DateCalculator::HijriDate).
11
+ # The civil holidays are fixed by the Yang di-Pertuan Agong (Conference
12
+ # of Rulers) on JAKIM's moon-sighting advice and in some years have
13
+ # landed a day before the arithmetic result; GAZETTE_OVERRIDES pins
14
+ # those years to the gazetted date. Years with no entry fall back to
15
+ # the calculation. See holidays#392.
16
+ module MY
17
+ SHAWWAL = 10
18
+ DHU_AL_HIJJAH = 12
19
+
20
+ # Gazetted first days that differ from the arithmetic calendar,
21
+ # keyed by [feast, gregorian_year]. Sourced from the official
22
+ # Malaysian public holiday gazette, cross-checked against
23
+ # definitions/sg.yaml (agrees in every overlapping year 2017-2026).
24
+ GAZETTE_OVERRIDES = {
25
+ [:hari_raya_puasa, 2014] => Date.new(2014, 7, 28),
26
+ [:hari_raya_puasa, 2015] => Date.new(2015, 7, 17),
27
+ [:hari_raya_puasa, 2016] => Date.new(2016, 7, 6),
28
+ [:hari_raya_puasa, 2017] => Date.new(2017, 6, 25),
29
+ [:hari_raya_puasa, 2026] => Date.new(2026, 3, 21),
30
+
31
+ [:hari_raya_haji, 2016] => Date.new(2016, 9, 12),
32
+ [:hari_raya_haji, 2017] => Date.new(2017, 9, 1),
33
+ [:hari_raya_haji, 2019] => Date.new(2019, 8, 11),
34
+ }.freeze
35
+
36
+ class << self
37
+ def hari_raya_puasa(year)
38
+ feast(:hari_raya_puasa, year, SHAWWAL, 1)
39
+ end
40
+
41
+ def hari_raya_haji(year)
42
+ feast(:hari_raya_haji, year, DHU_AL_HIJJAH, 10)
43
+ end
44
+
45
+ private
46
+
47
+ def feast(name, year, hijri_month, hijri_day)
48
+ GAZETTE_OVERRIDES[[name, year]] ||
49
+ Holidays::Factory::DateCalculator.hijri_date.gregorian_year_occurrence(year, hijri_month, hijri_day)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -13,6 +13,8 @@ require 'holidays/definition/custom_methods/is'
13
13
  require 'holidays/definition/custom_methods/jp'
14
14
  require 'holidays/definition/custom_methods/kr'
15
15
  require 'holidays/definition/custom_methods/lv'
16
+ require 'holidays/definition/custom_methods/ma'
17
+ require 'holidays/definition/custom_methods/my'
16
18
  require 'holidays/definition/custom_methods/nz'
17
19
  require 'holidays/definition/custom_methods/ph'
18
20
  require 'holidays/definition/custom_methods/se'
@@ -46,14 +48,19 @@ module Holidays
46
48
  "columbus_day_cl(year)" => CL.method(:columbus_day_cl).to_proc,
47
49
  "day_after_thanksgiving(year)" => FEDEX.method(:day_after_thanksgiving).to_proc,
48
50
  "de_buss_und_bettag(year)" => DE.method(:de_buss_und_bettag).to_proc,
51
+ "eid_al_adha(year)" => MA.method(:eid_al_adha).to_proc,
52
+ "eid_al_fitr(year)" => MA.method(:eid_al_fitr).to_proc,
49
53
  "election_day(year)" => US.method(:election_day).to_proc,
50
54
  "even_year_election_day(year)" => US.method(:even_year_election_day).to_proc,
51
55
  "fi_juhannusaatto(year)" => FI.method(:fi_juhannusaatto).to_proc,
52
56
  "fi_juhannuspaiva(year)" => FI.method(:fi_juhannuspaiva).to_proc,
53
57
  "fi_pyhainpaiva(year)" => FI.method(:fi_pyhainpaiva).to_proc,
54
58
  "georgia_state_holiday(year, month)" => US.method(:georgia_state_holiday).to_proc,
59
+ "hari_raya_haji(year)" => MY.method(:hari_raya_haji).to_proc,
60
+ "hari_raya_puasa(year)" => MY.method(:hari_raya_puasa).to_proc,
55
61
  "hobart_show_day(year)" => AU.method(:hobart_show_day).to_proc,
56
62
  "ie_st_brigids_day(year)" => IE.method(:ie_st_brigids_day).to_proc,
63
+ "independence_day(region, date)" => US.method(:independence_day).to_proc,
57
64
  "is_sumardagurinn_fyrsti(year)" => IS.method(:is_sumardagurinn_fyrsti).to_proc,
58
65
  "jp_citizens_holiday(year)" => JP.method(:jp_citizens_holiday).to_proc,
59
66
  "jp_health_sports_day_substitute(year)" => JP.method(:jp_health_sports_day_substitute).to_proc,
@@ -3,54 +3,63 @@ require 'date'
3
3
  module Holidays
4
4
  module Definition
5
5
  module CustomMethods
6
- # tr custom holiday calculations, ported verbatim from the
7
- # +methods:+ block in definitions/tr.yaml.
6
+ # tr custom holiday calculations.
7
+ #
8
+ # Ramazan Bayramı is 1 Shawwal and Kurban Bayramı is 10 Dhul-Hijjah of
9
+ # the Hijri calendar. Both are derived from the arithmetic Islamic
10
+ # calendar (Holidays::DateCalculator::HijriDate). The civil holidays are
11
+ # proclaimed by Türkiye's Diyanet and in most years so far have landed a
12
+ # day (2016 Ramazan Bayramı, two days) before the arithmetic result;
13
+ # DIYANET_OVERRIDES pins those years to the proclaimed date. Years with
14
+ # no entry fall back to the calculation. See definitions#377.
8
15
  module TR
16
+ SHAWWAL = 10
17
+ DHU_AL_HIJJAH = 12
18
+
19
+ # Proclaimed first days that differ from the arithmetic calendar,
20
+ # keyed by [feast, gregorian_year]. Sourced from the Diyanet dates
21
+ # that the definition file previously carried as a fixed table, plus
22
+ # https://vakithesaplama.diyanet.gov.tr/resmitatiller.php for 2021-2030.
23
+ DIYANET_OVERRIDES = {
24
+ [:ramadan_feast, 2014] => Date.new(2014, 7, 28),
25
+ [:ramadan_feast, 2015] => Date.new(2015, 7, 17),
26
+ [:ramadan_feast, 2016] => Date.new(2016, 7, 5),
27
+ [:ramadan_feast, 2017] => Date.new(2017, 6, 25),
28
+ [:ramadan_feast, 2019] => Date.new(2019, 6, 4),
29
+ [:ramadan_feast, 2022] => Date.new(2022, 5, 2),
30
+ [:ramadan_feast, 2023] => Date.new(2023, 4, 21),
31
+ [:ramadan_feast, 2025] => Date.new(2025, 3, 30),
32
+ [:ramadan_feast, 2027] => Date.new(2027, 3, 9),
33
+ [:ramadan_feast, 2028] => Date.new(2028, 2, 26),
34
+ [:ramadan_feast, 2030] => Date.new(2030, 2, 4),
35
+
36
+ [:sacrifice_feast, 2014] => Date.new(2014, 10, 4),
37
+ [:sacrifice_feast, 2016] => Date.new(2016, 9, 12),
38
+ [:sacrifice_feast, 2017] => Date.new(2017, 9, 1),
39
+ [:sacrifice_feast, 2018] => Date.new(2018, 8, 21),
40
+ [:sacrifice_feast, 2019] => Date.new(2019, 8, 11),
41
+ [:sacrifice_feast, 2022] => Date.new(2022, 7, 9),
42
+ [:sacrifice_feast, 2023] => Date.new(2023, 6, 28),
43
+ [:sacrifice_feast, 2024] => Date.new(2024, 6, 16),
44
+ [:sacrifice_feast, 2025] => Date.new(2025, 6, 6),
45
+ [:sacrifice_feast, 2027] => Date.new(2027, 5, 16),
46
+ [:sacrifice_feast, 2030] => Date.new(2030, 4, 13),
47
+ }.freeze
48
+
9
49
  class << self
10
50
  def ramadan_feast(year)
11
- begin_of_ramadan_feast = {
12
- '2014' => Date.civil(2014, 7, 28),
13
- '2015' => Date.civil(2015, 7, 17),
14
- '2016' => Date.civil(2016, 7, 5),
15
- '2017' => Date.civil(2017, 6, 25),
16
- '2018' => Date.civil(2018, 6, 15),
17
- '2019' => Date.civil(2019, 6, 4),
18
- '2020' => Date.civil(2020, 5, 24),
19
- '2021' => Date.civil(2021, 5, 13),
20
- '2022' => Date.civil(2022, 5, 2),
21
- '2023' => Date.civil(2023, 4, 21),
22
- '2024' => Date.civil(2024, 4, 10),
23
- '2025' => Date.civil(2025, 3, 30),
24
- '2026' => Date.civil(2026, 3, 20),
25
- '2027' => Date.civil(2027, 3, 9),
26
- '2028' => Date.civil(2028, 2, 26),
27
- '2029' => Date.civil(2029, 2, 15),
28
- '2030' => Date.civil(2030, 2, 4)
29
- }
30
- begin_of_ramadan_feast[year.to_s]
51
+ feast(:ramadan_feast, year, SHAWWAL, 1)
31
52
  end
32
53
 
33
54
  def sacrifice_feast(year)
34
- begin_of_sacrifice_feast = {
35
- '2014' => Date.civil(2014, 10, 4),
36
- '2015' => Date.civil(2015, 9, 24),
37
- '2016' => Date.civil(2016, 9, 12),
38
- '2017' => Date.civil(2017, 9, 1),
39
- '2018' => Date.civil(2018, 8, 21),
40
- '2019' => Date.civil(2019, 8, 11),
41
- '2020' => Date.civil(2020, 7, 31),
42
- '2021' => Date.civil(2021, 7, 20),
43
- '2022' => Date.civil(2022, 7, 9),
44
- '2023' => Date.civil(2023, 6, 28),
45
- '2024' => Date.civil(2024, 6, 16),
46
- '2025' => Date.civil(2025, 6, 6),
47
- '2026' => Date.civil(2026, 5, 27),
48
- '2027' => Date.civil(2027, 5, 16),
49
- '2028' => Date.civil(2028, 5, 5),
50
- '2029' => Date.civil(2029, 4, 24),
51
- '2030' => Date.civil(2030, 4, 13)
52
- }
53
- begin_of_sacrifice_feast[year.to_s]
55
+ feast(:sacrifice_feast, year, DHU_AL_HIJJAH, 10)
56
+ end
57
+
58
+ private
59
+
60
+ def feast(name, year, hijri_month, hijri_day)
61
+ DIYANET_OVERRIDES[[name, year]] ||
62
+ Holidays::Factory::DateCalculator.hijri_date.gregorian_year_occurrence(year, hijri_month, hijri_day)
54
63
  end
55
64
  end
56
65
  end
@@ -72,6 +72,33 @@ module Holidays
72
72
  end
73
73
  end
74
74
 
75
+ # Independence Day observed shift, which varies by region:
76
+ # - us_ri: Rhode Island moves to the following Monday on a weekend
77
+ # (Saturday -> +2, Sunday -> +1).
78
+ # - us_tx: Texas does not shift, the date is returned unchanged.
79
+ # - everyone else: nearest weekday, same as +to_weekday_if_weekend+
80
+ # (Saturday -> Friday, Sunday -> Monday).
81
+ def independence_day(region, date)
82
+ if region == :us_ri
83
+ case date.wday
84
+ when 6
85
+ date + 2
86
+ when 0
87
+ date + 1
88
+ else
89
+ date
90
+ end
91
+ elsif region == :us_tx
92
+ date
93
+ elsif date.wday == 6
94
+ date - 1
95
+ elsif date.wday == 0
96
+ date + 1
97
+ else
98
+ date
99
+ end
100
+ end
101
+
75
102
  def election_day(year)
76
103
  Holidays::Factory::DateCalculator.day_of_month_calculator.call(year, 11, 1, 1) + 1
77
104
  end
@@ -33,6 +33,10 @@ module Holidays
33
33
  @proc_cache[proc_key]
34
34
  end
35
35
 
36
+ def reset!
37
+ @proc_cache = {}
38
+ end
39
+
36
40
  private
37
41
 
38
42
  def validate!(function, function_arguments)
@@ -2,6 +2,7 @@ require 'holidays/date_calculator/easter'
2
2
  require 'holidays/date_calculator/weekend_modifier'
3
3
  require 'holidays/date_calculator/day_of_month'
4
4
  require 'holidays/date_calculator/lunar_date'
5
+ require 'holidays/date_calculator/hijri_date'
5
6
 
6
7
  module Holidays
7
8
  module Factory
@@ -25,10 +26,14 @@ module Holidays
25
26
  end
26
27
 
27
28
  class << self
28
- def lunar_date
29
+ def lunar_date
29
30
  Holidays::DateCalculator::LunarDate.new
30
31
  end
31
32
 
33
+ def hijri_date
34
+ Holidays::DateCalculator::HijriDate.new
35
+ end
36
+
32
37
  def weekend_modifier
33
38
  Holidays::DateCalculator::WeekendModifier.new
34
39
  end
@@ -57,6 +57,8 @@ module Holidays
57
57
  holidays_by_month_repository,
58
58
  regions_repository,
59
59
  custom_methods_repository,
60
+ cache_repository,
61
+ proc_result_cache_repository,
60
62
  )
61
63
  end
62
64
 
@@ -2,6 +2,11 @@ module Holidays
2
2
  module Finder
3
3
  module Context
4
4
  class NextHoliday
5
+ # Holidays are searched one 12 month window at a time. The search widens until
6
+ # enough holidays are found or this many years have been searched, which stops
7
+ # a region with no upcoming holidays from being searched forever.
8
+ MAX_YEARS = 50
9
+
5
10
  def initialize(definition_search, dates_driver_builder, options_parser)
6
11
  @definition_search = definition_search
7
12
  @dates_driver_builder = dates_driver_builder
@@ -12,31 +17,36 @@ module Holidays
12
17
  validate!(holidays_count, from_date)
13
18
 
14
19
  regions, observed, informal = @options_parser.call(options)
20
+ opts = gather_options(observed, informal)
15
21
 
16
22
  holidays = []
17
- opts = gather_options(observed, informal)
18
23
 
19
- # This could be smarter but I don't have any evidence that just checking for
20
- # the next 12 months will cause us issues. If it does we can implement something
21
- # smarter here to check in smaller increments.
22
- dates_driver = @dates_driver_builder.call(from_date, from_date >> 12)
24
+ 1.upto(MAX_YEARS) do |year|
25
+ holidays.concat(search_window(from_date, year, regions, opts))
26
+ break if holidays.size >= holidays_count
27
+ end
23
28
 
24
- @definition_search
25
- .call(dates_driver, regions, opts)
26
- .sort_by { |a| a[:date] }
27
- .each do |holiday|
28
- if holiday[:date] >= from_date
29
- holidays << holiday
30
- holidays_count -= 1
31
- break if holidays_count == 0
32
- end
33
- end
34
-
35
- holidays.sort_by { |a| a[:date] }
29
+ holidays.first(holidays_count)
36
30
  end
37
31
 
38
32
  private
39
33
 
34
+ # Windows are contiguous: each starts the day after the previous one ended. The
35
+ # search only looks at the months around a window (plus month 0, which covers the
36
+ # whole year), so dates past the window end are incomplete. They are discarded here
37
+ # and found by the next window instead.
38
+ def search_window(from_date, year, regions, opts)
39
+ window_start = year == 1 ? from_date : (from_date >> 12 * (year - 1)) + 1
40
+ window_end = from_date >> 12 * year
41
+
42
+ dates_driver = @dates_driver_builder.call(window_start, window_end)
43
+
44
+ @definition_search
45
+ .call(dates_driver, regions, opts)
46
+ .select { |holiday| (window_start..window_end).cover?(holiday[:date]) }
47
+ .sort_by { |holiday| holiday[:date] }
48
+ end
49
+
40
50
  def validate!(holidays_count, from_date)
41
51
  raise ArgumentError unless holidays_count
42
52
  raise ArgumentError if holidays_count <= 0
@@ -1,3 +1,3 @@
1
1
  module Holidays
2
- VERSION = '11.5.0'
2
+ VERSION = '11.7.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.5.0
4
+ version: 11.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex Dunae
@@ -197,6 +197,7 @@ files:
197
197
  - lib/holidays/core_extensions/time.rb
198
198
  - lib/holidays/date_calculator/day_of_month.rb
199
199
  - lib/holidays/date_calculator/easter.rb
200
+ - lib/holidays/date_calculator/hijri_date.rb
200
201
  - lib/holidays/date_calculator/lunar_date.rb
201
202
  - lib/holidays/date_calculator/weekend_modifier.rb
202
203
  - lib/holidays/definition/context/function_processor.rb
@@ -218,6 +219,8 @@ files:
218
219
  - lib/holidays/definition/custom_methods/jp.rb
219
220
  - lib/holidays/definition/custom_methods/kr.rb
220
221
  - lib/holidays/definition/custom_methods/lv.rb
222
+ - lib/holidays/definition/custom_methods/ma.rb
223
+ - lib/holidays/definition/custom_methods/my.rb
221
224
  - lib/holidays/definition/custom_methods/nz.rb
222
225
  - lib/holidays/definition/custom_methods/ph.rb
223
226
  - lib/holidays/definition/custom_methods/registry.rb