Repository: peopledoc/workalendar
Pull request #484 (2020-04-15 16:59:23)

Add Kenyan calendar
- [x] Tests with a significant number of years to be tested for your calendar.
- [x] Docstrings for the Calendar class and specific methods.
- [x] Use the ``workalendar.registry_tools.iso_register`` decorator to register your new calendar using ISO codes (optional).
- [x] Calendar country / label added to the README.rst file.
- [x] Changelog amended with a mention like: "Added ``<country>`` by ``@pseudo`` (#)".


KidkArolis: Fixed the linting.

Patch:
diff --git a/Changelog.md b/Changelog.md
index a35fda8..60dac0f 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,7 +2,7 @@
 
 ## master (unreleased)
 
-Nothing here yet.
+- Added Kenyan calendar, by @KidkArolis (#484)
 
 ## v8.3.0 (2020-04-14)
 
diff --git a/README.rst b/README.rst
index 5215290..b79c6c9 100644
--- a/README.rst
+++ b/README.rst
@@ -171,6 +171,7 @@ Africa
 * Angola
 * Benin
 * Ivory Coast
+* Kenya
 * Madagascar
 * São Tomé
 * South Africa
diff --git a/workalendar/africa/__init__.py b/workalendar/africa/__init__.py
index b4e347c..bfb31a6 100644
--- a/workalendar/africa/__init__.py
+++ b/workalendar/africa/__init__.py
@@ -1,6 +1,7 @@
 from .algeria import Algeria
 from .benin import Benin
 from .ivory_coast import IvoryCoast
+from .kenya import Kenya
 from .madagascar import Madagascar
 from .sao_tome import SaoTomeAndPrincipe
 from .south_africa import SouthAfrica
@@ -11,6 +12,7 @@ __all__ = (
     'Algeria',
     'Benin',
     'IvoryCoast',
+    'Kenya',
     'Madagascar',
     'SaoTomeAndPrincipe',
     'SouthAfrica',
diff --git a/workalendar/africa/kenya.py b/workalendar/africa/kenya.py
new file mode 100644
index 0000000..ac28d90
--- /dev/null
+++ b/workalendar/africa/kenya.py
@@ -0,0 +1,71 @@
+from copy import copy
+from datetime import timedelta, date
+
+from ..core import WesternCalendar, IslamicMixin, ChristianMixin, SUN
+from ..registry_tools import iso_register
+
+
+@iso_register('KE')
+class Kenya(WesternCalendar, IslamicMixin, ChristianMixin):
+    "Kenya"
+    include_good_friday = True
+    include_easter_monday = True
+    # Islamic holidays
+    include_eid_al_fitr = True
+    include_day_of_sacrifice = True
+    shift_sunday_holidays = True
+
+    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+        (5, 1, "Labour Day"),
+        (6, 1, "Madaraka Day"),
+        (10, 20, "Mashujaa Day"),
+        (12, 12, "Jamhuri Day"),
+        (12, 31, "New Years Eve"),
+    )
+
+    def get_fixed_holidays(self, year):
+        days = super().get_fixed_holidays(year)
+
+        if year >= 2020:
+            days.append((date(year, 2, 11), 'Moi Memorial Day'))
+
+        # Moi Day renamed
+        huduma_day_label = "Moi Day"
+        if year >= 2020:
+            huduma_day_label = "Huduma Day"
+        days.append((date(year, 10, 10), huduma_day_label))
+
+        # Boxing day renamed
+        boxing_day_label = "Boxing Day"
+        if year >= 2020:
+            boxing_day_label = "Utamaduni Day"
+        days.append((date(year, 12, 26), boxing_day_label))
+
+        return days
+
+    def get_shifted_holidays(self, dates):
+        """
+        Taking a list of existing holidays, yield a list of 'shifted' days if
+        the holiday falls on SUN, excluding the Islamic holidays.
+        """
+        for holiday, label in dates:
+            if (holiday.weekday() == SUN and
+                    label != self.eid_al_fitr_label and
+                    label != self.day_of_sacrifice_label):
+                yield (
+                    holiday + timedelta(days=1),
+                    label + ' Shift'
+                )
+
+    def get_calendar_holidays(self, year):
+        """
+        Take into account the eventual shift to the next MON if any holiday
+        falls on SUN.
+        """
+        # Unshifted days are here:
+        days = super().get_calendar_holidays(year)
+        if self.shift_sunday_holidays:
+            days_to_inspect = copy(days)
+            for day_shifted in self.get_shifted_holidays(days_to_inspect):
+                days.append(day_shifted)
+        return days


Test patch:
diff --git a/workalendar/tests/test_africa.py b/workalendar/tests/test_africa.py
index 3693121..990dabc 100644
--- a/workalendar/tests/test_africa.py
+++ b/workalendar/tests/test_africa.py
@@ -5,6 +5,7 @@ from ..africa import (
     Angola,
     Benin,
     IvoryCoast,
+    Kenya,
     Madagascar,
     SaoTomeAndPrincipe,
     SouthAfrica,
@@ -547,3 +548,43 @@ class AngolaTest(GenericCalendarTest):
         self.assertIn(date(2018, 11, 11), holidays)
         # Dia do Natal – 25 de Dezembro
         self.assertIn(date(2018, 12, 25), holidays)  # Natal
+
+
+class KenyaTest(GenericCalendarTest):
+    cal_class = Kenya
+
+    def test_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 1, 1), holidays)  # New Year
+        self.assertIn(date(2019, 4, 19), holidays)  # Good Friday
+        self.assertIn(date(2019, 4, 22), holidays)  # Easter Monday
+        self.assertIn(date(2019, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2019, 6, 1), holidays)  # Madaraka Day
+        self.assertIn(date(2019, 6, 5), holidays)  # Eid al-Fitr
+        self.assertIn(date(2019, 8, 12), holidays)  # Eid al-Adha
+        self.assertIn(date(2019, 10, 10), holidays)  # Moi Day (old name)
+        self.assertIn(date(2019, 10, 20), holidays)  # Mashujaa Day
+        self.assertIn(date(2019, 10, 21), holidays)  # Mashujaa Day shift
+        self.assertIn(date(2019, 12, 12), holidays)  # Jamhuri Day
+        self.assertIn(date(2019, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2019, 12, 26), holidays)  # Boxing Day (old name)
+        self.assertIn(date(2019, 12, 31), holidays)  # New Years Eve
+        self.assertEquals(len(holidays), 14)
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New Year
+        self.assertIn(date(2020, 2, 11), holidays)  # Moi Memorial (as of 2020)
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2020, 5, 24), holidays)  # Eid al-Fitr
+        self.assertIn(date(2020, 6, 1), holidays)  # Madaraka Day
+        self.assertIn(date(2020, 7, 31), holidays)  # Eid al-Adha
+        self.assertIn(date(2020, 10, 10), holidays)  # Huduma Day
+        self.assertIn(date(2020, 10, 20), holidays)  # Mashujaa Day
+        self.assertIn(date(2020, 12, 12), holidays)  # Jamhuri Day
+        self.assertIn(date(2020, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2020, 12, 26), holidays)  # Utamaduni Day
+        self.assertIn(date(2020, 12, 31), holidays)  # New Years Eve
+        self.assertEquals(len(holidays), 14)