Skip to content

Commit

Permalink
Optimize observed holidays for Mozambique, Namibia, Zambia, Zimbabwe
Browse files Browse the repository at this point in the history
  • Loading branch information
KJhellico committed Feb 15, 2023
1 parent f2571fa commit c31a54a
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 258 deletions.
37 changes: 21 additions & 16 deletions holidays/countries/mozambique.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,34 +22,39 @@ class Mozambique(HolidayBase):
country = "MZ"

def _populate(self, year):
def _add_with_observed(hol_date: date, hol_name: str) -> None:
# whenever a public holiday falls on a Sunday,
# it rolls over to the following Monday
self[hol_date] = hol_name
if self.observed and self._is_sunday(hol_date):
self[hol_date + td(days=+1)] = f"{hol_name} (PONTE)"

if year <= 1974:
return None

super()._populate(year)

self[date(year, JAN, 1)] = "Ano novo"
_add_with_observed(date(year, JAN, 1), "Ano novo")
easter_date = easter(year)
self[easter_date + td(days=-2)] = "Sexta-feira Santa"

# carnival is the Tuesday before Ash Wednesday
# which is 40 days before easter excluding sundays
self[easter_date + td(days=-47)] = "Carnaval"

self[date(year, FEB, 3)] = "Dia dos Heróis Moçambicanos"
self[date(year, APR, 7)] = "Dia da Mulher Moçambicana"
self[date(year, MAY, 1)] = "Dia Mundial do Trabalho"
self[date(year, JUN, 25)] = "Dia da Independência Nacional"
self[date(year, SEP, 7)] = "Dia da Vitória"
self[date(year, SEP, 25)] = "Dia das Forças Armadas"
self[date(year, OCT, 4)] = "Dia da Paz e Reconciliação"
self[date(year, DEC, 25)] = "Dia de Natal e da Família"

# whenever a public holiday falls on a Sunday,
# it rolls over to the following Monday
if self.observed:
for k, v in list(self.items()):
if self._is_sunday(k) and k.year == year:
self[k + td(days=+1)] = v + " (PONTE)"
_add_with_observed(date(year, FEB, 3), "Dia dos Heróis Moçambicanos")
_add_with_observed(date(year, APR, 7), "Dia da Mulher Moçambicana")
_add_with_observed(date(year, MAY, 1), "Dia Mundial do Trabalho")
_add_with_observed(
date(year, JUN, 25), "Dia da Independência Nacional"
)
_add_with_observed(date(year, SEP, 7), "Dia da Vitória")
_add_with_observed(date(year, SEP, 25), "Dia das Forças Armadas")
if year >= 1993:
_add_with_observed(
date(year, OCT, 4), "Dia da Paz e Reconciliação"
)
_add_with_observed(date(year, DEC, 25), "Dia de Natal e da Família")


class MZ(Mozambique):
Expand Down
53 changes: 28 additions & 25 deletions holidays/countries/namibia.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from dateutil.easter import easter

from holidays.constants import JAN, MAR, MAY, AUG, SEP, DEC
from holidays.constants import JAN, FEB, MAR, MAY, AUG, SEP, DEC
from holidays.holiday_base import HolidayBase


Expand All @@ -33,44 +33,47 @@ class Namibia(HolidayBase):
}

def _populate(self, year):
def _add_with_observed(hol_date: date, hol_name: str) -> None:
# https://tinyurl.com/lacorg5835
# As of 1991/2/1, whenever a public holiday falls on a Sunday,
# it rolls over to the monday, unless that monday is already
# a public holiday.
self[hol_date] = hol_name
if (
self.observed
and self._is_sunday(hol_date)
and hol_date >= date(1991, FEB, 1)
):
self[hol_date + td(days=+1)] = f"{hol_name} (Observed)"

if year <= 1989:
return None

super()._populate(year)

self[date(year, JAN, 1)] = "New Year's Day"
self[date(year, MAR, 21)] = "Independence Day"
_add_with_observed(date(year, JAN, 1), "New Year's Day")
_add_with_observed(date(year, MAR, 21), "Independence Day")

# Easter Calculation
easter_date = easter(year)
self[easter_date + td(days=-2)] = "Good Friday"
self[easter_date + td(days=+1)] = "Easter Monday"
self[easter_date + td(days=+39)] = "Ascension Day"
# --------END OF EASTER------------#

self[date(year, MAY, 1)] = "Workers' Day"
self[date(year, MAY, 4)] = "Cassinga Day"
self[date(year, MAY, 25)] = "Africa Day"
self[date(year, AUG, 26)] = "Heroes' Day"
_add_with_observed(date(year, MAY, 1), "Workers' Day")
_add_with_observed(date(year, MAY, 4), "Cassinga Day")
_add_with_observed(date(year, MAY, 25), "Africa Day")
_add_with_observed(date(year, AUG, 26), "Heroes' Day")

