Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update working days calculation #2197

Merged
merged 3 commits into from
Dec 30, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions holidays/groups/custom.py
Original file line number Diff line number Diff line change
@@ -10,6 +10,10 @@
# Website: https://github.com/vacanza/holidays
# License: MIT (see LICENSE file)

from datetime import date

from holidays.helpers import _normalize_tuple


class StaticHolidays:
"""Helper class for special and substituted holidays support.
@@ -33,3 +37,11 @@ def __init__(self, cls) -> None:
):
setattr(self, attribute_name, value)
self.has_substituted_holidays = True

# Populate substituted holidays from adjacent years.
self.weekend_workdays = set()
for special_public_holidays in getattr(self, "special_public_holidays", {}).values():
for special_public_holiday in _normalize_tuple(special_public_holidays):
if len(special_public_holiday) == 5: # The fifth element is the year.
_, _, from_month, from_day, from_year = special_public_holiday
self.weekend_workdays.add(date(from_year, from_month, from_day))
4 changes: 2 additions & 2 deletions holidays/holiday_base.py
Original file line number Diff line number Diff line change
@@ -233,7 +233,7 @@ def _populate(self, year):
ones."""
weekend: set[int] = {SAT, SUN}
"""Country weekend days."""
weekend_workdays: set[date] = set()
weekend_workdays: set[date]
"""Working days moved to weekends."""
default_category: str = PUBLIC
"""The entity category used by default."""
@@ -362,7 +362,7 @@ def __init__(
self.language = language.lower() if language else None
self.observed = observed
self.subdiv = subdiv
self.weekend_workdays = set()
self.weekend_workdays = getattr(self, "weekend_workdays", set())

supported_languages = set(self.supported_languages)
self.tr = (
20 changes: 20 additions & 0 deletions tests/countries/test_azerbaijan.py
Original file line number Diff line number Diff line change
@@ -66,6 +66,26 @@ def test_substituted_holidays(self):
"2025-01-03",
)

for year, dts in {
2012: (
"2012-12-29",
"2012-12-30",
),
2013: (
"2013-12-28",
"2013-12-29",
),
2019: (
"2019-12-28",
"2019-12-29",
),
2023: ("2023-12-30",),
2024: ("2024-12-29",),
}.items():
az_holidays = Azerbaijan(years=year)
for dt in dts:
self.assertTrue(az_holidays.is_working_day(dt))

def test_new_years_day(self):
name = "Yeni il bayramı"
self.assertHolidayName(name, (f"{year}-01-01" for year in range(1990, 2050)))
12 changes: 12 additions & 0 deletions tests/countries/test_belarus.py
Original file line number Diff line number Diff line change
@@ -225,6 +225,18 @@ def test_radunitsa(self):
"2030-05-07",
)

for year, dts in {
2006: (
"2006-01-21",
"2006-05-06",
"2006-11-04",
"2006-12-30",
),
}.items():
be_holidays = Belarus(years=year)
for dt in dts:
self.assertTrue(be_holidays.is_working_day(dt))

def test_dzyady(self):
name = "Дзень памяці"

44 changes: 44 additions & 0 deletions tests/countries/test_china.py
Original file line number Diff line number Diff line change
@@ -29,6 +29,50 @@ def test_no_holidays(self):
self.assertNoHolidays(China(years=1949))
self.assertNoHolidays(China(years=1949, categories=HALF_DAY))

def test_substituted_holidays(self):
for year, dts in {
2001: (
"2001-01-20",
"2001-01-21",
"2001-04-28",
"2001-04-29",
"2001-09-29",
"2001-09-30",
"2001-12-29",
"2001-12-30",
),
2005: (
"2005-02-05",
"2005-02-06",
"2005-04-30",
"2005-05-08",
"2005-10-08",
"2005-10-09",
"2005-12-31",
),
2006: (
"2006-01-28",
"2006-02-05",
"2006-04-29",
"2006-04-30",
"2006-09-30",
"2006-10-08",
"2006-12-30",
"2006-12-31",
),
2011: (
"2011-01-30",
"2011-02-12",
"2011-04-02",
"2011-10-08",
"2011-10-09",
"2011-12-31",
),
}.items():
cn_holidays = China(years=year)
for dt in dts:
self.assertTrue(cn_holidays.is_working_day(dt))

def test_new_years_day(self):
self.assertHolidayName("元旦", (f"{year}-01-01" for year in range(1950, 2050)))

11 changes: 11 additions & 0 deletions tests/countries/test_kazakhstan.py
Original file line number Diff line number Diff line change
@@ -242,6 +242,17 @@ def test_substituted_holidays(self):
"2025-01-03",
)

for year, dts in {
2013: (
"2013-05-04",
"2013-10-12",
"2013-12-28",
),
}.items():
kz_holidays = Kazakhstan(years=year)
for dt in dts:
self.assertTrue(kz_holidays.is_working_day(dt))

def test2022(self):
self.assertHolidays(
Kazakhstan(years=2022),
8 changes: 8 additions & 0 deletions tests/countries/test_taiwan.py
Original file line number Diff line number Diff line change
@@ -28,6 +28,14 @@ def test_country_aliases(self):
def test_no_holidays(self):
self.assertNoHolidays(Taiwan(years=1911))

def test_substituted_holidays(self):
for year, dts in {
2014: ("2014-12-27",),
}.items():
tw_holidays = Taiwan(years=year)
for dt in dts:
self.assertTrue(tw_holidays.is_working_day(dt))

def test_new_years_day(self):
name = "中華民國開國紀念日"
self.assertHolidayName(name, (f"{year}-01-01" for year in range(1990, 2030)))
12 changes: 12 additions & 0 deletions tests/countries/test_ukraine.py
Original file line number Diff line number Diff line change
@@ -33,6 +33,18 @@ def test_no_holidays(self):
def test_special_holidays(self):
self.assertHoliday("1995-01-09")

def test_substituted_holidays(self):
for year, dts in {
1996: (
"1996-05-05",
"1996-05-12",
"1996-12-28",
),
}.items():
ua_holidays = Ukraine(years=year)
for dt in dts:
self.assertTrue(ua_holidays.is_working_day(dt))

def test_new_year_day(self):
self.assertHoliday(f"{year}-01-01" for year in range(1991, 2023))
dt = (
12 changes: 12 additions & 0 deletions tests/countries/test_vietnam.py
Original file line number Diff line number Diff line change
@@ -174,6 +174,18 @@ def test_substituted_holidays(self):
"2024-04-29",
)

for year, dts in {
2014: (
"2014-04-26",
"2014-09-06",
"2014-12-27",
),
2019: ("2019-01-05",),
}.items():
vn_holidays = Vietnam(years=year)
for dt in dts:
self.assertTrue(vn_holidays.is_working_day(dt))

def test_l10n_default(self):
self.assertLocalizedHolidays(
("2022-01-01", "Tết Dương lịch"),
Loading