Repository: peopledoc/workalendar
Pull request #339 (2019-03-14 14:40:05)

Add Barbados calendar
replaces and thus, closes #333

- [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.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`` (#)"

I've not found much of official sources, here are examples of two years on the official websites:
https://web.archive.org/web/20090806030650/http://www.barbados.gov.bb/bdospublichol2.htm
https://labour.gov.bb/wp-content/uploads/PDF/Public-Holidays-for-the-Year-2019.pdf


brunobord: Could you please have a look at this @ludsoft?
ludsoft: It is indeed cleaner than my implementation. You changes look good to me. Thanks.

Patch:
diff --git a/Changelog.md b/Changelog.md
index cac7489..518df09 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -3,6 +3,7 @@
 ## master (unreleased)
 
 - Added isolated tests for shifting mechanics in USA calendars - previously untested (#335).
+- Added Barbados by @ludsoft.
 
 ## v4.2.0 (2019-02-21)
 
diff --git a/README.rst b/README.rst
index 7d4ff6b..481d167 100644
--- a/README.rst
+++ b/README.rst
@@ -117,6 +117,7 @@ Europe
 America
 -------
 
+* Barbados
 * Brazil (all states, cities and for bank transactions, except the city of Viana)
 * Chile
 * Colombia
diff --git a/workalendar/america/__init__.py b/workalendar/america/__init__.py
index 7285f66..7d8117b 100644
--- a/workalendar/america/__init__.py
+++ b/workalendar/america/__init__.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+from .barbados import Barbados
 from .brazil import (
     Brazil, BrazilAcre, BrazilAlagoas, BrazilAmapa, BrazilAmazonas,
     BrazilBahia, BrazilCeara, BrazilDistritoFederal, BrazilEspiritoSanto,
@@ -23,6 +24,7 @@ from .paraguay import Paraguay
 
 
 __all__ = (
+    # Brazil & its states.
     'Brazil',
     'BrazilAcre',
     'BrazilAlagoas',
@@ -72,6 +74,7 @@ __all__ = (
     'NorthwestTerritories',
     'Nunavut',
     # Other american countries
+    'Barbados',
     'Chile',
     'Colombia',
     'Mexico',
diff --git a/workalendar/america/barbados.py b/workalendar/america/barbados.py
new file mode 100644
index 0000000..60b0aa3
--- /dev/null
+++ b/workalendar/america/barbados.py
@@ -0,0 +1,57 @@
+from __future__ import absolute_import, division, print_function
+from datetime import timedelta
+from copy import copy
+
+from ..core import WesternCalendar, ChristianMixin
+from ..core import SUN, MON
+from ..registry import iso_register
+
+
+@iso_register("BB")
+class Barbados(WesternCalendar, ChristianMixin):
+    "Barbados"
+
+    include_good_friday = True
+    include_easter_sunday = True
+    include_easter_monday = True
+    include_whit_monday = True
+    include_boxing_day = True
+
+    # All holiday are shifted if on a Sunday
+    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+        (1, 21, "Errol Barrow Day"),
+        (4, 28, "National Heroes Day"),
+        (5, 1, "Labour Day"),
+        (8, 1, "Emancipation Day"),
+        (11, 30, "Independance Day"),
+    )
+
+    def get_kadooment_day(self, year):
+        """
+        First Monday of August.
+        """
+        return (Barbados.get_nth_weekday_in_month(year, 8, MON),
+                "Kadooment Day")
+
+    def get_variable_days(self, year):
+        """
+        Return variable holidays of the Barbados calendar.
+        """
+        days = super(Barbados, self).get_variable_days(year)
+        days.append(self.get_kadooment_day(year))
+        return days
+
+    def get_fixed_holidays(self, year):
+        """
+        Return fixed holidays of the Barbados calendar.
+
+        A shift day is appended if a fixed holiday happens on SUN.
+        """
+        days = super(Barbados, self).get_fixed_holidays(year)
+        days_to_shift = copy(days)
+        for day, label in days_to_shift:
+            if day.weekday() == SUN:
+                days.append(
+                    (day + timedelta(days=1), "%s %s" % (label, "(shifted)"))
+                )
+        return days


Test patch:
diff --git a/workalendar/tests/test_america.py b/workalendar/tests/test_america.py
index a223f5b..8f0aeb9 100644
--- a/workalendar/tests/test_america.py
+++ b/workalendar/tests/test_america.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 from datetime import date
 from workalendar.tests import GenericCalendarTest
-from workalendar.america import Colombia
+from workalendar.america import Colombia, Barbados
 from workalendar.america import Mexico, Chile, Panama, Paraguay
 
 
@@ -149,3 +149,60 @@ class ParaguayTest(GenericCalendarTest):
         # Boqueron Battle Victory Day: moved to October 2nd for 2017
         self.assertNotIn(date(2017, 9, 29), holidays)
         self.assertIn(date(2017, 10, 2), holidays)
+
+
+class BarbadosTest(GenericCalendarTest):
+    cal_class = Barbados
+
+    def test_holidays_2009(self):
+        holidays = self.cal.holidays_set(2009)
+        self.assertIn(date(2009, 1, 1), holidays)
+        self.assertIn(date(2009, 1, 21), holidays)  # Errol Barrow Day
+        self.assertIn(date(2009, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2009, 4, 12), holidays)  # Easter Sunday
+        self.assertIn(date(2009, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2009, 4, 28), holidays)  # National Heroes Day
+        self.assertIn(date(2009, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2009, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2009, 8, 1), holidays)  # Emancipation Day
+        self.assertIn(date(2009, 8, 3), holidays)  # Kabooment Day
+        self.assertIn(date(2009, 11, 30), holidays)  # Independant Day
+        self.assertIn(date(2009, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2009, 12, 26), holidays)  # Boxing Day
+
+    def test_holidays_2018(self):
+        holidays = self.cal.holidays_set(2018)
+        self.assertIn(date(2018, 1, 1), holidays)
+        self.assertIn(date(2018, 1, 21), holidays)  # Errol Barrow Day
+        self.assertIn(date(2018, 1, 22), holidays)  # Errol Barrow Day (shift)
+        self.assertIn(date(2018, 3, 30), holidays)  # Good Friday
+        self.assertIn(date(2018, 4, 1), holidays)  # Easter Sunday
+        self.assertIn(date(2018, 4, 2), holidays)  # Easter Monday
+        self.assertIn(date(2018, 4, 28), holidays)  # National Heroes Day
+        self.assertIn(date(2018, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2018, 5, 21), holidays)  # Whit Monday
+        self.assertIn(date(2018, 8, 1), holidays)  # Emancipation Day
+        self.assertIn(date(2018, 8, 6), holidays)  # Kabooment Day
+        self.assertIn(date(2018, 11, 30), holidays)  # Independant Day
+        self.assertIn(date(2018, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2018, 12, 26), holidays)  # Boxing Day
+
+    def test_holidays_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 1, 1), holidays)
+        self.assertIn(date(2019, 1, 21), holidays)  # Errol Barrow Day
+        self.assertIn(date(2019, 4, 19), holidays)  # Good Friday
+        self.assertIn(date(2019, 4, 21), holidays)  # Easter Sunday
+        self.assertIn(date(2019, 4, 22), holidays)  # Easter Monday
+
+        # National Heroes Day & shift
+        self.assertIn(date(2019, 4, 28), holidays)
+        self.assertIn(date(2019, 4, 29), holidays)  # shft'd
+
+        self.assertIn(date(2019, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2019, 6, 10), holidays)  # Whit Monday
+        self.assertIn(date(2019, 8, 1), holidays)  # Emancipation Day
+        self.assertIn(date(2019, 8, 5), holidays)  # Kabooment Day
+        self.assertIn(date(2019, 11, 30), holidays)  # Independant Day
+        self.assertIn(date(2019, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2019, 12, 26), holidays)  # Boxing Day