dt = date(year, SEP, 10)
if year >= 2005:
# http://www.lac.org.na/laws/2004/3348.pdf
self[dt] = "Day of the Namibian Women and Intr. Human Rights Day"
else:
self[dt] = "International Human Rights Day"
# http://www.lac.org.na/laws/2004/3348.pdf
_add_with_observed(
date(year, SEP, 10),
"Day of the Namibian Women and International Human Rights Day"
if year >= 2005
else "International Human Rights Day",
)

self[date(year, DEC, 25)] = "Christmas Day"
self[date(year, DEC, 26)] = "Family Day"

# https://tinyurl.com/lacorg5835
# As of 1991/2/1, whenever a public holiday falls on a Sunday,
# it rolls over to the monday, unless that monday is already
# a public holiday.
if self.observed:
for k, v in list(self.items()):
if self._is_sunday(k) and k.year == year:
self[k + td(days=+1)] = v + " (Observed)"
_add_with_observed(date(year, DEC, 26), "Family Day")


class NA(Namibia):
Expand Down
35 changes: 17 additions & 18 deletions holidays/countries/zambia.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,51 +54,50 @@ class Zambia(HolidayBase):
}

def _populate(self, year):
def _add_with_observed(hol_date: date, hol_name: str) -> None:
# whenever a public holiday falls on a Sunday,
# it rolls over to the following Monday
self[hol_date] = hol_name
if self.observed and self._is_sunday(hol_date):
self[hol_date + td(days=+1)] = f"{hol_name} (Observed)"

# Observed since 1965
if year <= 1964:
return None

super()._populate(year)

self[date(year, JAN, 1)] = "New Year's Day"
_add_with_observed(date(year, JAN, 1), "New Year's Day")

if year >= 1991:
self[date(year, MAR, 8)] = "International Women's Day"
_add_with_observed(date(year, MAR, 8), "International Women's Day")

self[date(year, MAR, 12)] = "Youth Day"
_add_with_observed(date(year, MAR, 12), "Youth Day")

easter_date = easter(year)
self[easter_date + td(days=-2)] = "Good Friday"
self[easter_date + td(days=-1)] = "Holy Saturday"
self[easter_date + td(days=+1)] = "Easter Monday"

if year >= 2022:
self[date(year, APR, 28)] = "Kenneth Kaunda Day"
_add_with_observed(date(year, APR, 28), "Kenneth Kaunda Day")

self[date(year, MAY, 1)] = "Labour Day"
self[date(year, MAY, 25)] = "Africa Freedom Day"
_add_with_observed(date(year, MAY, 1), "Labour Day")
_add_with_observed(date(year, MAY, 25), "Africa Freedom Day")

# 1st Monday of July = "Heroes' Day"
dt = date(year, JUL, 1) + rd(weekday=MO)
self[dt] = "Heroes' Day"
self[dt + td(days=+1)] = "Unity Day"

# 1st Monday of Aug = "Farmers' Day"
dt = date(year, AUG, 1) + rd(weekday=MO)
self[dt] = "Farmers' Day"
self[date(year, AUG, 1) + rd(weekday=MO)] = "Farmers' Day"

if year >= 2015:
self[date(year, OCT, 18)] = "National Prayer Day"

self[date(year, OCT, 24)] = "Independence Day"
self[date(year, DEC, 25)] = "Christmas Day"
_add_with_observed(date(year, OCT, 18), "National Prayer Day")

# whenever a public holiday falls on a Sunday,
# it rolls over to the following Monday
if self.observed:
for k, v in list(self.items()):
if k.year == year and self._is_sunday(k):
self[k + td(days=+1)] = v + " (Observed)"
_add_with_observed(date(year, OCT, 24), "Independence Day")
_add_with_observed(date(year, DEC, 25), "Christmas Day")


class ZM(Zambia):
Expand Down
46 changes: 25 additions & 21 deletions holidays/countries/zimbabwe.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,48 +29,52 @@ class Zimbabwe(HolidayBase):
country = "ZW"

def _populate(self, year):
def _add_with_observed(
hol_date: date, hol_name: str, days: int = +1
) -> None:
self[hol_date] = hol_name
if self.observed and self._is_sunday(hol_date):
self[hol_date + td(days=days)] = f"{hol_name} (Observed)"

if year <= 1987:
return None

super()._populate(year)

self[date(year, JAN, 1)] = "New Year's Day"
_add_with_observed(date(year, JAN, 1), "New Year's Day")

# https://en.wikipedia.org/wiki/Robert_Gabriel_Mugabe_National_Youth_Day
if year >= 2018:
self[
date(year, FEB, 21)
] = "Robert Gabriel Mugabe National Youth Day"
_add_with_observed(
date(year, FEB, 21), "Robert Gabriel Mugabe National Youth Day"
)

easter_date = easter(year)
self[easter_date + td(days=-2)] = "Good Friday"
self[easter_date + td(days=-1)] = "Easter Saturday"
self[easter_date + td(days=+1)] = "Easter Monday"

self[date(year, APR, 18)] = "Independence Day"
# In 2049, 2055, 2060 Apr 19 is Easter Monday,
# so observed on Apr 20 (Tue)
_add_with_observed(
date(year, APR, 18),
"Independence Day",
+2 if year in {2049, 2055, 2060} else +1,
)

