Repository: peopledoc/workalendar
Pull request #440 (2019-12-11 15:50:00)

Ukraine
refs #

<!-- if your contribution is a new calendar -->

For information, read and make sure you're okay with the [Contributing guidelines](https://github.com/novafloss/workalendar/blob/master/contributing.md#adding-new-calendars).

- [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`` (#)". **Note** *Please do NOT change the version number here. It's the project maintainers' duty.*

<!-- if your contribution is a fix -->

- [x] Tests with a significant number of years to be tested for your calendar.
- [x] Changelog amended with a mention describing your changes. **Note** *Please do NOT change the version number here. It's the project maintainers' duty.*

<!-- Release management

- Commit for the tag:
    - [ ] Edit version in setup.py
    - [ ] Add version in Changelog.md ; trim things
    - [ ] Push & wait for the tests to be green
    - [ ] tag me.
    - [ ] build sdist + wheel packages (``make package``)
- Back to dev commit:
    - [ ] Edit version in setup.py
    - [ ] Add the "master / nothing to see here" in Changelog.md
    - [ ] Push & wait for the tests to be green
- [ ] Merge --ff
- Github stuff
    - [ ] Push tag in Github
    - [ ] Edit release on Github using the changelog.
    - [ ] Delete branch
- [ ] upload release on PyPI using ``twine``
- [ ] (*optional*) Make feeback on the various PR or issues.

 -->



Patch:
diff --git a/Changelog.md b/Changelog.md
index 7d127e6..4999259 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,6 +1,7 @@
 # CHANGELOG
 
 ## master (unreleased)
+- Added Ukraine calendar, by @apelloni.
 
 ### ISO Registry API Change
 
diff --git a/README.rst b/README.rst
index 8bf6c1a..f0eaa31 100644
--- a/README.rst
+++ b/README.rst
@@ -116,6 +116,7 @@ Europe
   * Geneva
 
 * Turkey
+* Ukraine
 * United Kingdom (incl. Northern Ireland, Scotland and all its territories)
 
 America
diff --git a/workalendar/europe/__init__.py b/workalendar/europe/__init__.py
index 79dc51c..4407c43 100644
--- a/workalendar/europe/__init__.py
+++ b/workalendar/europe/__init__.py
@@ -32,6 +32,7 @@ from .slovenia import Slovenia
 from .spain import Spain, Catalonia
 from .sweden import Sweden
 from .switzerland import Switzerland, Vaud, Geneva
+from .ukraine import Ukraine
 from .united_kingdom import UnitedKingdom, UnitedKingdomNorthernIreland
 from .turkey import Turkey
 
@@ -90,6 +91,7 @@ __all__ = (
     'Switzerland',
     'Vaud',
     'Geneva',
+    'Ukraine',
     'UnitedKingdom',
     'UnitedKingdomNorthernIreland',
     'Turkey',
diff --git a/workalendar/europe/ukraine.py b/workalendar/europe/ukraine.py
new file mode 100644
index 0000000..aaca69c
--- /dev/null
+++ b/workalendar/europe/ukraine.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+from datetime import date
+from workalendar.core import WesternCalendar, OrthodoxMixin
+from ..registry_tools import iso_register
+
+
+@iso_register('UA')
+class Ukraine(OrthodoxMixin, WesternCalendar):
+    'Ukraine'
+
+    shift_sunday_holidays = True
+    shift_new_years_day = True
+
+    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+        (3, 8, "International Women’s Day"),
+        (5, 1, "Workers Solidarity Day"),
+        (5, 9, "Victory Day"),
+    )
+
+    include_christmas = False
+    include_good_friday = True
+    include_easter_sunday = True
+    include_easter_monday = True
+    include_whit_monday = True
+
+    def get_variable_days(self, year):
+        days = super(Ukraine, self).get_variable_days(year)
+
+        # Orthodox Christmas holiday is moved when it falls over the week
+        orthodox_christmas = date(year, 1, 7)
+        if orthodox_christmas.weekday() in self.get_weekend_days():
+            days.append((
+                self.find_following_working_day(orthodox_christmas),
+                "Orthodox Christmas (postponed)"))
+        else:
+            days.append((orthodox_christmas, "Orthodox Christmas"))
+
+        # Constitution Day was celebrated for the first time in 1996
+        if year >= 1996:
+            constitution_day = date(year, 6, 28)
+            if constitution_day.weekday() in self.get_weekend_days():
+                days.append((
+                    self.find_following_working_day(constitution_day),
+                    "Constitution Day (postponed)"))
+            else:
+                days.append((constitution_day, "Constitution Day"))
+
+        # Independence Day was first celebrated in 1991 on the 16th of June
+        # After the official independence has been moved to the 24th of August
+        if year == 1991:
+            days.append((date(year, 6, 16), "Independence Day"))
+        if year >= 1992:
+            independence_day = date(year, 8, 24)
+
+            if independence_day.weekday() in self.get_weekend_days():
+                days.append((
+                    self.find_following_working_day(independence_day),
+                    "Independence Day (postponed)"))
+            else:
+                days.append((independence_day, "Independence Day"))
+
+        # Defender of Ukraine from 2015
+        # https://en.wikipedia.org/wiki/Defender_of_Ukraine_Day
+        if year >= 2015:
+            days.append((date(year, 10, 14), "Day of Defender of Ukraine"))
+
+        # Catholic Christmas has become an holiday only starting from 2017
+        if year >= 2017:
+            days.append((date(year, 12, 25), "Christmas Day"))
+
+        # Workers Solidarity Day was celebrated also on the 2nd till 2017
+        if year <= 2017:
+            days.append((date(year, 5, 2), "Workers Solidarity Day"))
+
+        return days


Test patch:
diff --git a/workalendar/tests/test_europe.py b/workalendar/tests/test_europe.py
index dd8fc85..25aaec2 100644
--- a/workalendar/tests/test_europe.py
+++ b/workalendar/tests/test_europe.py
@@ -35,6 +35,7 @@ from workalendar.europe import Serbia
 from workalendar.europe import Spain, Catalonia
 from workalendar.europe import Slovenia
 from workalendar.europe import Switzerland, Vaud, Geneva
+from workalendar.europe import Ukraine
 from workalendar.europe import UnitedKingdom
 from workalendar.europe import UnitedKingdomNorthernIreland
 from workalendar.europe import EuropeanCentralBank
@@ -832,7 +833,62 @@ class Russia(GenericCalendarTest):
         self.assertIn(date(2018, 5, 1), holidays)  # Labour Day
         self.assertIn(date(2018, 5, 9), holidays)  # Victory Day
         self.assertIn(date(2018, 6, 12), holidays)  # National Day
-        self.assertIn(date(2018, 11, 4), holidays)  # Day of Unity
+        self.assertIn(date(2018, 11, 4), holidays)  # Independence Day
+
+
+class UkraineTest(GenericCalendarTest):
+    cal_class = Ukraine
+
+    def test_year_1991(self):
+        holidays = self.cal.holidays_set(1991)
+        self.assertIn(date(1991, 6, 16), holidays)  # Independence Day
+        self.assertNotIn(date(1991, 8, 24), holidays)
+
+    def test_year_1992(self):
+        holidays = self.cal.holidays_set(1992)
+        self.assertNotIn(date(1992, 6, 16), holidays)
+        self.assertIn(date(1992, 8, 24), holidays)  # Independence Day
+
+    def test_year_2010(self):
+        holidays = self.cal.holidays_set(2010)
+        self.assertIn(date(2010, 1, 1), holidays)  # New Year's Day
+        self.assertIn(date(2010, 1, 7), holidays)  # Christmas (Orthodox)
+        self.assertIn(date(2010, 3, 8), holidays)  # International Women's Day
+        self.assertIn(date(2010, 5, 1), holidays)  # Workers Solidarity Day
+        self.assertIn(date(2010, 5, 9), holidays)  # Victory Day
+        self.assertIn(date(2010, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2010, 6, 28), holidays)  # Constitution Day
+        self.assertIn(date(2010, 8, 24), holidays)  # Independence Day
+
+    def test_year_2015(self):
+        holidays = self.cal.holidays_set(2015)
+        self.assertIn(date(2015, 6, 29), holidays)  # Constitution Day (moved)
+        self.assertIn(date(2015, 10, 14), holidays)  # Defender of Ukraine
+
+    def test_year_2016(self):
+        holidays = self.cal.holidays_set(2016)
+        self.assertIn(date(2016, 1, 1), holidays)  # New Year's Day
+        self.assertIn(date(2016, 1, 7), holidays)  # Christmas (Orthodox)
+        self.assertIn(date(2016, 3, 8), holidays)  # International Women's Day
+        self.assertIn(date(2016, 5, 1), holidays)  # Workers Solidarity Day
+        self.assertIn(date(2016, 5, 2), holidays)  # Workers Solidarity Day
+        self.assertIn(date(2016, 5, 2), holidays)
+        self.assertIn(date(2016, 5, 9), holidays)  # Victory Day
+        self.assertIn(date(2016, 6, 20), holidays)
+        self.assertIn(date(2016, 6, 28), holidays)  # Constitution Day
+        self.assertIn(date(2016, 8, 24), holidays)  # Day of Unity
+        self.assertIn(date(2016, 10, 14), holidays)  # Defender of Ukraine
+        self.assertNotIn(date(2016, 12, 25), holidays)  # Christmas Day
+
+    def test_year_2017(self):
+        holidays = self.cal.holidays_set(2017)
+        self.assertIn(date(2017, 1, 2), holidays)  # New Year's Day (postponed)
+        self.assertIn(date(2017, 1, 9), holidays)  # Christmas Orthodox (moved)
+        self.assertIn(date(2017, 12, 25), holidays)  # Christmas Day
+
+    def test_year_2018(self):
+        holidays = self.cal.holidays_set(2018)
+        self.assertNotIn(date(2018, 5, 2), holidays)  # Workers Solidarity Day
 
 
 class UnitedKingdomTest(GenericCalendarTest):