Repository: peopledoc/workalendar (python)

Issue:
HK has not yet implemented Sat shift
        # Boxing day & XMas shift
        shifts = self.shift_christmas_boxing_days(year=year)
        days.extend(shifts)

so for 2020, Dec 28 (Mon) is not a holiday, please fix

Pull request description:
Hong Kong saturday is a working day
closes #477 

- [x] Tests with a significant number of years to be tested for your calendar.
- [x] Changelog amended with a mention describing your changes.


Patch:
diff --git a/Changelog.md b/Changelog.md
index 767b48e..9c3d974 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,7 +2,8 @@
 
 # master (unreleased)
 
-Nothing here yet.
+- Fixing Hong-Kong calendar, where SAT are common working days (#477).
+- Introducing Hong-Kong Bank calendar. For banks, Saturdays are non-working days (#477).
 
 ## v8.2.2 (2020-04-10)
 
diff --git a/workalendar/asia/__init__.py b/workalendar/asia/__init__.py
index 9d21335..2a65e49 100644
--- a/workalendar/asia/__init__.py
+++ b/workalendar/asia/__init__.py
@@ -1,5 +1,5 @@
 from .china import China
-from .hong_kong import HongKong
+from .hong_kong import HongKong, HongKongBank
 from .japan import Japan, JapanBank
 from .malaysia import Malaysia
 from .qatar import Qatar
@@ -12,6 +12,7 @@ from .israel import Israel
 __all__ = (
     'China',
     'HongKong',
+    'HongKongBank',
     'Japan',
     'JapanBank',
     'Malaysia',
diff --git a/workalendar/asia/hong_kong.py b/workalendar/asia/hong_kong.py
index cac944c..a056385 100644
--- a/workalendar/asia/hong_kong.py
+++ b/workalendar/asia/hong_kong.py
@@ -1,6 +1,9 @@
-from datetime import timedelta
+from datetime import date, timedelta
 
-from ..core import ChineseNewYearCalendar, WesternCalendar, ChristianMixin
+from ..core import (
+    ChineseNewYearCalendar, WesternCalendar, ChristianMixin,
+    SUN, SAT
+)
 from ..astronomy import solar_term
 from ..registry_tools import iso_register
 
@@ -13,6 +16,8 @@ class HongKong(WesternCalendar, ChineseNewYearCalendar, ChristianMixin):
     include_easter_monday = True
     include_boxing_day = True
 
+    WEEKEND_DAYS = (SUN,)
+
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (5, 1, "Labour Day"),
         (7, 1, "SAR Establishment Day"),
@@ -54,8 +59,24 @@ class HongKong(WesternCalendar, ChineseNewYearCalendar, ChristianMixin):
             (ChineseNewYearCalendar.lunar(year, 9, 9), "Chung Yeung Festival"),
         ])
 
-        # Boxing day & XMas shift
-        shifts = self.shift_christmas_boxing_days(year=year)
+        # Shifting all potential holidays that fall on SUN.
+        shifts = []
+        for day, label in days:
+            if day.weekday() == SUN:
+                shifts.append((
+                    day + timedelta(days=1), "{} (shift)".format(label)
+                ))
+        # Special case for Boxing Day.
+        # If Christmas day is on SUN, the December 27th is also a holiday
+        if date(year, 12, 25).weekday() == SUN:
+            shifts.append(
+                (date(year, 12, 27), "The second weekday after Christmas")
+            )
         days.extend(shifts)
 
         return days
+
+
+class HongKongBank(HongKong):
+    "Hong Kong Bank"
+    WEEKEND_DAYS = (SAT, SUN)


Test patch:
diff --git a/workalendar/tests/test_asia.py b/workalendar/tests/test_asia.py
index d2cfa77..2d967c2 100644
--- a/workalendar/tests/test_asia.py
+++ b/workalendar/tests/test_asia.py
@@ -3,7 +3,8 @@ from datetime import date
 from . import GenericCalendarTest
 
 from ..asia import (
-    HongKong, Japan, JapanBank, Qatar, Singapore,
+    HongKong, HongKongBank,
+    Japan, JapanBank, Qatar, Singapore,
     SouthKorea, Taiwan, Malaysia, China, Israel
 )
 from ..asia.china import holidays as china_holidays
@@ -104,9 +105,10 @@ class HongKongTest(GenericCalendarTest):
     cal_class = HongKong
 
     def test_year_2010(self):
-        """ Interesting because Christmas fell on a Saturday and CNY fell
-            on a Sunday, so didn't roll, and Ching Ming was on the same day
-            as Easter Monday """
+        # Interesting because Christmas fell on a Saturday and CNY fell
+        # on a Sunday, so didn't roll, and Ching Ming was on the same day
+        # as Easter Monday
+        # https://www.gov.hk/en/about/abouthk/holiday/2010.htm
         holidays = self.cal.holidays_set(2010)
         self.assertIn(date(2010, 1, 1), holidays)    # New Year
         self.assertIn(date(2010, 2, 13), holidays)   # Chinese new year (shift)
@@ -128,6 +130,7 @@ class HongKongTest(GenericCalendarTest):
         self.assertIn(date(2010, 12, 27), holidays)  # Boxing Day (shifted)
 
     def test_year_2013(self):
+        # https://www.gov.hk/en/about/abouthk/holiday/2013.htm
         holidays = self.cal.holidays_set(2013)
         self.assertIn(date(2013, 1, 1), holidays)    # New Year
         self.assertIn(date(2013, 2, 11), holidays)   # Chinese new year
@@ -148,6 +151,7 @@ class HongKongTest(GenericCalendarTest):
         self.assertIn(date(2013, 12, 26), holidays)  # Boxing Day
 
     def test_year_2016(self):
+        # https://www.gov.hk/en/about/abouthk/holiday/2016.htm
         holidays = self.cal.holidays_set(2016)
         self.assertIn(date(2016, 1, 1), holidays)    # New Year
         self.assertIn(date(2016, 2, 8), holidays)    # Chinese new year
@@ -157,6 +161,7 @@ class HongKongTest(GenericCalendarTest):
         self.assertIn(date(2016, 3, 26), holidays)   # Day after Good Friday
         self.assertIn(date(2016, 3, 28), holidays)   # Easter Monday
         self.assertIn(date(2016, 4, 4), holidays)    # Ching Ming
+        self.assertIn(date(2016, 5, 1), holidays)    # Labour Day (SUN)
         self.assertIn(date(2016, 5, 2), holidays)    # Labour Day (shifted)
         self.assertIn(date(2016, 5, 14), holidays)   # Buddha's Birthday
         self.assertIn(date(2016, 6, 9), holidays)    # Tuen Ng Festival
@@ -164,10 +169,12 @@ class HongKongTest(GenericCalendarTest):
         self.assertIn(date(2016, 9, 16), holidays)   # Day after Mid-Autumn
         self.assertIn(date(2016, 10, 1), holidays)   # National Day
         self.assertIn(date(2016, 10, 10), holidays)  # Chung Yeung Festival
-        self.assertIn(date(2016, 12, 26), holidays)  # Christmas Day (shifted)
-        self.assertIn(date(2016, 12, 27), holidays)  # Boxing Day (shifted)
+        self.assertIn(date(2016, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2016, 12, 26), holidays)  # Christmas + Boxing Day
+        self.assertIn(date(2016, 12, 27), holidays)  # Second weekday
 
     def test_year_2017(self):
+        # https://www.gov.hk/en/about/abouthk/holiday/2017.htm
         holidays = self.cal.holidays_set(2017)
         self.assertIn(date(2017, 1, 2), holidays)    # New Year (shifted)
         self.assertIn(date(2017, 1, 28), holidays)   # Chinese new year
@@ -204,6 +211,51 @@ class HongKongTest(GenericCalendarTest):
         self.assertIn(date(2017, 4, 4), self.cal.holidays_set(2017))
         self.assertIn(date(2018, 4, 5), self.cal.holidays_set(2018))
 
+    def test_holidays_2020(self):
+        # https://www.gov.hk/en/about/abouthk/holiday/2020.htm
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)    # New Year (shifted)
+        self.assertIn(date(2020, 1, 25), holidays)   # Chinese new year
+        self.assertIn(date(2020, 1, 27), holidays)   # Chinese new year
+        self.assertIn(date(2020, 1, 28), holidays)   # Chinese new year
+        self.assertIn(date(2020, 4, 4), holidays)    # Ching Ming
+        self.assertIn(date(2020, 4, 10), holidays)   # Good Friday
+        self.assertIn(date(2020, 4, 11), holidays)   # Day after Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)   # Easter Monday
+        self.assertIn(date(2020, 4, 30), holidays)    # Buddha's Birthday
+        self.assertIn(date(2020, 5, 1), holidays)    # Labour Day
+        self.assertIn(date(2020, 6, 25), holidays)   # Tuen Ng Festival
+        self.assertIn(date(2020, 7, 1), holidays)    # HK SAR Establishment Day
+        self.assertIn(date(2020, 10, 1), holidays)   # National Day
+        self.assertIn(date(2020, 10, 2), holidays)   # Day after Mid-Autumn
+        self.assertIn(date(2020, 10, 26), holidays)  # Chung Yeung Festival
+        self.assertIn(date(2020, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2020, 12, 26), holidays)  # Boxing Day
+        # Special: Boxing day is not shifted, because it's not a SUN
+        self.assertNotIn(date(2020, 12, 28), holidays)
+
+    def test_are_saturdays_working_days(self):
+        # Let's start with february 6th.
+        start = date(2020, 2, 6)
+        # If SAT was a non-working day, it would have been the 13th.
+        self.assertEqual(
+            self.cal.add_working_days(start, 5),
+            date(2020, 2, 12)
+        )
+
+
+class HongKongBankTest(HongKongTest):
+    cal_class = HongKongBank
+
+    def test_are_saturdays_working_days(self):
+        # Let's start with february 6th.
+        start = date(2020, 2, 6)
+        # If SAT and SUN are non-working days
+        self.assertEqual(
+            self.cal.add_working_days(start, 5),
+            date(2020, 2, 13)
+        )
+
 
 class JapanTest(GenericCalendarTest):