self[date(year, MAY, 1)] = "Workers' Day"
self[date(year, MAY, 25)] = "Africa Day"
_add_with_observed(date(year, MAY, 1), "Workers' Day")
_add_with_observed(date(year, MAY, 25), "Africa Day")

# 2nd Monday of August
zimbabwe_heroes_day = date(year, AUG, 1) + rd(weekday=MO(+2))
self[zimbabwe_heroes_day] = "Zimbabwe Heroes' Day"

# Tuesday after 2nd Monday of August
defence_forces_day = zimbabwe_heroes_day + td(days=+1)
self[defence_forces_day] = "Defense Forces Day"

self[date(year, DEC, 22)] = "Unity Day"
self[date(year, DEC, 25)] = "Christmas Day"
self[date(year, DEC, 26)] = "Boxing Day"

if self.observed:
for k, v in list(self.items()):
if self._is_sunday(k) and k.year == year:
dt = k + td(days=+1)
while self.get(dt):
dt += td(days=+1)
self[dt] = v + " (Observed)"
self[zimbabwe_heroes_day + td(days=+1)] = "Defense Forces Day"

_add_with_observed(date(year, DEC, 22), "Unity Day")
_add_with_observed(date(year, DEC, 25), "Christmas Day", days=+2)
_add_with_observed(date(year, DEC, 26), "Boxing Day")


class ZW(Zimbabwe):
Expand Down
103 changes: 68 additions & 35 deletions tests/countries/test_mozambique.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,79 @@
# Website: https://github.com/dr-prodigy/python-holidays
# License: MIT (see LICENSE file)

import unittest
from holidays.countries.mozambique import Mozambique, MZ, MOZ
from tests.common import TestCase

import holidays


class TestMozambique(unittest.TestCase):
class TestMozambique(TestCase):
def setUp(self):
self.holidays = holidays.MZ()

def test_new_years(self):
self.assertIn("1975-01-01", self.holidays)
self.assertIn("2017-01-01", self.holidays)
self.assertIn("2999-01-01", self.holidays)
self.assertIn("2017-01-02", self.holidays) # sunday

def test_carnival(self):
self.assertIn("1994-02-15", self.holidays)
self.assertIn("2002-02-12", self.holidays)
self.assertIn("2010-02-16", self.holidays)
self.assertIn("2017-02-28", self.holidays)
self.assertIn("2018-02-13", self.holidays)
self.assertIn("2019-03-05", self.holidays)
self.assertIn("2020-02-25", self.holidays)
self.assertIn("2021-02-16", self.holidays)
self.assertIn("2022-03-01", self.holidays)
self.holidays = Mozambique()

def test_easter(self):
self.assertIn("2017-04-14", self.holidays)
self.assertIn("2020-04-10", self.holidays)
self.assertIn("1994-04-01", self.holidays)
def test_country_aliases(self):
self.assertCountryAliases(Mozambique, MZ, MOZ)

def test_no_holidays(self):
self.assertNoHolidays(Mozambique(years=1974))

def test_not_holiday(self):
self.assertNotIn("2018-02-04", self.holidays)
self.assertNotIn("2015-04-13", self.holidays)
self.assertNotIn("2018-03-23", self.holidays)
def test_holidays(self):
for year in range(1975, 2050):
self.assertHoliday(
f"{year}-01-01",
f"{year}-02-03",
f"{year}-04-07",
f"{year}-05-01",
f"{year}-06-25",
f"{year}-09-07",
f"{year}-09-25",
f"{year}-12-25",
)

def test_pre1974(self):
# Holidays not defined since 1975
self.assertEqual(len(holidays.Mozambique(years=[1974])), 0)
self.assertNoHoliday(f"{year}-10-04" for year in range(1975, 1993))
self.assertNoHolidayName(
"Dia da Paz e Reconciliação",
Mozambique(years=range(1975, 1993)),
)
self.assertHoliday(f"{year}-10-04" for year in range(1993, 2050))

def test_easter(self):
self.assertHoliday(
# Good Friday
"2018-03-30",
"2019-04-19",
"2020-04-10",
"2021-04-02",
"2022-04-15",
# Carnival
"2018-02-13",
"2019-03-05",
"2020-02-25",
"2021-02-16",
"2022-03-01",
)

def test_observed(self):
not_observed = holidays.MZ(observed=False)
self.assertNotIn("2017-01-02", not_observed)
dt = (
"2011-05-02",
"2011-09-26",
"2011-12-26",
"2012-01-02",
"2013-02-04",
"2013-04-08",
"2014-09-08",
"2015-10-05",
"2016-05-02",
"2016-09-26",
"2016-12-26",
"2017-01-02",
"2017-06-26",
"2019-02-04",
"2019-04-08",
"2020-10-05",
"2022-05-02",
"2022-09-26",
"2022-12-26",
"2023-01-02",
"2023-06-26",
)
self.assertHoliday(dt)
self.assertNoHoliday(Mozambique(observed=False), dt)
Loading

0 comments on commit c31a54a

Please sign in to comment.