Repository: jaraco/calendra (language: python)

Issue:
Sync with Workalendar 14
Calendra last synced with Workalendar at version 8, so is due for a refresh.


Discussion / hints:

Fix patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index dc876dc7..5aa4432b 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -2,12 +2,12 @@ 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).
+For information, read and make sure you're okay with the [Contributing guidelines](https://peopledoc.github.io/workalendar/contributing.html#adding-new-calendars).
 
 - [ ] Tests with a significant number of years to be tested for your calendar.
 - [ ] Docstrings for the Calendar class and specific methods.
 - [ ] Use the ``workalendar.registry_tools.iso_register`` decorator to register your new calendar using ISO codes (optional).
-- [ ] Calendar country / label added to the README.rst file.
+- [ ] Calendar country / label added to the README.md file.
 - [ ] 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 -->
@@ -18,13 +18,13 @@ For information, read and make sure you're okay with the [Contributing guideline
 <!-- Release management
 
 - Commit for the tag:
-    - [ ] Edit version in setup.py
+    - [ ] Edit version in setup.cfg
     - [ ] 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
+    - [ ] Edit version in setup.cfg
     - [ ] Add the "master / nothing to see here" in Changelog.md
     - [ ] Push & wait for the tests to be green
 - [ ] Merge --ff
diff --git a/CHANGES.rst b/CHANGES.rst
index 661b6cfa..439a6045 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,199 @@
+v7.0.0
+-------
+
+Incorporate changes from workalendar v14.1.0 (2020-12-10)
+
+- Fix Russia 2021 holidays, thx @MichalChromcak for the bug report (#578).
+
+Incorporate changes from workalendar v14.0.0 (2020-11-27)
+
+- Fixes
+
+  - Fix Russia calendar: non-working days are shifted to the next MON when they happen on the week-end (#589).
+  - Fix Russia New year holidays. It has become a week off since 2005 (related to #578).
+  - Added Russia COVID-19 non-working days for the year 2020 ; these days are not shifted to next MON (#578).
+  - Fixed Russia Christmas day ; December 25th is not a public holiday. Fixed several other Orthodox calendars (#530).
+  - Update China's public holidays for 2021, thanks @iamsk.
+
+- Minor changes
+
+  - Added a `daterange` function in `workalendar.core` module to iterate between two dates.
+
+Incorporate changes from workalendar v13.0.0 (2020-11-13)
+
+- Calendars
+
+  - Add optional school holidays to Netherlands calendar, by @Flix6x (#556).
+  - Add optional carnival to Netherlands calendar.
+
+- Documentation
+
+  - Moving the `contributing.md` file to the `docs/` directory (#573).
+  - Changed from `setup.py` to a nice `setup.cfg` file, thanks @ewjoachim (#576).
+  - Added documentation about class options (#572).
+  - Converted `README.rst` file into `README.md` (#575).
+  - Fixed Pull Request template to reference `setup.cfg` (#587).
+
+- Other changes
+
+  - Switched from Travis CI to Github Actions for CI jobs, thanks to @mgu.
+  - Added support of Python 3.9 (#557).
+  - Changed from `setup.py` to a nice `setup.cfg` file, thanks @ewjoachim (#576).
+  - Use the `setup.cfg` file in the key to cache in `ci.yml` file (#587).
+  - [OBSOLETE] Switched from bionic to focal on Travis CI (we've switched to GH actions after that).
+
+Incorporate changes from workalendar v12.1.0 (2020-10-16)
+
+- New calendars
+
+  - Added Spain regions: Andalusia, Aragon, Castile and León, Castilla-La Mancha, Canary Islands, Extremadura, Galicia, Balearic Islands, La Rioja, Community of Madrid, Murcia, Navarre, Asturias, Basque Country, Cantabria, Valencian Community (#531).
+  - Added all ISO codes for Spain regions - thx @ainarela for your help on this (#531).
+
+- Other changes
+
+  - Refactored Spain test modules (#531).
+  - Fix Catalonia calendar by removing *Sant Juan* day, which does not appear to be an official holiday (#531).
+  - Improve coverage of `workalendar/core.py` module (#546).
+  - Improve coverage for the Netherlands calendar - Queen's Day (#546).
+  - Improve coverage for the Romania calendar - Liberation day (#546).
+  - Improve coverage for the New Zealand calendar (#546).
+  - Added a tox entrypoint to ensure code is Python 3.6+, using ``pyupgrade`` (#566).
+  - Added the pyupgrade tox job to the test suite, amended contributing documentation (#566).
+
+Incorporate changes from workalendar v12.0.0 (2020-10-02)
+
+- **Deprecation:** Dropped support for Python 3.5. As of this version, workalendar now requires Python 3.6+ (#330).
+- Improve coverage of Singapore calendar (#546).
+
+Incorporate changes from workalendar v11.0.1 (2020-09-11)
+
+- Add ISO code decorator to Catalonia calendar, thanks to @jbagot (#551).
+- Improve coverage of South Africa calendar (#546).
+- Improve coverage of Brazil calendar (#546).
+- Improve coverage of Canada (Nunavut) calendar (#546).
+- Improve coverage of Israel calendar (#546).
+
+Incorporate changes from workalendar v11.0.0 (2020-09-04)
+
+New calendar
+
+- Added Mozambique calendar by @mr-shovel (#542).
+
+New feature
+
+- Added iCal export feature, initiated by @joooeey (#197).
+- Fix PRODID pattern for iCal exports: `"PRODID:-//workalendar//ical {__version__}//EN"`, using current workalendar version (#543).
+
+Incorporate changes from workalendar v10.4.0 (2020-08-28)
+
+- New calendar
+
+  - Added Monaco calendar by @joaopbnogueira (#538).
+
+- Major changes and bugfixes
+
+  - Migrating Labour Day as a worldwide holiday, disabled by default, but activated (to date) for about 50 countries (including label change when necessary), `contributing.md` documentation amended (#467).
+  - Bugfix: Avoid Cesar Chavez Day duplicated shifts by refactoring the California shift rules (#528).
+
+- Other changes
+
+  - Small refactoring for the Colombia / added docstrings & comments to explain why we're not using stock options. Added tests for year 2020 and handling shift exceptions (#509).
+  - Tech: Replace occurrences of `assertEquals` with `assertEqual` to clear warnings (#533).
+  - Use `include_immaculate_conception` flag for Portugal, Brazil, Argentina, Paraguay calendars (#529).
+
+Incorporate changes from workalendar v10.3.0 (2020-07-10)
+
+- Bugfixes
+
+  - Belarus: removing day after Radonitsa, which is apparently not a holiday.
+  - Algeria: assigning the week-end days as FRI+SAT, as it's following a Islamic calendar.
+
+- Other changes
+
+  - Refactoring the core ``Calendar`` classes / mixins for better understanding. Only one ``Calendar`` subclass should be imported / used in calendar classes, the rest (when possible) should be ``Mixins`` (related to #511).
+  - Declaring the New year's Day as a worldwide holiday, with only two exceptions (to date): Israel & Qatar (#511).
+  - Fixed `contributing.md` documentation with the new class/mixin organization (#511).
+
+Incorporate changes from workalendar v10.2.0 (2020-06-26)
+
+- Bugfix: setting *Consciência Negra day* as a non-holiday by default for Brazilian calendars, thx to @edniemeyer (#516).
+- Bugfix: Introducing the changes in Croatia holidays as of 2020 - Remembrance Day, Independence Day, Statehood Day... thx to @davidpodrebarac for the bug report (#515).
+
+Incorporate changes from workalendar v10.1.0 (2020-06-18)
+
+- Calendar fix
+
+  - Adding All Souls' Day to Lithuania calendar, starting of 2020, thx to @norkunas (#512).
+
+- Minor changes
+
+  - Small fixes (docstrings, use of extends, etc) on Cayman Islands calendar (#507).
+  - Moving Carnaval / Mardi Gras / Fat Tuesday calculation into the `workalendar.core` module, because it's used in at least 3 countries and some States / Counties in the USA.
+
+Incorporate changes from workalendar v10.0.0 (2020-06-05)
+
+- **BREAKING CHANGE**: the ``IsoRegistry.get_calendar_class()`` method has been removed from the code and should no longer be used (#375, #495).
+
+Incorporate changes from workalendar v9.2.0 (2020-06-02)
+
+- New Calendars
+
+- Added rules for all Switzerland Cantons, branching off the initial work by @brutasse (#497).
+
+Incorporate changes from workalendar v9.0.1 (2020-05-22)
+
+- Making the Israel calendar more efficient (#498).
+- Fixing duplicated holidays in Hong-Kong and Hong-Kong Bank holiday calendars (#496).
+- Integrating Hong-Kong holidays for 2021 (#496).
+
+Incorporate changes from workalendar v9.0.0 (2020-04-24)
+
+- **BREAKING CHANGE**: the ``IsoRegistry.items()`` method has been removed from the API. You must use the ``get_calendars()`` to perform the same registry queries (#375, #491).
+- *Deprecation notice*: The usage of ``IsoRegistry.get_calendar_class()`` is strongly discouraged, in favor of ``get()``. The ``get_calendar_class`` method will be dropped in a further release. In the meantime, they'll be both equivalent (#375, #418).
+
+Incorporate changes from workalendar v8.4.0 (2020-04-17)
+
+- New Calendar
+
+  - Added Kenyan calendar, by @KidkArolis (#484)
+
+- Minor fixes
+
+  - Fixed Lithuania calendar to use the core flags for Assumption and All Saints (#468).
+  - Fixed Malta calendar ; January 1st was already included, no need to add it to the ``FIXED_HOLIDAYS`` property (#469).
+  - Small refactor in Netherlands calendar to use core constants (#470).
+
+Incorporate changes from workalendar v8.3.0 (2020-04-14)
+
+- Fixing Hong-Kong calendar, where SAT are common working days (#477).
+- Introducing Hong-Kong Bank calendar. For banks, Saturdays are non-working days (#477).
+
+Incorporate changes from workalendar v8.2.2 (2020-04-10)
+
+- Fixed Argentina's "Malvinas Day" date for 2020, shifted to March 31st because of the coronavirus crisis (#476).
+- Fixed Argentina's label for "Malvinas Day" and "Día de la Memoria" (#476).
+
+Incorporate changes from workalendar v8.2.1 (2020-04-03)
+
+- Added BrazilBankCalendar to support `include_` flags and make it possible to extend and change these flags to support custom bank calendars (#474).
+
+Incorporate changes from workalendar v8.2.0 (2020-03-13)
+
+- Added Belarus calendar, by @alexdoesstuff (#472).
+
+Incorporate changes from workalendar v8.1.0 (2020-02-07)
+
+- Added Israel holidays eves and removed holidays which are not affecting the working days in Israel (#461).
+- Fix warning in China's holidays to dynamically read supported years, thx @fredrike (#459).
+
+Incorporate changes from workalendar v8.0.2 (2020-01-24)
+
+- Fix several miscalculations in Georgia (USA) calendar (#451).
+
+Incorporate changes from workalendar v8.0.1 (2020-01-24)
+
+- Fix Family Day for British Columbia (Canada) which was switched from 2nd to 3rd Monday of February in 2019 - thx @jbroudou for the bug report (#454).
+
 v6.1.2
 ------
 
@@ -60,7 +256,6 @@ Other changes
 - Increase Malaysia coverage by adding tests for missing Deepavali & Thaipusam.
 - Increase China coverage by adding tests for special extra-holidays & extra-working days cases.
 
-
 v6.0.0
 ------
 
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
deleted file mode 100644
index 9efa7780..00000000
--- a/CONTRIBUTING.rst
+++ /dev/null
@@ -1,284 +0,0 @@
-======================
-Contribute to Calendra
-======================
-
-Use it (and test it)
-====================
-
-If you are using ``calendra``, you are already contributing to it. As long
-as you are able to check its result, compare the designated working days and
-holidays to the reality, and make sure these are right, you're helping.
-
-If any of the computed holidays for the country / area your are using is
-**wrong**, please report
-`it using the Github issues <https://github.com/jaraco/calendra/issues>`_.
-
-Report an issue
-===============
-
-If you think you've found a bug you can report an issue. In order to help us
-sort this out, please follow the guidelines:
-
-* Tell us which ``calendra`` version (master, PyPI release) you are using.
-* Tell us which Python version you are using, and your platform.
-* Give us extensive details on the country / area Calendar, the exact date(s) that was (were) computed and the one(s) that should have been the correct result.
-* If possible, please provide us a reliable source about the designated country / area calendar, where we could effectively check that we were wrong about a date, and giving us a way to patch our code properly so we can fix the bug.
-
-
-Adding new calendars
-====================
-
-Since ``calendra`` is mainly built around configuration variables and generic
-methods, it's not that difficult to add a calendar to our codebase. A few
-**mandatory** steps should be observed:
-
-1. Fork the repository and create a new branch named after the calendar you want to implement,
-2. Add a test class to the test suite that checks holidays,
-3. Implement the class using the core class APIs as much as possible. Test it until all tests pass.
-4. Make a nice pull-request we'll be glad to review and merge when it's perfect.
-
-.. note::
-
-    Please respect the PEP8 convention, otherwise your PR won't be accepted.
-
-Example
--------
-
-Let's assume you want to include the holidays of the magic (fictional) kingdom
-of *"Zhraa"*, which has a few holidays of different kind.
-
-For the sake of the example, it has the following specs:
-
-* it's a Gregorian-based Calendar (i.e. the Western European / American one),
-* even if the King is not versed into religions, the kingdom includes a few Christian holidays,
-* even if you never knew about it, it is set in Europe,
-
-Here is a list of the holidays in *Zhraa*:
-
-* January 1st, New year's Day,
-* May 1st, Labour day,
-* Easter Monday, which is variable (from March to May),
-* The first monday in June, to celebrate the birth of the Founder of the Kingdom, Zhraa (nobody knows the exact day he was born, so this day was chosen as a convention),
-* The birthday of the King, August 2nd.
-* Christmas Day, Dec 25th.
-
-
-Getting ready
-#############
-
-You'll need to install ``calendra`` dependencies beforehand. What's great is
-that you'll use virtualenv to set it up. Or even better: ``virtualenvwrapper``.
-Just go in your working copy (cloned from github) of calendra and type, for
-example::
-
-    mkvirtualenv CALENDRA
-    pip install -e ./
-
-
-Test-driven start
-#################
-
-
-Let's prepare the Zhraa class. Edit the ``calendra/europe/zhraa.py`` file and
-add a class like this::
-
-    from calendra.core import WesternCalendar
-
-    class Zhraa(WesternCalendar):
-        "Kingdom of Zhraa"
-
-.. note::
-
-    The docstring is not mandatory, but if you omit it, the ``name`` property of
-    your class will be the name of your class. For example, using upper
-    CamelCase, ``KingdomOfZhraa``. For a more human-readable label, use your
-    docstring.
-
-Meanwhile, in the ``calendra/europe/__init__.py`` file, add these snippets
-where needed:
-
-.. code-block:: python
-
-    from .zhraa import Zhraa
-    # ...
-    __all__ = (
-        Belgium,
-        CzechRepublic,
-        # ...
-        Zhraa,
-    )
-
-Now, we're building a test class. Edit the ``calendra/tests/test_europe.py``
-file and add the following code::
-
-    from ..europe import Zhraa
-    # snip...
-
-    class ZhraaTest(GenericCalendarTest):
-        cal_class = Zhraa
-
-        def test_year_2014(self):
-            holidays = self.cal.holidays_set(2014)
-            self.assertIn(date(2014, 1, 1), holidays)  # new year
-            self.assertIn(date(2014, 5, 1), holidays)  # labour day
-            self.assertIn(date(2014, 8, 2), holidays)  # king birthday
-            self.assertIn(date(2014, 12, 25), holidays)  # Xmas
-            # variable days
-            self.assertIn(date(2014, 4, 21), holidays)  # easter monday
-            self.assertIn(date(2014, 6, 2), holidays)  # First MON in June
-
-of course, if you run the test using the ``tox`` or ``py.test`` command,
-this will fail, since we haven't implemented anything yet.
-
-Install tox using the following command::
-
-    workon CALENDRA
-    pip install tox
-
-With the ``WesternCalendar`` base class you have at least one holiday as a
-bonus: the New year's day, which is commonly a holiday.
-
-Add fixed days
-##############
-
-::
-
-    class Zhraa(WesternCalendar):
-        FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-            (5, 1, "Labour Day"),
-            (8, 2, "King Birthday"),
-        )
-
-Now we've got 3 holidays out of 6.
-
-Add religious holidays
-######################
-
-Using ChristianMixin as a base to our Zhraa class will instantly add Christmas
-Day as a holiday. Now we can add Easter monday just by triggering the correct
-flag.
-
-::
-
-    from calendra.core import WesternCalendar, ChristianMixin
-
-    class Zhraa(WesternCalendar, ChristianMixin):
-        include_easter_monday = True
-        FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-            (5, 1, "Labour Day"),
-            (8, 2, "King Birthday"),
-        )
-
-Almost there, 5 holidays out of 6.
-
-Add variable "non-usual" holidays
-#################################
-
-There are many static methods that will grant you a clean access to variable
-days computation. It's very easy to add days like the "Birthday of the Founder"::
-
-
-    class Zhraa(WesternCalendar, ChristianMixin):
-        include_easter_monday = True
-        FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-            (5, 1, "Labour Day"),
-            (8, 2, "King Birthday"),
-        )
-
-        def get_variable_days(self, year):
-            # usual variable days
-            days = super(Zhraa, self).get_variable_days(year)
-
-            days.append(
-                (Zhraa.get_nth_weekday_in_month(year, 6, MON),
-                'Day of the Founder'),
-            )
-            return days
-
-.. note::
-
-    Please mind that the returned "variable_days" is a list of tuples. The first
-    item being a date object (in the Python ``datetime.date`` sense) and the
-    second one is the label string.
-
-Add you calendar to the global registry
-#######################################
-
-If you're adding a Country calendar that has an ISO code, you may want to add
-it to our global registry.
-
-Workalendar is providing a registry that you can use to query and fetch calendar
-based on their ISO code. For the current example, let's pretend that the Zhraa
-Kingdom ISO code is ``ZK``.
-
-To register, add the following::
-
-    from workalendar.registry import iso_register
-
-    @iso_register('ZK')
-    class Zhraa(WesternCalendar, ChristianMixin):
-        # The rest of your code...
-
-You're done for the code!
-#########################
-
-There you are. Commit with a nice commit message, test, make sure it works for
-the other years as well and you're almost there.
-
-The final steps
-###############
-
-Do not forget to:
-
-1. put the appropriate doctring in the Calendar class.
-2. add your calendar in the ``README.rst`` file, included in the appropriate continent.
-3. add your calendar to the ``CHANGELOG`` file.
-
-.. note::
-
-    We're planning to build a complete documentation for the other cases
-    (special holiday rules, other calendar types, other religions, etc). But
-    with this tutorial you're sorted for a lot of other calendars.
-
-
-Other code contributions
-========================
-
-There are dozens of calendars all over the world. We'd appreciate you to
-contribute to the core of the library by adding some new Mixins or Calendars.
-
-Bear in mind that the code you'd provide **must** be tested using unittests
-before you submit your pull-request.
-
-Syncing from upstream Workalendar
-=================================
-
-1. Create a fork, and clone it::
-
-    git clone git@github.com:ShaheedHaque/calendra.git
-
-2. Add a remote pointing to upstream Workalendar::
-
-    cd calendra
-    git remote add workalendar git@github.com:peopledoc/workalendar.git
-
-3. Pull the remote, including its tags::
-
-    git remote update
-
-4. Make a branch to work on::
-
-    git checkout -b srh_sync_workalendar_5_2_2
-
-5. Review the changes to be brought in::
-
-    git diff 5.2.2..HEAD
-
-6. Merge, based on the diff command above, and a careful review/tweaking
-   of the results.
-
-7. Proceed with care, and test as needed::
-
-    tox
-
-8. Generate a PR when ready!
diff --git a/MANIFEST.in b/MANIFEST.in
index 9561fb10..bb3ec5f0 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,1 +1,1 @@
-include README.rst
+include README.md
diff --git a/README.md b/README.md
index c493f27b..d159fc84 100644
--- a/README.md
+++ b/README.md
@@ -8,13 +8,11 @@
 
 [![](https://readthedocs.org/projects/calendra/badge/?version=latest)](https://calendra.readthedocs.io/en/latest/?badge=latest)
 
-# Overview
+## Overview
 
-Calendra is a Python module that offers classes able to handle calendars,
-list legal / religious holidays and gives working-day-related computation
-functions.
+Calendra is a Python module that offers classes able to handle calendars, list legal / religious holidays and gives working-day-related computation functions.
 
-# History
+## History
 
 Calendra is a fork of [Workalendar](https://github.com/peopledoc/workalendar)
 designed to be more extensible and introspectable, adding interfaces where
@@ -30,14 +28,13 @@ What can Calendra do that Workalendar cannot?
 - Consolidates observance logic in the core code rather than requiring
   each calendar implementation to implement its own.
 
-# Status
+## Status
 
-The project is stable and in production use. Calendra follows the principles
-of [semver](https://semver.org) for released verisons.
+The project is stable and in production use. Calendra follows the principles of [semver](https://semver.org) for released verisons.
 
-If you spot any bug or wish to add a calendar, please refer to the [Contributing doc](CONTRIBUTING.rst).
+If you spot any bug or wish to add a calendar, please refer to the [Contributing doc](https://peopledoc.github.io/workalendar/contributing.html).
 
-# Usage sample
+## Usage sample
 
 ```python-repl
 >>> from datetime import date
@@ -65,135 +62,127 @@ True
 datetime.date(2012, 12, 31)
 ```
 
-For a more complete documentation and advanced usage, go to
-[the official workalendar documentation](https://peopledoc.github.io/workalendar).
+For a more complete documentation and advanced usage, go to [the official workalendar documentation](https://peopledoc.github.io/workalendar).
 
-# External dependencies
+## External dependencies
 
 Calendra has been tested on the Python versions declared in setup.cfg.
 
 If you're using wheels, you should be fine without having to install extra system packages. As of `v7.0.0`, we have dropped `ephem` as a dependency for computing astronomical ephemeris in favor of `skyfield`. So if you had any trouble because of this new dependency, during the installation or at runtime, [do not hesitate to file an issue](https://github.com/peopledoc/workalendar/issues/).
 
-# Tests
+## Tests
 
 To run test, just install tox with `pip install tox` and run `tox`
 from the command line.
 
 
-# Available Calendars
-
-## Europe
-
-* Austria
-* Belgium
-* Bulgaria
-* Cayman Islands
-* Croatia
-* Cyprus
-* Czech Republic
-* Denmark
-* Estonia
-* European Central Bank
-* Finland
-* France
-* France (Alsace / Moselle)
-* Germany
-* Greece
-* Hungary
-* Iceland
-* Ireland
-* Italy
-* Latvia
-* Lithuania
-* Luxembourg
-* Malta
-* Netherlands
-* Norway
-* Poland
-* Portugal
-* Romania
-* Russia
-* Serbia
-* Slovakia
-* Slovenia
-* Spain (incl. Catalonia)
-* Sweden
-* Switzerland
-
-  * Vaud
-  * Geneva
-
-* Turkey
-* Ukraine
-* United Kingdom (incl. Northern Ireland, Scotland and all its territories)
-
-## America
-
-* Argentina
-* Barbados
-* Brazil (all states, cities and for bank transactions, except the city of Viana)
-* Canada (including provincial and territory holidays)
-* Chile
-* Colombia
-* Mexico
-* Panama
-* Paraguay
-* United States of America
-
-  * State holidays for all the 50 States
-  * American Samoa
-  * Chicago, Illinois
-  * Guam
-  * Suffolk County, Massachusetts
-  * California Education, Berkeley, San Francisco, West Hollywood
-  * Florida Legal and Florida Circuit Courts, Miami-Dade
-
-## Asia
-
-* China
-* Hong Kong
-* Israel
-* Japan
-* JapanBank
-* Malaysia
-* Qatar
-* Singapore
-* South Korea
-* Taiwan
-
-## Oceania
-
-* Australia (incl. its different states)
-* Marshall Islands
-* New Zealand
-
-## Africa
-
-* Algeria
-* Angola
-* Benin
-* Ivory Coast
-* Madagascar
-* São Tomé
-* South Africa
+## Available Calendars
+
+### Europe
+
+- Austria
+- Belarus
+- Belgium
+- Bulgaria
+- Cayman Islands
+- Croatia
+- Cyprus
+- Czech Republic
+- Denmark
+- Estonia
+- European Central Bank
+- Finland
+- France
+- France (Alsace / Moselle)
+- Germany
+- Greece
+- Hungary
+- Iceland
+- Ireland
+- Italy
+- Latvia
+- Lithuania
+- Luxembourg
+- Malta
+- Monaco
+- Netherlands (optionally with school holidays and carnival)
+- Norway
+- Poland
+- Portugal
+- Romania
+- Russia
+- Serbia
+- Slovakia
+- Slovenia
+- Spain (Andalusia, Aragon, Castile and León, Castilla-La Mancha, Canary Islands, Extremadura, Galicia, Balearic Islands, La Rioja, Community of Madrid, Murcia, Navarre, Asturias, Basque Country, Cantabria, Valencian Community)
+- Sweden
+- Switzerland (Aargau, Appenzell Innerrhoden, Appenzell Ausserrhoden, Bern, Basel-Landschaft, Basel-Stadt, Fribourg, Geneva, Glarus, Graubünden, Jura, Luzern, Neuchâtel, Nidwalden, Obwalden, St. Gallen, Schaffhausen, Solothurn, Schwyz, Thurgau, Ticino, Uri, Vaud, Valais, Zug, Zurich)
+- Turkey
+- Ukraine
+- United Kingdom (incl. Northern Ireland, Scotland and all its territories)
+
+### America
+
+- Argentina
+- Barbados
+- Brazil (all states, cities and for bank transactions, except the city of Viana)
+- Canada (including provincial and territory holidays)
+- Chile
+- Colombia
+- Mexico
+- Panama
+- Paraguay
+- United States of America
+  - State holidays for all the 50 States
+  - American Samoa
+  - Chicago, Illinois
+  - Guam
+  - Suffolk County, Massachusetts
+  - California Education, Berkeley, San Francisco, West Hollywood
+  - Florida Legal and Florida Circuit Courts, Miami-Dade
+
+### Asia
+
+- China
+- Hong Kong
+- Israel
+- Japan
+- JapanBank
+- Malaysia
+- Qatar
+- Singapore
+- South Korea
+- Taiwan
+
+### Oceania
+
+- Australia (incl. its different states)
+- Marshall Islands
+- New Zealand
+
+### Africa
+
+- Algeria
+- Angola
+- Benin
+- Ivory Coast
+- Kenya
+- Madagascar
+- Mozambique
+- São Tomé
+- South Africa
 
 And more to come (I hope!)
 
-# Caveats
+## Caveats
 
-Please take note that some calendars are not 100% accurate. The most common
-example is the Islamic calendar, where some computed holidays are not exactly on
-the same official day decided by religious authorities, and this may vary
-country by country. Whenever it's possible, try to adjust your results with
-the official data provided by the adequate authorities.
+Please take note that some calendars are not 100% accurate. The most common example is the Islamic calendar, where some computed holidays are not exactly on the same official day decided by religious authorities, and this may vary country by country. Whenever it's possible, try to adjust your results with the official data provided by the adequate authorities.
 
-# Contributing
 
-Please read our [CONTRIBUTING.rst](https://github.com/jaraco/calandra/blob/master/CONTRIBUTING.rst)
-document to discover how you can contribute to `calendra`. Pull-requests
-are very welcome.
+## Contributing
 
-# License
+Please read our [contributing.md](https://github.com/peopledoc/workalendar/blob/master/docs/contributing.md) document to discover how you can contribute to `workalendar`. Pull-requests are very welcome.
 
-This library is published under the terms of the MIT License. Please check the
-LICENSE file for more details.
+## License
+
+This library is published under the terms of the MIT License. Please check the LICENSE file for more details.
diff --git a/calendra/africa/__init__.py b/calendra/africa/__init__.py
index b4e347c1..9e805f67 100644
--- a/calendra/africa/__init__.py
+++ b/calendra/africa/__init__.py
@@ -1,18 +1,21 @@
 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
 from .angola import Angola
-
+from .mozambique import Mozambique
 
 __all__ = (
     'Algeria',
     'Benin',
     'IvoryCoast',
+    'Kenya',
     'Madagascar',
     'SaoTomeAndPrincipe',
     'SouthAfrica',
     'Angola',
+    'Mozambique',
 )
diff --git a/calendra/africa/algeria.py b/calendra/africa/algeria.py
index 444498eb..fe8302d1 100644
--- a/calendra/africa/algeria.py
+++ b/calendra/africa/algeria.py
@@ -1,22 +1,23 @@
-from ..core import WesternCalendar, IslamicMixin
-from ..registry import iso_register
+from ..core import IslamicCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('DZ')
-class Algeria(WesternCalendar, IslamicMixin):
+class Algeria(IslamicCalendar):
     "Algeria"
+    # Civil holidays
+    include_labour_day = True
     # Islamic holidays
     include_prophet_birthday = True
     include_eid_al_fitr = True
     include_day_of_sacrifice = True
     include_islamic_new_year = True
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
+    FIXED_HOLIDAYS = IslamicCalendar.FIXED_HOLIDAYS + (
         (7, 5, "Independence Day"),
         (11, 1, "Anniversary of the revolution"),
     )
 
-    ISLAMIC_HOLIDAYS = IslamicMixin.ISLAMIC_HOLIDAYS + (
+    ISLAMIC_HOLIDAYS = IslamicCalendar.ISLAMIC_HOLIDAYS + (
         (1, 10, "Ashura"),
     )
diff --git a/calendra/africa/angola.py b/calendra/africa/angola.py
index ca7780c0..1bf13dab 100644
--- a/calendra/africa/angola.py
+++ b/calendra/africa/angola.py
@@ -1,12 +1,16 @@
-from datetime import timedelta
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('AO')
-class Angola(WesternCalendar, ChristianMixin):
-    """Angola"""
-
+class Angola(WesternCalendar):
+    "Angola"
+    # Civil holiday
+    include_labour_day = True
+    labour_day_label = "Dia Internacional do Trabalhador"
+    # Christian holidays
+    include_fat_tuesday = True
+    fat_tuesday_label = "Dia de Carnaval"
     include_good_friday = True
     include_easter_sunday = True
     include_christmas = True
@@ -16,16 +20,6 @@ class Angola(WesternCalendar, ChristianMixin):
         (2, 4, "Dia do Inicio da Luta Armada"),
         (3, 8, "Dia Internacional da Mulher"),
         (4, 4, "Dia da Paz"),
-        (5, 1, "Dia Internacional do Trabalhador"),
         (9, 17, "Dia do Fundador da Nação e do Herói Nacional"),
         (11, 11, "Dia da Independência Nacional"),
     )
-
-    def get_variable_entrudo(self, year):
-        easter_sunday = self.get_easter_sunday(year)
-        return easter_sunday - timedelta(days=47)
-
-    def get_variable_days(self, year):
-        days = super().get_variable_days(year)
-        days.append((self.get_variable_entrudo(year), "Dia de Carnaval"))
-        return days
diff --git a/calendra/africa/benin.py b/calendra/africa/benin.py
index f4a03e2b..81541709 100644
--- a/calendra/africa/benin.py
+++ b/calendra/africa/benin.py
@@ -1,10 +1,13 @@
-from ..core import WesternCalendar, IslamicMixin, ChristianMixin
-from ..registry import iso_register
+from ..core import IslamoWesternCalendar, SAT, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('BJ')
-class Benin(WesternCalendar, IslamicMixin, ChristianMixin):
+class Benin(IslamoWesternCalendar):
     "Benin"
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
@@ -16,10 +19,12 @@ class Benin(WesternCalendar, IslamicMixin, ChristianMixin):
     include_day_of_sacrifice = True
     include_day_of_sacrifice_label = "Tabaski"
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    FIXED_HOLIDAYS = IslamoWesternCalendar.FIXED_HOLIDAYS + (
         (1, 10, "Traditional Day"),
-        (5, 1, "Labour Day"),
         (8, 1, "Independence Day"),
         (10, 26, "Armed Forces Day"),
         (11, 30, "National Day"),
     )
+
+    # Explicitly assign these WE days, Benin has adopted the western workweek
+    WEEKEND_DAYS = (SAT, SUN)
diff --git a/calendra/africa/ivory_coast.py b/calendra/africa/ivory_coast.py
index 0aa80b2d..b0f6b913 100644
--- a/calendra/africa/ivory_coast.py
+++ b/calendra/africa/ivory_coast.py
@@ -1,22 +1,28 @@
-from ..core import WesternCalendar, IslamicMixin, ChristianMixin
-from ..registry import iso_register
+from ..core import IslamoWesternCalendar, SAT, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('CI')
-class IvoryCoast(WesternCalendar, ChristianMixin, IslamicMixin):
+class IvoryCoast(IslamoWesternCalendar):
     "Ivory Coast"
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
     include_assumption = True
     include_all_saints = True
+    # Islamic holidays
     include_day_after_prophet_birthday = True
     include_eid_al_fitr = True
     include_day_of_sacrifice = True
     include_day_of_sacrifice_label = "Feast of the Sacrifice"
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
+    FIXED_HOLIDAYS = IslamoWesternCalendar.FIXED_HOLIDAYS + (
         (8, 7, "Independence Day"),
         (11, 15, "National Peace Day"),
     )
+
+    # Ivory Coast has adopted the "western" workweek.
+    WEEKEND_DAYS = (SAT, SUN)
diff --git a/calendra/africa/kenya.py b/calendra/africa/kenya.py
new file mode 100644
index 00000000..ce56f65f
--- /dev/null
+++ b/calendra/africa/kenya.py
@@ -0,0 +1,76 @@
+from copy import copy
+from datetime import timedelta, date
+
+from ..core import IslamoWesternCalendar, SAT, SUN
+from ..registry_tools import iso_register
+
+
+@iso_register('KE')
+class Kenya(IslamoWesternCalendar):
+    "Kenya"
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
+    include_good_friday = True
+    include_easter_monday = True
+    # Islamic holidays
+    include_eid_al_fitr = True
+    include_day_of_sacrifice = True
+    shift_sunday_holidays = True
+
+    # Explicitly assign these WE days, Kenya has adopted the western workweek
+    WEEKEND_DAYS = (SAT, SUN)
+
+    FIXED_HOLIDAYS = IslamoWesternCalendar.FIXED_HOLIDAYS + (
+        (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
diff --git a/calendra/africa/madagascar.py b/calendra/africa/madagascar.py
index 2196eaa7..af1adc35 100644
--- a/calendra/africa/madagascar.py
+++ b/calendra/africa/madagascar.py
@@ -1,15 +1,17 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('MG')
-class Madagascar(WesternCalendar, ChristianMixin):
+class Madagascar(WesternCalendar):
     "Madagascar"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (3, 29, "Martyrs' Day"),
-        (5, 1, "Labour Day"),
         (6, 26, "Independence Day"),
     )
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
diff --git a/calendra/africa/mozambique.py b/calendra/africa/mozambique.py
new file mode 100644
index 00000000..10bd9754
--- /dev/null
+++ b/calendra/africa/mozambique.py
@@ -0,0 +1,22 @@
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
+
+
+@iso_register('MZ')
+class Mozambique(WesternCalendar):
+    "Mozambique"
+    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+        (2, 3, "Mozambican Heroes' Day"),
+        (4, 7, "Mozambican Women's Day"),
+        (6, 25, "Independence Day"),
+        (9, 7, "Victory Day"),
+        (9, 25, "Armed Forces Day"),
+        (10, 4, "Peace And Reconciliation Day"),
+    )
+
+    # Civil holidays
+    include_labour_day = True
+
+    # Christian holidays
+    include_good_friday = True
+    include_christmas = True
diff --git a/calendra/africa/sao_tome.py b/calendra/africa/sao_tome.py
index 3fe7a081..3ab0c770 100644
--- a/calendra/africa/sao_tome.py
+++ b/calendra/africa/sao_tome.py
@@ -1,16 +1,18 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('ST')
-class SaoTomeAndPrincipe(WesternCalendar, ChristianMixin):
+class SaoTomeAndPrincipe(WesternCalendar):
     "São Tomé and Príncipe"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (2, 3, "Martyr's Day"),
-        (5, 1, "Labour Day"),
         (7, 12, "Independence Day"),
         (9, 6, "Armed Forces Day"),
         (9, 30, "Agricultural Reform Day"),
         (12, 21, "São Tomé Day"),
     )
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_all_saints = True
diff --git a/calendra/africa/south_africa.py b/calendra/africa/south_africa.py
index 7ec1e7fa..30606236 100644
--- a/calendra/africa/south_africa.py
+++ b/calendra/africa/south_africa.py
@@ -1,14 +1,12 @@
 from datetime import date
 
-from ..core import WesternCalendar
-from ..core import MON, FRI
-from ..core import ChristianMixin
+from ..core import WesternCalendar, MON, FRI
 from ..exceptions import CalendarError
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('ZA')
-class SouthAfrica(WesternCalendar, ChristianMixin):
+class SouthAfrica(WesternCalendar):
     "South Africa"
     include_good_friday = True
     include_christmas = True
diff --git a/calendra/america/argentina.py b/calendra/america/argentina.py
index 57cc802e..2a08e5bf 100644
--- a/calendra/america/argentina.py
+++ b/calendra/america/argentina.py
@@ -1,55 +1,31 @@
 from datetime import timedelta, date
-from ..core import (
-    WesternCalendar, ChristianMixin,
-    MON, TUE, WED, THU, FRI, SAT
-)
-from ..registry import iso_register
+from ..core import WesternCalendar, MON, TUE, WED, THU, FRI, SAT
+from ..registry_tools import iso_register
 
 
 @iso_register('AR')
-class Argentina(WesternCalendar, ChristianMixin):
+class Argentina(WesternCalendar):
     'Argentina'
-
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Día del Trabajador"
+    # Christian holidays
+    include_fat_tuesday = True
+    fat_tuesday_label = "Carnival"
     include_good_friday = True
     include_easter_saturday = True
     include_easter_sunday = True
     include_christmas = True
+    include_immaculate_conception = True
+    immaculate_conception_label = "Día de la Inmaculada Concepción de María"
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (3, 24, "Día de la Memoria"),
-        (4, 2, "Día de las Malvinas"),
-        (5, 1, "Día del Trabajador"),
+        (3, 24, "Día Nacional de la Memoria por la Verdad y la Justicia"),
         (5, 25, "Día de la Revolución de Mayo"),
         (6, 20, "Día Paso a la Inmortalidad del General Manuel Belgrano"),
         (7, 9, "Día de la Independencia"),
-        (12, 8, "Día de la Inmaculada Concepción de María"),
     )
 
-    def get_variable_days(self, year):
-
-        days = super().get_variable_days(year)
-        days.append(
-            (self.get_easter_sunday(year) - timedelta(days=48),
-                "Carnival Lunes"))
-
-        days.append(
-            (self.get_easter_sunday(year) - timedelta(days=47),
-                "Carnival"))
-
-        days.append(
-            (self.get_general_guemes_day(year)))
-
-        days.append(
-            (self.get_general_martin_day(year)))
-
-        days.append(
-            (self.get_soberania_day(year)))
-
-        days.append(
-            (self.get_diversidad_day(year)))
-
-        return days
-
     def get_general_guemes_day(self, year):
         """
         Día Paso a la Inmortalidad del General Martín Miguel de Güemes.
@@ -131,3 +107,36 @@ def get_diversidad_day(self, year):
 
         return (diversidad_day,
                 "Día del Respeto a la Diversidad Cultural")
+
+    def get_malvinas_day(self, year):
+        """
+        Día de las Malvinas
+
+        In honour of the Veterans and the Fallen of the Malvinas war.
+        https://en.wikipedia.org/wiki/Malvinas_Day
+
+        In 2020, it was shifted to March 31st because of
+        the coronavirus crisis.
+        """
+        label = "Día del Veterano y de los Caídos en la Guerra de Malvinas"
+        if year == 2020:
+            day = date(year, 3, 31)
+        else:
+            day = date(year, 4, 2)
+        return (day, label)
+
+    def get_variable_days(self, year):
+        days = super().get_variable_days(year)
+        days.append(
+            (self.get_easter_sunday(year) - timedelta(days=48),
+                "Carnival Lunes"))
+
+        days.extend([
+            self.get_malvinas_day(year),
+            self.get_general_guemes_day(year),
+            self.get_general_martin_day(year),
+            self.get_soberania_day(year),
+            self.get_diversidad_day(year)
+        ])
+
+        return days
diff --git a/calendra/america/barbados.py b/calendra/america/barbados.py
index 1b2044fe..a660343f 100644
--- a/calendra/america/barbados.py
+++ b/calendra/america/barbados.py
@@ -1,15 +1,16 @@
 from datetime import timedelta
 from copy import copy
 
-from ..core import WesternCalendar, ChristianMixin
-from ..core import SUN, MON
-from ..registry import iso_register
+from ..core import WesternCalendar, SUN, MON
+from ..registry_tools import iso_register
 
 
 @iso_register("BB")
-class Barbados(WesternCalendar, ChristianMixin):
+class Barbados(WesternCalendar):
     "Barbados"
-
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_good_friday = True
     include_easter_sunday = True
     include_easter_monday = True
@@ -20,7 +21,6 @@ class Barbados(WesternCalendar, ChristianMixin):
     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"),
     )
@@ -50,7 +50,8 @@ def get_fixed_holidays(self, 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)"))
-                )
+                days.append((
+                    day + timedelta(days=1),
+                    "{} {}".format(label, "(shifted)")
+                ))
         return days
diff --git a/calendra/america/brazil.py b/calendra/america/brazil.py
index efe0947d..32ad5b92 100644
--- a/calendra/america/brazil.py
+++ b/calendra/america/brazil.py
@@ -1,16 +1,14 @@
 from datetime import timedelta, date
 
-from ..core import WesternCalendar, ChristianMixin
-from ..core import MON, SAT, SUN
-from ..registry import iso_register
+from ..core import WesternCalendar, MON, SAT, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('BR')
-class Brazil(WesternCalendar, ChristianMixin):
+class Brazil(WesternCalendar):
     "Brazil"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (4, 21, "Tiradentes' Day"),
-        (5, 1, "Labour Day"),
         (9, 7, "Independence Day"),
         (10, 12, "Our Lady of Aparecida"),
         (11, 2, "All Souls' Day"),
@@ -22,24 +20,22 @@ class Brazil(WesternCalendar, ChristianMixin):
     sao_pedro_label = "São Pedro"
     include_sao_joao = False
     sao_joao_label = "São João"
+    # Civil holidays
+    include_labour_day = True
     include_servidor_publico = False
     servidor_publico_label = "Dia do Servidor Público"
     # Consciência Negra day
-    include_consciencia_negra = True
+    include_consciencia_negra = False
     # There are two dates for the Consciência Negra day
     # The most common is November, 20th
     consciencia_negra_day = (11, 20)
     consciencia_negra_label = "Consciência Negra"
-    include_nossa_senhora_conceicao = False
-    include_easter_sunday = True
-
-    def get_carnaval(self, year):
-        """
-        Return the third day of Carnaval (Tuesday)
 
-        This day is shared holidays by several Brazil states.
-        """
-        return self.get_easter_sunday(year) - timedelta(days=47)
+    # Christian holidays
+    include_easter_sunday = True
+    # Dia de Nossa Senhora da Conceição is the Immaculate Conception.
+    include_immaculate_conception = False
+    immaculate_conception_label = "Dia de Nossa Senhora da Conceição"
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
@@ -56,10 +52,6 @@ def get_variable_days(self, year):
             days.append(
                 (date(year, month, day), self.consciencia_negra_label)
             )
-        if self.include_nossa_senhora_conceicao:
-            days.append(
-                (date(year, 12, 8), "Dia de Nossa Senhora da Conceição")
-            )
         return days
 
 
@@ -106,7 +98,7 @@ class BrazilAmazonas(Brazil):
         (9, 5, "Elevação do Amazonas á categoria de província"),
     )
     include_consciencia_negra = True
-    include_nossa_senhora_conceicao = True
+    include_immaculate_conception = True
 
 
 @iso_register('BR-BA')
@@ -154,7 +146,7 @@ class BrazilMaranhao(Brazil):
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (7, 28, "Adesão do Maranhão á independência do Brasil"),
     )
-    include_nossa_senhora_conceicao = True
+    include_immaculate_conception = True
 
 
 @iso_register('BR-MG')
@@ -186,7 +178,7 @@ class BrazilPara(Brazil):
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (8, 15, "Adesão do Grão-Pará á independência do Brasil"),
     )
-    include_nossa_senhora_conceicao = True
+    include_immaculate_conception = True
 
 
 @iso_register('BR-PB')
@@ -231,11 +223,13 @@ class BrazilRioDeJaneiro(Brazil):
         (4, 23, "Dia de São Jorge"),
         (3, 1, "Aniversário da Cidade do Rio de Janeiro"),
     )
+    include_fat_tuesday = True
+    fat_tuesday_label = "Carnaval"
     include_servidor_publico = True
     servidor_publico_label = "Dia do Funcionário Público"
     include_consciencia_negra = True
     consciencia_negra_label = "Dia da Consciência Negra"
-    include_nossa_senhora_conceicao = True
+    include_immaculate_conception = True
 
     def get_dia_do_comercio(self, year):
         """
@@ -247,7 +241,6 @@ def get_dia_do_comercio(self, year):
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
-        days.append((self.get_carnaval(year), "Carnaval"))
         days.append((self.get_dia_do_comercio(year), "Dia do Comércio"))
         return days
 
@@ -309,6 +302,8 @@ class BrazilSaoPauloCity(BrazilSaoPauloState):
     FIXED_HOLIDAYS = BrazilSaoPauloState.FIXED_HOLIDAYS + (
         (1, 25, "Anniversary of the city of São Paulo"),
     )
+    include_fat_tuesday = True
+    fat_tuesday_label = "Carnaval"
     include_easter_sunday = True
     include_corpus_christi = True
     include_good_friday = True
@@ -316,11 +311,6 @@ class BrazilSaoPauloCity(BrazilSaoPauloState):
     include_consciencia_negra = True
     consciencia_negra_label = "Dia da Consciência Negra"
 
-    def get_variable_days(self, year):
-        days = super().get_variable_days(year)
-        days.append((self.get_carnaval(year), "Carnaval"))
-        return days
-
 
 @iso_register('BR-SE')
 class BrazilSergipe(Brazil):
@@ -380,7 +370,7 @@ class BrazilGuarapariCity(BrazilEspiritoSanto):
     include_sao_pedro = True
     include_consciencia_negra = True
     consciencia_negra_day = (11, 29)
-    include_nossa_senhora_conceicao = True
+    include_immaculate_conception = True
 
 
 class BrazilSerraCity(BrazilEspiritoSanto):
@@ -388,18 +378,19 @@ class BrazilSerraCity(BrazilEspiritoSanto):
     FIXED_HOLIDAYS = BrazilEspiritoSanto.FIXED_HOLIDAYS + (
         (12, 26, "Dia do Serrano"),
     )
+    include_fat_tuesday = True
+    fat_tuesday_label = "Carnaval"
     include_ash_wednesday = True
     ash_wednesday_label = "Quarta-feira de cinzas"
     include_good_friday = True
     good_friday_label = "Paixão do Cristo"
     include_sao_pedro = True
-    include_nossa_senhora_conceicao = True
+    include_immaculate_conception = True
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
-        carnaval_tuesday = self.get_carnaval(year)
+        carnaval_tuesday = self.get_fat_tuesday(year)
         days.append((carnaval_tuesday - timedelta(days=1), "Carnaval Monday"))
-        days.append((carnaval_tuesday, "Carnaval"))
         return days
 
 
@@ -583,10 +574,12 @@ class BrazilBankCalendar(Brazil):
     Calendar that considers only working days for bank transactions
     for companies and the general public
     """
-    FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
-        (12, 25, "Christmas Day"),
-    )
-    observance_shift = None
+    include_fat_tuesday = True
+    fat_tuesday_label = "Tuesday carnaval"
+    include_good_friday = True
+    include_ash_wednesday = True
+    include_corpus_christi = True
+    include_easter_sunday = False
 
     def get_last_day_of_year_for_only_internal_bank_trans(self, year):
         """
@@ -608,31 +601,18 @@ def get_variable_days(self, year):
         Define the brazilian variable holidays and the last
         day for only internal bank transactions
         """
-        tuesday_carnaval = self.get_carnaval(year)
+        days = super().get_variable_days(year)
+        tuesday_carnaval = self.get_fat_tuesday(year)
         monday_carnaval = tuesday_carnaval - timedelta(days=1)
-        good_friday = self.get_good_friday(year)
-        corpus_christi = self.get_corpus_christi(year)
-        ash_wednesday = self.get_ash_wednesday(year)
-
-        non_fixed_holidays = [
-            (monday_carnaval, "Monday carnaval"),
-            (tuesday_carnaval, "Tuesday carnaval"),
-            (good_friday, "Good friday"),
-            (corpus_christi, "Corpus Christi"),
-            (ash_wednesday, "Ash Wednesday"),
-        ]
-
-        non_working_days = [
-            (
-                self.get_last_day_of_year_for_only_internal_bank_trans(year),
-                "Last day of year for only internal bank transactions"
-            )
-        ]
 
-        return (
-            super().get_variable_days(year)
-            + non_fixed_holidays + non_working_days
-        )
+        days.append((monday_carnaval, "Monday carnaval"))
+
+        days.append((
+            self.get_last_day_of_year_for_only_internal_bank_trans(year),
+            "Last day of year for only internal bank transactions"
+        ))
+
+        return days
 
     def find_following_working_day(self, day):
         """
diff --git a/calendra/america/canada.py b/calendra/america/canada.py
index ba9e6a01..51a9268e 100644
--- a/calendra/america/canada.py
+++ b/calendra/america/canada.py
@@ -1,12 +1,11 @@
 from datetime import date
 
-from ..core import WesternCalendar, ChristianMixin, Calendar
-from ..core import SUN, MON, SAT
-from ..registry import iso_register
+from ..core import WesternCalendar, SUN, MON, SAT
+from ..registry_tools import iso_register
 
 
 @iso_register('CA')
-class Canada(WesternCalendar, ChristianMixin):
+class Canada(WesternCalendar):
     "Canada"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (7, 1, "Canada Day"),
@@ -31,21 +30,14 @@ def get_variable_days(self, year):
         return days
 
 
-class EarlyFamilyDayMixin(Calendar):
-    "2nd Monday of February"
-
-    def get_family_day(self, year, label="Family Day"):
-        return (self.get_nth_weekday_in_month(year, 2, MON, 2), label)
-
-
-class LateFamilyDayMixin(Calendar):
+class LateFamilyDayMixin:
     "3rd Monday of February"
 
     def get_family_day(self, year, label="Family Day"):
         return (self.get_nth_weekday_in_month(year, 2, MON, 3), label)
 
 
-class VictoriaDayMixin(Calendar):
+class VictoriaDayMixin:
     "Monday preceding the 25th of May"
 
     def get_victoria_day(self, year):
@@ -54,14 +46,14 @@ def get_victoria_day(self, year):
                 return (date(year, 5, day), "Victoria Day")
 
 
-class AugustCivicHolidayMixin(Calendar):
+class AugustCivicHolidayMixin:
     "1st Monday of August; different names depending on location"
 
     def get_civic_holiday(self, year, label="Civic Holiday"):
         return (self.get_nth_weekday_in_month(year, 8, MON), label)
 
 
-class ThanksgivingMixin(Calendar):
+class ThanksgivingMixin:
     "2nd Monday of October"
 
     def get_thanksgiving(self, year):
@@ -69,7 +61,7 @@ def get_thanksgiving(self, year):
         return (thanksgiving, "Thanksgiving")
 
 
-class BoxingDayMixin(Calendar):
+class BoxingDayMixin:
     "26th of December; shift to next working day"
 
     def get_boxing_day(self, year):
@@ -85,7 +77,7 @@ def get_boxing_day(self, year):
         return days
 
 
-class StJeanBaptisteMixin(Calendar):
+class StJeanBaptisteMixin:
     "24th of June; shift to next working day"
 
     def get_st_jean(self, year):
@@ -99,7 +91,7 @@ def get_st_jean(self, year):
         return days
 
 
-class RemembranceDayShiftMixin(Calendar):
+class RemembranceDayShiftMixin:
     "11th of November; shift to next day"
     def get_remembrance_day(self, year):
         remembranceday = date(year, 11, 11)
@@ -113,8 +105,9 @@ def get_remembrance_day(self, year):
 
 
 @iso_register('CA-ON')
-class Ontario(Canada, BoxingDayMixin, ThanksgivingMixin, VictoriaDayMixin,
-              LateFamilyDayMixin, AugustCivicHolidayMixin):
+class Ontario(BoxingDayMixin, ThanksgivingMixin, VictoriaDayMixin,
+              LateFamilyDayMixin, AugustCivicHolidayMixin,
+              Canada):
     "Ontario"
     include_good_friday = True
 
@@ -132,7 +125,7 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-QC')
-class Quebec(Canada, VictoriaDayMixin, StJeanBaptisteMixin, ThanksgivingMixin):
+class Quebec(VictoriaDayMixin, StJeanBaptisteMixin, ThanksgivingMixin, Canada):
     "Quebec"
     include_easter_monday = True
 
@@ -147,8 +140,8 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-BC')
-class BritishColumbia(Canada, VictoriaDayMixin, AugustCivicHolidayMixin,
-                      ThanksgivingMixin, EarlyFamilyDayMixin):
+class BritishColumbia(VictoriaDayMixin, AugustCivicHolidayMixin,
+                      ThanksgivingMixin, Canada):
     "British Columbia"
 
     include_good_friday = True
@@ -157,10 +150,24 @@ class BritishColumbia(Canada, VictoriaDayMixin, AugustCivicHolidayMixin,
         (11, 11, "Remembrance Day"),
     )
 
+    def get_family_day(self, year):
+        """
+        Return Family Day for British Columbia.
+
+        From 2013 to 2018, Family Day was on 2nd MON of February
+        As of 2019, Family Day happens on 3rd MON of February
+        """
+        label = "Family Day"
+        if year >= 2019:
+            return (self.get_nth_weekday_in_month(year, 2, MON, 3), label)
+        return (self.get_nth_weekday_in_month(year, 2, MON, 2), label)
+
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
+        if year >= 2013:
+            days.append(self.get_family_day(year))
+
         days.extend([
-            (self.get_family_day(year)),
             (self.get_victoria_day(year)),
             (self.get_civic_holiday(year, "British Columbia Day")),
             (self.get_thanksgiving(year)),
@@ -169,7 +176,7 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-AB')
-class Alberta(Canada, LateFamilyDayMixin, VictoriaDayMixin, ThanksgivingMixin):
+class Alberta(LateFamilyDayMixin, VictoriaDayMixin, ThanksgivingMixin, Canada):
     "Alberta"
     include_good_friday = True
 
@@ -188,9 +195,9 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-SK')
-class Saskatchewan(Canada, LateFamilyDayMixin, VictoriaDayMixin,
+class Saskatchewan(LateFamilyDayMixin, VictoriaDayMixin,
                    RemembranceDayShiftMixin, AugustCivicHolidayMixin,
-                   ThanksgivingMixin):
+                   ThanksgivingMixin, Canada):
     "Saskatchewan"
     include_good_friday = True
 
@@ -207,8 +214,9 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-MB')
-class Manitoba(Canada, LateFamilyDayMixin, VictoriaDayMixin,
-               AugustCivicHolidayMixin, ThanksgivingMixin):
+class Manitoba(LateFamilyDayMixin, VictoriaDayMixin,
+               AugustCivicHolidayMixin, ThanksgivingMixin,
+               Canada):
     "Manitoba"
     include_good_friday = True
 
@@ -224,7 +232,7 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-NB')
-class NewBrunswick(Canada, AugustCivicHolidayMixin):
+class NewBrunswick(AugustCivicHolidayMixin, Canada):
     "New Brunswick"
 
     FIXED_HOLIDAYS = Canada.FIXED_HOLIDAYS + (
@@ -240,7 +248,7 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-NS')
-class NovaScotia(Canada, RemembranceDayShiftMixin, LateFamilyDayMixin):
+class NovaScotia(RemembranceDayShiftMixin, LateFamilyDayMixin, Canada):
     "Nova Scotia"
 
     include_good_friday = True
@@ -254,14 +262,14 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-PE')
-class PrinceEdwardIsland(Canada, LateFamilyDayMixin, RemembranceDayShiftMixin):
+class PrinceEdwardIsland(LateFamilyDayMixin, RemembranceDayShiftMixin, Canada):
     "Prince Edward Island"
 
     include_good_friday = True
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
-        days.append((self.get_family_day(year, "Islander Day")))
+        days.append(self.get_family_day(year, "Islander Day"))
         days.extend(self.get_remembrance_day(year))
         return days
 
@@ -273,7 +281,7 @@ class Newfoundland(Canada):
 
 
 @iso_register('CA-YT')
-class Yukon(Canada, VictoriaDayMixin, ThanksgivingMixin):
+class Yukon(VictoriaDayMixin, ThanksgivingMixin, Canada):
     "Yukon"
 
     FIXED_HOLIDAYS = Canada.FIXED_HOLIDAYS + (
@@ -293,8 +301,8 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-NT')
-class NorthwestTerritories(Canada, RemembranceDayShiftMixin, VictoriaDayMixin,
-                           ThanksgivingMixin):
+class NorthwestTerritories(RemembranceDayShiftMixin, VictoriaDayMixin,
+                           ThanksgivingMixin, Canada):
     "Northwest Territories"
 
     FIXED_HOLIDAYS = Canada.FIXED_HOLIDAYS + (
@@ -314,8 +322,8 @@ def get_variable_days(self, year):
 
 
 @iso_register('CA-NU')
-class Nunavut(Canada, VictoriaDayMixin, ThanksgivingMixin,
-              RemembranceDayShiftMixin):
+class Nunavut(VictoriaDayMixin, ThanksgivingMixin, RemembranceDayShiftMixin,
+              Canada):
     "Nunavut"
     include_good_friday = True
 
diff --git a/calendra/america/chile.py b/calendra/america/chile.py
index d8d47e1f..29c13f1e 100644
--- a/calendra/america/chile.py
+++ b/calendra/america/chile.py
@@ -1,15 +1,13 @@
 from datetime import date
 
-from ..core import WesternCalendar, ChristianMixin
-from ..core import MON, TUE, WED, FRI
-from ..registry import iso_register
+from ..core import WesternCalendar, MON, TUE, WED, FRI
+from ..registry_tools import iso_register
 
 
 @iso_register('CL')
-class Chile(WesternCalendar, ChristianMixin):
+class Chile(WesternCalendar):
     "Chile"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (5, 21, "Navy Day"),
         (6, 29, "Saint Peter and Saint Paul"),
         (7, 16, "Our Lady of Mount Carmel"),
@@ -18,6 +16,9 @@ class Chile(WesternCalendar, ChristianMixin):
         (10, 12, "Columbus Day"),
         (12, 31, "Banking Holiday"),
     )
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_good_friday = True
     include_easter_saturday = True
     include_assumption = True
diff --git a/calendra/america/colombia.py b/calendra/america/colombia.py
index 10adc9b1..b5e3497e 100644
--- a/calendra/america/colombia.py
+++ b/calendra/america/colombia.py
@@ -1,17 +1,19 @@
 from datetime import timedelta, date
 
-from ..core import WesternCalendar, ChristianMixin, MON
-from ..registry import iso_register
+from ..core import WesternCalendar, MON
+from ..registry_tools import iso_register
 
 
 @iso_register('CO')
-class Colombia(WesternCalendar, ChristianMixin):
+class Colombia(WesternCalendar):
     "Colombia"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (7, 20, "Independence Day"),
         (8, 7, "Boyacá Battle"),
     )
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_palm_sunday = True
     include_holy_thursday = True
     include_good_friday = True
@@ -20,43 +22,86 @@ class Colombia(WesternCalendar, ChristianMixin):
     include_immaculate_conception = True
 
     def get_epiphany(self, year):
+        """
+        Epiphany is shifted in Colombia
+        """
         base_day = date(year, 1, 6)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_saint_joseph(self, year):
         base_day = date(year, 3, 19)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_ascension(self, year):
-        return self.get_easter_sunday(year) + timedelta(days=43)
+        # By default, Ascension falls on THU.
+        # But in Colombia, they celebrate it the next MON.
+        base_day = self.get_ascension_thursday(year)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_corpus_christi(self, year):
-        return self.get_easter_sunday(year) + timedelta(days=64)
+        # By default, Corpus Christi falls 60 days after Easter.
+        # But in Colombia, they celebrate it the next MON.
+        base_day = super().get_corpus_christi(year)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_sacred_heart(self, year):
-        return self.get_easter_sunday(year) + timedelta(days=71)
+        # By default, Sacred Heart falls 68 days after Easter.
+        # But in Colombia, they celebrate it the next MON.
+        base_day = self.get_easter_sunday(year) + timedelta(days=68)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_saint_peter_and_saint_paul(self, year):
         base_day = date(year, 6, 29)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_assumption(self, year):
+        # By default, Assumption is a fixed date (August 15th)
+        # But in Colombia, they celebrate it the next MON.
         base_day = date(year, 8, 15)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
-    def get_race_day(self, year):
+    def get_day_of_the_races(self, year):
+        """
+        Return Day of the Races and Hispanity
+
+        a.k.a. "Día de la Raza"
+        Fixed to the next MON after October 12th (Columbus Day)
+        """
         base_day = date(year, 10, 12)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_all_saints(self, year):
+        # By default, All Saints is a fixed date (November 1st)
+        # But in Colombia, they celebrate it the next MON.
         base_day = date(year, 11, 1)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_cartagena_independence(self, year):
+        """
+        Cartagena independance day
+
+        Fixed to the next MON after November 11th.
+        """
         base_day = date(year, 11, 11)
-        return Colombia.get_first_weekday_after(base_day, MON)
+        return self.get_first_weekday_after(base_day, MON)
 
     def get_variable_days(self, year):
+        """
+        Return variable holidays for Colombia
+
+        The following days are set to "the next MON after the 'true' date".
+
+        * Epiphany,
+        * Saint Joseph,
+        * Ascension,
+        * Corpus Christi,
+        * Sacred Heart,
+        * Saint Peter & Saint Paul
+        * Assumption
+        * Columbus Day / Race Day
+        * All Saints
+        * Cartagena Independance
+        """
         days = super().get_variable_days(year)
         days.extend([
             (self.get_epiphany(year), "Epiphany"),
@@ -66,7 +111,8 @@ def get_variable_days(self, year):
             (self.get_saint_peter_and_saint_paul(year),
                 "Saint Peter and Saint Paul"),
             (self.get_assumption(year), "Assumption of Mary to Heaven"),
-            (self.get_race_day(year), "Race Day"),
+            (self.get_day_of_the_races(year),
+                "Day of the Races and Hispanity"),
             (self.get_all_saints(year), "All Saints"),
             (self.get_cartagena_independence(year),
                 "Cartagena's Independence"),
diff --git a/calendra/america/mexico.py b/calendra/america/mexico.py
index 359af5a0..ebeb5f82 100644
--- a/calendra/america/mexico.py
+++ b/calendra/america/mexico.py
@@ -1,10 +1,11 @@
-from datetime import date
+from datetime import date, timedelta
 
 from dateutil import relativedelta as rd
 
 from ..core import WesternCalendar, ChristianMixin
+from ..core import SUN, SAT
 from ..core import Holiday
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('MX')
@@ -39,3 +40,18 @@ def get_variable_days(self, year):
         ))
 
         return days
+
+    def get_calendar_holidays(self, year):
+        days = super().get_calendar_holidays(year)
+        # If any statutory day is on Sunday, the monday is off
+        # If it's on a Saturday, the Friday is off
+        for day, label in days:
+            if day.weekday() == SAT:
+                days.append((day - timedelta(days=1), "%s substitute" % label))
+            elif day.weekday() == SUN:
+                days.append((day + timedelta(days=1), "%s substitute" % label))
+        # Extra: if new year's day is a saturday, the friday before is off
+        next_new_year = date(year + 1, 1, 1)
+        if next_new_year.weekday():
+            days.append((date(year, 12, 31), "New Year Day substitute"))
+        return days
diff --git a/calendra/america/panama.py b/calendra/america/panama.py
index a989b4d1..683c351e 100644
--- a/calendra/america/panama.py
+++ b/calendra/america/panama.py
@@ -1,19 +1,21 @@
 from datetime import timedelta
 
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('PA')
-class Panama(WesternCalendar, ChristianMixin):
+class Panama(WesternCalendar):
     "Panama"
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_good_friday = True
     include_easter_saturday = True
     include_easter_sunday = True
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (1, 9, "Martyrs' Day"),
-        (5, 1, "Labour Day"),
         (11, 3, "Independence Day"),
         (11, 5, "Colon Day"),
         (11, 10, "Shout in Villa de los Santos"),
diff --git a/calendra/america/paraguay.py b/calendra/america/paraguay.py
index 8e3383b8..b949997d 100644
--- a/calendra/america/paraguay.py
+++ b/calendra/america/paraguay.py
@@ -1,22 +1,25 @@
 from datetime import date
 
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('PY')
-class Paraguay(WesternCalendar, ChristianMixin):
+class Paraguay(WesternCalendar):
     "Paraguay"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (5, 14, "Independence Day"),
         (6, 12, "Chaco Armistice"),
         (9, 19, "Army holiday"),
-        (12, 8, "Virgin of Caacupé Day"),
     )
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_holy_thursday = True
     include_good_friday = True
     include_easter_saturday = True
+    include_immaculate_conception = True
+    immaculate_conception_label = "Virgin of Caacupé Day"
 
     def get_heroes_day(self, year):
         """
diff --git a/calendra/asia/__init__.py b/calendra/asia/__init__.py
index 9d213356..2a65e49b 100644
--- a/calendra/asia/__init__.py
+++ b/calendra/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 @@
 __all__ = (
     'China',
     'HongKong',
+    'HongKongBank',
     'Japan',
     'JapanBank',
     'Malaysia',
diff --git a/calendra/asia/china.py b/calendra/asia/china.py
index 86215094..5d9856de 100644
--- a/calendra/asia/china.py
+++ b/calendra/asia/china.py
@@ -1,8 +1,8 @@
 from datetime import date
 import warnings
 
-from ..core import ChineseNewYearCalendar, WesternCalendar
-from ..registry import iso_register
+from ..core import ChineseNewYearCalendar, SeriesShiftMixin
+from ..registry_tools import iso_register
 from ..exceptions import CalendarError
 
 holidays = {
@@ -28,6 +28,13 @@
             'Dragon Boat Festival': [(6, 25), (6, 26), (6, 27)],
             'National Day': [(10, 8)]
         },
+    2021:
+        {
+            'Ching Ming Festival': [(4, 3), (4, 4), (4, 5)],
+            'Labour Day Holiday': [(5, 1), (5, 2), (5, 3), (5, 4), (5, 5)],
+            'Dragon Boat Festival': [(6, 12), (6, 13), (6, 14)],
+            'Mid-Autumn Festival': [(9, 19), (9, 20), (9, 21)]
+        },
 }
 
 workdays = {
@@ -52,18 +59,29 @@
             'Dragon Boat Festival Shift': [(6, 28)],
             'National Day Shift': [(9, 27), (10, 10)]
         },
+    2021:
+        {
+            'Spring Festival Shift': [(2, 7), (2, 20)],
+            'Labour Day Holiday Shift': [(4, 25), (5, 8)],
+            'Mid-Autumn Festival Shift': [(9, 18)],
+            'National Day Shift': [(9, 26), (10, 9)]
+        },
 }
 
 
 @iso_register('CN')
-class China(ChineseNewYearCalendar, WesternCalendar):
+class China(SeriesShiftMixin, ChineseNewYearCalendar):
     "China"
     # WARNING: Support 2018, 2019 currently, need update every year.
     # National Days, 10.1 - 10.7
     national_days = [(10, i, "National Day") for i in range(1, 8)]
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + tuple(national_days)
+    FIXED_HOLIDAYS = tuple(national_days)
 
     include_chinese_new_year_eve = True
+    series_requiring_shifts = ['Spring Festival', 'Ching Ming Festival',
+                               'Labour Day Holiday', 'Dragon Boat Festival',
+                               'Mid-Autumn Festival', 'New year',
+                               'National Day']
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -74,9 +92,15 @@ def __init__(self, *args, **kwargs):
                     self.extra_working_days.append(date(year, v[0], v[1]))
 
     def get_calendar_holidays(self, year):
-        warnings.warn("Support 2018, 2019 currently, need update every year.")
+        year_min, year_max = min(holidays.keys()), max(holidays.keys())
+        warnings.warn(
+            "Support years {}-{} currently, need update every year.".format(
+                year_min,
+                year_max,
+            )
+        )
         if year not in holidays.keys():
-            msg = "Need configure {} for China.".format(year)
+            msg = f"Need configure {year} for China."
             raise CalendarError(msg)
         return super().get_calendar_holidays(year)
 
diff --git a/calendra/asia/hong_kong.py b/calendra/asia/hong_kong.py
index 2acd30b3..52964f59 100644
--- a/calendra/asia/hong_kong.py
+++ b/calendra/asia/hong_kong.py
@@ -1,20 +1,27 @@
-from datetime import timedelta
+from datetime import date, timedelta
 
-from ..core import ChineseNewYearCalendar, WesternCalendar, ChristianMixin
+from ..core import (
+    ChineseNewYearCalendar, WesternMixin,
+    SUN, SAT
+)
 from ..astronomy import solar_term
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('HK')
-class HongKong(WesternCalendar, ChineseNewYearCalendar, ChristianMixin):
+class HongKong(WesternMixin, ChineseNewYearCalendar):
     "Hong Kong"
+    # Civil holidays
+    include_labour_day = True
+    # Christian holidays
     include_good_friday = True
     include_easter_saturday = True
     include_easter_monday = True
     include_boxing_day = True
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
+    WEEKEND_DAYS = (SUN,)
+
+    FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + (
         (7, 1, "SAR Establishment Day"),
         (10, 1, "National Day"),
     )
@@ -41,6 +48,7 @@ def get_variable_days(self, year):
 
         days = super().get_variable_days(year)
         chingming = solar_term(year, 15, 'Asia/Hong_Kong')
+        solar_term_chingming = chingming
         dupe_holiday = [chingming for day in days if chingming == day[0]]
         if dupe_holiday:
             # Roll Chingming forward a day as it clashes with another holiday
@@ -54,4 +62,27 @@ def get_variable_days(self, year):
             (ChineseNewYearCalendar.lunar(year, 9, 9), "Chung Yeung Festival"),
         ])
 
+        # All holidays that fall on SUN are shifted in
+        # ``ChineseNewYearCalendar.get_calendar_holidays()``
+        # 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:
+            days.append(
+                (date(year, 12, 27), "The second weekday after Christmas")
+            )
+
+        # Special case when Ching Ming and Easter overlap
+        # Ching Ming is shifted to easter monday (but it's handled elsewhere)
+        # Easter Monday is also shifted
+        easter_sunday = self.get_easter_sunday(year)
+        if easter_sunday == solar_term_chingming:
+            days.append((
+                easter_sunday + timedelta(days=2),
+                "The day following Easter Monday"
+            ))
         return days
+
+
+class HongKongBank(HongKong):
+    "Hong Kong Bank"
+    WEEKEND_DAYS = (SAT, SUN)
diff --git a/calendra/asia/israel.py b/calendra/asia/israel.py
index b15f49d1..adec2932 100644
--- a/calendra/asia/israel.py
+++ b/calendra/asia/israel.py
@@ -1,83 +1,64 @@
-from datetime import date, timedelta
-
 from pyluach.dates import GregorianDate, HebrewDate
 
 from ..core import Calendar, FRI, SAT
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register("IL")
 class Israel(Calendar):
     "Israel"
-
+    include_new_years_day = False
     WEEKEND_DAYS = (SAT, FRI)
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
 
-        delta = timedelta(days=1)
-        current_date = date(year, 1, 1)
-
-        while current_date.year == year:
-            hebrew_date = GregorianDate(
-                year=current_date.year,
-                month=current_date.month,
-                day=current_date.day,
-            ).to_heb()
+        hebrew_date = GregorianDate(year=year, month=1, day=1).to_heb()
+        jewish_year = hebrew_date.year
 
-            jewish_year = hebrew_date.year
-            month = hebrew_date.month
-            day = hebrew_date.day
+        holidays_hebrew_dates = [
+            (HebrewDate(jewish_year, 6, 29), "Rosh Hashana Eve"),
+            (HebrewDate(jewish_year + 1, 7, 1), "Rosh Hashana"),
+            (HebrewDate(jewish_year + 1, 7, 2), "Rosh Hashana"),
+            (HebrewDate(jewish_year + 1, 7, 9), "Yom Kippur Eve"),
+            (HebrewDate(jewish_year + 1, 7, 10), "Yom Kippur"),
+            (HebrewDate(jewish_year + 1, 7, 14), "Sukkot Eve"),
+            (HebrewDate(jewish_year + 1, 7, 15), "Sukkot"),
+            (HebrewDate(jewish_year + 1, 7, 21), "Shmini Atzeres Eve"),
+            (HebrewDate(jewish_year + 1, 7, 22), "Shmini Atzeres"),
+            (HebrewDate(jewish_year, 1, 14), "Pesach Eve"),
+            (HebrewDate(jewish_year, 1, 15), "Pesach"),
+            (HebrewDate(jewish_year, 1, 20), "7th of Pesach Eve"),
+            (HebrewDate(jewish_year, 1, 21), "7th of Pesach"),
+            (HebrewDate(jewish_year, 3, 5), "Shavout Eve"),
+            (HebrewDate(jewish_year, 3, 6), "Shavout"),
+        ]
+        holidays_hebrew_dates += self.get_hebrew_independence_day(jewish_year)
 
-            if month == 7:
-                if day in {1, 2, 3}:
-                    days.append((current_date, "Rosh Hashana"))
-                elif day == 10:
-                    days.append((current_date, "Yom Kippur"))
-                elif day in range(15, 22):
-                    days.append((current_date, "Sukkot"))
-                elif day == 22:
-                    days.append((current_date, "Shmini Atzeres"))
-            elif month == 12 and not HebrewDate._is_leap(jewish_year):
-                if day == 14:
-                    days.append((current_date, "Purim"))
-                if day == 15:
-                    days.append((current_date, "Shushan Purim"))
-            elif month == 13:
-                if day == 14:
-                    days.append((current_date, "Purim"))
-                elif day == 15:
-                    days.append((current_date, "Shushan Purim"))
-            elif month == 1 and day in {15, 21}:
-                days.append((current_date, "Pesach"))
-            elif month == 2:
-                if day == 5:
-                    if hebrew_date.weekday() == 6:
-                        days.append(
-                            (
-                                HebrewDate(jewish_year, month, 4).to_pydate(),
-                                "Independence Day",
-                            )
-                        )
-                    elif hebrew_date.weekday() == 7:
-                        days.append(
-                            (
-                                HebrewDate(jewish_year, month, 3).to_pydate(),
-                                "Independence Day",
-                            )
-                        )
-                    elif hebrew_date.weekday() == 2:
-                        days.append(
-                            (
-                                HebrewDate(jewish_year, month, 6).to_pydate(),
-                                "Independence Day",
-                            )
-                        )
-                    else:
-                        days.append((current_date, "Independence Day"))
-            elif month == 3 and day == 6:
-                days.append((current_date, "Shavout"))
+        for holiday_hebrew_date, holiday_name in holidays_hebrew_dates:
+            days.append((holiday_hebrew_date.to_pydate(), holiday_name))
+        return days
 
-            current_date += delta
+    def get_hebrew_independence_day(self, jewish_year):
+        """
+        Returns the independence day eve and independence day dates
+        according to the given hebrew year
 
-        return days
+        :param jewish_year: the specific hebrew year for calculating
+                            the independence day dates
+        :return: independence day dates
+                 in the type of List[Tuple[HebrewDate, str]]
+        """
+        month = 2
+        day = 5
+        original_hebrew_independence_date = HebrewDate(jewish_year, month, day)
+        if original_hebrew_independence_date.weekday() == 6:
+            day = 4
+        if original_hebrew_independence_date.weekday() == 7:
+            day = 3
+        if original_hebrew_independence_date.weekday() == 2:
+            day = 6
+        return [
+            (HebrewDate(jewish_year, month, day - 1), "Independence Day Eve"),
+            (HebrewDate(jewish_year, month, day), "Independence Day")
+        ]
diff --git a/calendra/asia/japan.py b/calendra/asia/japan.py
index a15d8832..04ffc17d 100644
--- a/calendra/asia/japan.py
+++ b/calendra/asia/japan.py
@@ -1,16 +1,18 @@
 from datetime import date
 
-from ..core import MON
-from ..core import WesternCalendar
+from ..core import Calendar, MON, SAT, SUN
 from ..astronomy import calculate_equinoxes
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('JP')
-class Japan(WesternCalendar):
+class Japan(Calendar):
     "Japan"
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    # Japan uses the "western" workweek
+    WEEKEND_DAYS = (SAT, SUN)
+
+    FIXED_HOLIDAYS = Calendar.FIXED_HOLIDAYS + (
         (2, 11, "Foundation Day"),
         (4, 29, "Showa Day"),
         (5, 3, "Constitution Memorial Day"),
diff --git a/calendra/asia/malaysia.py b/calendra/asia/malaysia.py
index b3c045cd..480b0875 100644
--- a/calendra/asia/malaysia.py
+++ b/calendra/asia/malaysia.py
@@ -1,13 +1,20 @@
 from datetime import date
 
-from ..core import ChineseNewYearCalendar, WesternCalendar
-from ..core import IslamicMixin
-from ..registry import iso_register
+from ..core import (
+    IslamicMixin, ChineseNewYearCalendar,
+    SAT, SUN
+)
+from ..registry_tools import iso_register
 
 
 @iso_register('MY')
-class Malaysia(ChineseNewYearCalendar, WesternCalendar, IslamicMixin):
+class Malaysia(IslamicMixin, ChineseNewYearCalendar):
     "Malaysia"
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Workers' Day"
+
+    # Islamic holidays
     include_nuzul_al_quran = True
     include_eid_al_fitr = True
     length_eid_al_fitr = 2
@@ -17,9 +24,12 @@ class Malaysia(ChineseNewYearCalendar, WesternCalendar, IslamicMixin):
     include_islamic_new_year = True
     include_prophet_birthday = True
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    # Most of th Malaysian territory uses these Week-end days
+    WEEKEND_DAYS = (SAT, SUN)
+    # TODO: Add calendar exceptions
+
+    FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + (
         (2, 1, "Federal Territory Day"),
-        (5, 1, "Workers' Day"),
         (8, 31, "National Day"),
         (9, 16, "Malaysia Day"),
         (12, 25, "Christmas Day"),
diff --git a/calendra/asia/qatar.py b/calendra/asia/qatar.py
index 14034392..9cf3ace7 100644
--- a/calendra/asia/qatar.py
+++ b/calendra/asia/qatar.py
@@ -1,12 +1,12 @@
-from ..core import Calendar
-from ..core import FRI, SAT, IslamicMixin
-from ..registry import iso_register
+from ..core import IslamicCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('QA')
-class Qatar(IslamicMixin, Calendar):
+class Qatar(IslamicCalendar):
     "Qatar"
-    WEEKEND_DAYS = (FRI, SAT)
+
+    include_new_years_day = False
 
     FIXED_HOLIDAYS = (
         (12, 18, "National Day"),
diff --git a/calendra/asia/singapore.py b/calendra/asia/singapore.py
index eaaa61fa..a4149f05 100644
--- a/calendra/asia/singapore.py
+++ b/calendra/asia/singapore.py
@@ -1,26 +1,34 @@
 from datetime import date
 
 from ..core import (
-    ChineseNewYearCalendar, WesternCalendar, ChristianMixin, IslamicMixin
+    WesternMixin, IslamicMixin, ChineseNewYearCalendar,
+    SAT, SUN
 )
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('SG')
-class Singapore(WesternCalendar,
-                ChineseNewYearCalendar, ChristianMixin, IslamicMixin):
+class Singapore(WesternMixin, IslamicMixin, ChineseNewYearCalendar):
     "Singapore"
+    # Civil holidays
+    include_labour_day = True
+
+    # Christian holiday
     include_good_friday = True
+
+    # Islamic holidays
     include_eid_al_fitr = True
     eid_al_fitr_label = "Hari Raya Puasa"
     include_day_of_sacrifice = True
     day_of_sacrifice_label = "Hari Raya Haji"
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
+    FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + (
         (8, 9, "National Day"),
     )
 
+    # Explicitly assign these WE days, Singapore calendar is too much of a mix
+    WEEKEND_DAYS = (SAT, SUN)
+
     # Diwali/Deepavali is sometimes celebrated on a different day to India
     # so this can't be put into a HinduMixin
     DEEPAVALI = {
@@ -44,8 +52,8 @@ class Singapore(WesternCalendar,
         2017: date(2017, 10, 18),
         2018: date(2018, 11, 6),
         2019: date(2019, 10, 27),
-        2020: date(2020, 11, 14),   # This might change
-        2021: date(2021, 11, 4),
+        2020: date(2020, 11, 14),
+        2021: date(2021, 11, 4),  # This might change
     }
     chinese_new_year_label = "Chinese Lunar New Year's Day"
     include_chinese_second_day = True
@@ -56,6 +64,10 @@ def get_variable_days(self, year):
         """
         Singapore variable days
         """
+        if year not in self.DEEPAVALI:
+            msg = 'Missing date for Singapore Deepavali for year: %s' % year
+            raise KeyError(msg)
+
         days = super().get_variable_days(year)
 
         # Vesak Day
@@ -64,9 +76,5 @@ def get_variable_days(self, year):
         )
 
         # Add in Deepavali (hardcoded dates, so no need to shift)
-        deepavali = self.DEEPAVALI.get(year)
-        if not deepavali:
-            msg = 'Missing date for Singapore Deepavali for year: %s' % year
-            raise KeyError(msg)
-        days.append((deepavali, 'Deepavali'))
+        days.append((self.DEEPAVALI.get(year), 'Deepavali'))
         return days
diff --git a/calendra/asia/south_korea.py b/calendra/asia/south_korea.py
index 3715e0fa..0a735387 100644
--- a/calendra/asia/south_korea.py
+++ b/calendra/asia/south_korea.py
@@ -1,11 +1,11 @@
-from ..core import ChineseNewYearCalendar, WesternCalendar
-from ..registry import iso_register
+from ..core import ChineseNewYearCalendar, SeriesShiftMixin
+from ..registry_tools import iso_register
 
 
 @iso_register('KR')
-class SouthKorea(WesternCalendar, ChineseNewYearCalendar):
+class SouthKorea(SeriesShiftMixin, ChineseNewYearCalendar):
     "South Korea"
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + (
         (3, 1, "Independence Day"),
         (5, 5, "Children's Day"),
         (6, 6, "Memorial Day"),
@@ -19,6 +19,7 @@ class SouthKorea(WesternCalendar, ChineseNewYearCalendar):
     chinese_new_year_eve_label = "Korean New Year's Day"
     include_chinese_second_day = True
     chinese_second_day_label = "Korean New Year's Day"
+    series_requiring_shifts = ["Korean New Year's Day", "Midautumn Festival"]
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
diff --git a/calendra/asia/taiwan.py b/calendra/asia/taiwan.py
index 22ac0679..f84bef9b 100644
--- a/calendra/asia/taiwan.py
+++ b/calendra/asia/taiwan.py
@@ -1,18 +1,15 @@
-from ..core import ChineseNewYearCalendar, WesternCalendar
+from ..core import ChineseNewYearCalendar
 from ..astronomy import solar_term
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('TW')
-class Taiwan(ChineseNewYearCalendar, WesternCalendar):
+class Taiwan(ChineseNewYearCalendar):
     "Taiwan (Republic of China)"
-    FIXED_HOLIDAYS = (
-        WesternCalendar.FIXED_HOLIDAYS +
-        (
-            (2, 28, "228 Peace Memorial Day"),
-            (4, 4, "Combination of Women's Day and Children's Day"),
-            (10, 10, "National Day/Double Tenth Day"),
-        )
+    FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + (
+        (2, 28, "228 Peace Memorial Day"),
+        (4, 4, "Combination of Women's Day and Children's Day"),
+        (10, 10, "National Day/Double Tenth Day"),
     )
     include_chinese_new_year_eve = True
     include_chinese_second_day = True
diff --git a/calendra/astronomy.py b/calendra/astronomy.py
index d5a3b713..ae157f16 100644
--- a/calendra/astronomy.py
+++ b/calendra/astronomy.py
@@ -1,7 +1,7 @@
 """
 Astronomical functions
 """
-from math import pi, radians
+from math import pi, radians, tau
 import pytz
 from skyfield.api import Loader
 from skyfield import almanac
@@ -16,9 +16,6 @@
 second = minute / 60.
 newton_precision = second / 10.
 
-# ``math.tau`` appears only in Python 3.6+
-tau = 2 * pi
-
 
 def calculate_equinoxes(year, timezone='UTC'):
     """ calculate equinox with time zone """
diff --git a/calendra/core.py b/calendra/core.py
index 141cf563..88e6347a 100644
--- a/calendra/core.py
+++ b/calendra/core.py
@@ -1,11 +1,14 @@
 """
 Working day tools
 """
-from copy import copy
+import os
+from os.path import isdir
 import warnings
+
 import itertools
 from calendar import monthrange
 from datetime import date, timedelta, datetime
+from typing import Optional, List
 
 from calverter import Calverter
 from dateutil import easter
@@ -13,7 +16,11 @@
 from dateutil import relativedelta as rd
 from more_itertools import recipes
 
-from .exceptions import UnsupportedDateType
+from .exceptions import (
+    UnsupportedDateType, CalendarError,
+    ICalExportRangeError, ICalExportTargetPathError
+)
+from . import __version__
 
 MON, TUE, WED, THU, FRI, SAT, SUN = range(7)
 
@@ -45,6 +52,21 @@ def cleaned_date(day, keep_datetime=False):
     return day
 
 
+def daterange(start, end):
+    """
+    Yield days from ``start`` to ``end`` including both of them.
+
+    If start and end are in opposite order, they'll be swapped silently.
+    """
+    # Swap if necessary
+    if start > end:
+        end, start = start, end
+    day = start
+    while day <= end:
+        yield day
+        day += timedelta(days=1)
+
+
 class Holiday(date):
     """
     A named holiday with an indicated date, name, and additional keyword
@@ -140,7 +162,7 @@ def _from_resolved_definition(cls, item):
         return item
 
 
-class Calendar:
+class CoreCalendar:
 
     FIXED_HOLIDAYS = ()
     WEEKEND_DAYS = ()
@@ -333,8 +355,6 @@ def add_working_days(self, day, delta,
 
         days = 0
         temp_day = day
-        if type(temp_day) is datetime and not keep_datetime:
-            temp_day = temp_day.date()
         day_added = 1 if delta >= 0 else -1
         delta = abs(delta)
         while days < delta:
@@ -506,12 +526,231 @@ def get_working_days_delta(self, start, end, include_start=False):
                 count += 1
         return count
 
+    def _get_ical_period(self, period=None):
+        """
+        Return a usable period for iCal export
+
+        Default period is [2000, 2030]
+        """
+        # Default value.
+        if not period:
+            period = [2000, 2030]
+
+        # Make sure it's a usable iterable
+        if type(period) not in (list, tuple):
+            raise ICalExportRangeError(
+                "Incorrect Range type. Must be list or tuple.")
+
+        # Taking the extremes
+        period = [min(period), max(period)]
+
+        # check for internal types
+        check_types = map(type, period)
+        check_types = map(lambda x: x != int, check_types)
+        if any(check_types):
+            raise ICalExportRangeError(
+                "Incorrect Range boundaries. Must be int.")
+
+        return period
+
+    def _get_ical_target_path(self, target_path):
+        """
+        Return target path for iCal export.
+
+        Note
+        ----
+        If `target_path` does not have one of the extensions `.ical`, `.ics`,
+        `.ifb`, or `.icalendar`, the extension `.ics` is appended to the path.
+        Returns
+        -------
+        None.
+        Examples
+        --------
+        >>> from calendra.europe import Austria
+        >>> cal = Austria()
+        >>> cal._get_ical_target_path('austria')
+        'austria.ics'
+        """
+
+        if not target_path:
+            raise ICalExportTargetPathError(
+                "Incorrect target path. It must not be empty")
+
+        if isdir(target_path):
+            raise ICalExportTargetPathError(
+                "Incorrect target path. It must not be a directory"
+            )
+
+        ical_extensions = ['.ical', '.ics', '.ifb', '.icalendar']
+        if os.path.splitext(target_path)[1] not in ical_extensions:
+            target_path += '.ics'
+        return target_path
+
+    def export_to_ical(self, period=[2000, 2030], target_path=None):
+        """
+        Export the calendar to iCal (RFC 5545) format.
+
+        Parameters
+        ----------
+        period: [int, int]
+            start and end year (inclusive) of calendar
+            Default is [2000, 2030]
+
+        target_path: str
+            the name or path of the exported file. If this argument is missing,
+            the function will return the ical content.
+
+        """
+        first_year, last_year = self._get_ical_period(period)
+        if target_path:
+            # Generate filename path before calculate the holidays
+            target_path = self._get_ical_target_path(target_path)
+
+        # fetch holidays
+        holidays = []
+        for year in range(first_year, last_year + 1):
+            holidays.extend(self.holidays(year))
+
+        # initialize icalendar
+        ics = [
+            'BEGIN:VCALENDAR',
+            'VERSION:2.0',  # current RFC5545 version
+            f'PRODID:-//workalendar//ical {__version__}//EN'
+        ]
+        common_timestamp = datetime.utcnow().strftime('%Y%m%dT%H%M%SZ')
+        dtstamp = 'DTSTAMP;VALUE=DATE-TIME:%s' % common_timestamp
+
+        # add an event for each holiday
+        for holiday in holidays:
+            date_ = self.get_observed_date(holiday)
+            ics.extend([
+                'BEGIN:VEVENT',
+                'SUMMARY:%s' % holiday.name,
+                'DTSTART;VALUE=DATE:%s' % date_.strftime('%Y%m%d'),
+                dtstamp,
+                f'UID:{date_}{holiday.name}@peopledoc.github.io/workalendar',
+                'END:VEVENT',
+            ])
+
+        # add footer
+        ics.append('END:VCALENDAR\n')  # last line with a trailing \n
+
+        # Transform this list into text lines
+        ics = "\n".join(ics)
+
+        if target_path:
+            # save iCal file
+            with open(target_path, 'w+') as export_file:
+                export_file.write(ics)
+            return
+
+        return ics
+
+
+class Calendar(CoreCalendar):
+    """
+    The cornerstone of Earth calendars.
+
+    Take care of the New Years Day, which is almost a worldwide holiday.
+    """
+    include_new_years_day = True
+    shift_new_years_day = False
+    include_labour_day = False
+    labour_day_label = "Labour Day"
+
+    def __init__(self, **kwargs):
+        super().__init__()
+
+    def get_fixed_holidays(self, year):
+        days = super().get_fixed_holidays(year)
+        if self.include_new_years_day:
+            days.insert(
+                0, (date(year, 1, 1), "New year")
+            )
+
+        if self.include_labour_day:
+            days.append(
+                (date(year, 5, 1), self.labour_day_label)
+            )
+        return days
+
+    def get_variable_days(self, year):
+        days = super().get_variable_days(year)
+        new_year = date(year, 1, 1)
+        if self.include_new_years_day and self.shift_new_years_day:
+            if new_year.weekday() in self.get_weekend_days():
+                days.append((
+                    self.find_following_working_day(new_year),
+                    "New Year shift"))
+        return days
+
+
+class SeriesShiftMixin:
+    """
+    "Series" holidays like the two Islamic Eid's or Chinese Spring Festival span
+    multiple days. If one of these days encounters a non-zero observance_shift,
+    we need to apply that shift to all subsequent members of the series.
+
+    Packagin as a standalone Mixin ensure that the logic can be applied as
+    needed *after* any default shift is applied.
+    """
+    series_requiring_shifts: Optional[List[str]] = None
+    """
+    A list of all holiday labels that require series shifting to be applied.
+    """
 
-class ChristianMixin(Calendar):
+    def get_calendar_holidays(self, year):
+        """
+        The point at which any shift occurs is year-specific.
+        """
+        days = super().get_calendar_holidays(year)
+        series_shift = {series: None for series in self.series_requiring_shifts}
+        holidays = []
+        for holiday, label in days:
+            #
+            # Make a year-specific copy in case we have to attach a shift.
+            #
+            holiday = Holiday(holiday, label)
+            #
+            # For either Eid series, apply the shift to all days in the
+            # series after the first shift.
+            #
+            if label in series_shift:
+                shifted = self.get_observed_date(holiday)
+                if series_shift[holiday.name] is None and shifted.day != holiday.day:
+
+                    def observance_shift_for_series(holiday, calendar):
+                        """
+                        Taking an existing holiday, return a 'shifted' day based
+                        on delta in the current year's closure.
+                        """
+                        return holiday + delta
+
+                    delta = date(shifted.year, shifted.month, shifted.day) - \
+                        date(holiday.year, holiday.month, holiday.day)
+                    #
+                    # Learn the observance_shift for all subsequent days in the
+                    # series.
+                    #
+                    series_shift[holiday.name] = observance_shift_for_series
+                elif series_shift[holiday.name] is not None:
+                    #
+                    # Apply the learned observance_shift.
+                    #
+                    holiday.observance_shift = series_shift[holiday.name]
+            holidays.append(holiday)
+        return holidays
+
+
+class ChristianMixin:
     EASTER_METHOD = None  # to be assigned in the inherited mixin
     include_epiphany = False
     include_clean_monday = False
     include_annunciation = False
+    include_fat_tuesday = False
+    # Fat tuesday forced to `None` to make sure this value is always set
+    # We've seen that there was a wide variety of labels.
+    fat_tuesday_label = None
     include_ash_wednesday = False
     ash_wednesday_label = "Ash Wednesday"
     include_palm_sunday = False
@@ -537,6 +776,14 @@ class ChristianMixin(Calendar):
     boxing_day_label = "Boxing Day"
     include_all_souls = False
 
+    def get_fat_tuesday(self, year):
+        if not self.fat_tuesday_label:
+            raise CalendarError(
+                "Improperly configured: please provide a "
+                "`fat_tuesday_label` value")
+        sunday = self.get_easter_sunday(year)
+        return sunday - timedelta(days=47)
+
     def get_ash_wednesday(self, year):
         sunday = self.get_easter_sunday(year)
         return sunday - timedelta(days=46)
@@ -615,6 +862,10 @@ def get_variable_days(self, year):  # noqa
             days.append((self.get_clean_monday(year), "Clean Monday"))
         if self.include_annunciation:
             days.append((date(year, 3, 25), "Annunciation"))
+        if self.include_fat_tuesday:
+            days.append(
+                (self.get_fat_tuesday(year), self.fat_tuesday_label)
+            )
         if self.include_ash_wednesday:
             days.append(
                 (self.get_ash_wednesday(year), self.ash_wednesday_label)
@@ -665,7 +916,7 @@ def get_variable_days(self, year):  # noqa
         return days
 
 
-class WesternCalendar(Calendar):
+class WesternMixin(ChristianMixin):
     """
     General usage calendar for Western countries.
 
@@ -674,6 +925,7 @@ class WesternCalendar(Calendar):
     """
     EASTER_METHOD = easter.EASTER_WESTERN
     WEEKEND_DAYS = (SAT, SUN)
+    include_new_years_day = False
     shift_new_years_day = False
 
     def get_variable_days(self, year):
@@ -687,11 +939,31 @@ def get_variable_days(self, year):
         return days
 
 
+class WesternCalendar(WesternMixin, Calendar):
+    pass
+
+
 class OrthodoxMixin(ChristianMixin):
     EASTER_METHOD = easter.EASTER_ORTHODOX
+    WEEKEND_DAYS = (SAT, SUN)
+    include_orthodox_christmas = True
+    # This label should be de-duplicated if needed
+    orthodox_christmas_day_label = "Christmas"
 
+    def get_fixed_holidays(self, year):
+        days = super().get_fixed_holidays(year)
+        if self.include_orthodox_christmas:
+            days.append(
+                (date(year, 1, 7), self.orthodox_christmas_day_label)
+            )
+        return days
 
-class LunarCalendar(Calendar):
+
+class OrthodoxCalendar(OrthodoxMixin, Calendar):
+    pass
+
+
+class LunarMixin:
     """
     Calendar ready to compute luncar calendar days
     """
@@ -700,7 +972,11 @@ def lunar(year, month, day):
         return LunarDate(year, month, day).toSolarDate()
 
 
-class ChineseNewYearCalendar(LunarCalendar):
+class LunarCalendar(LunarMixin, Calendar):
+    pass
+
+
+class ChineseNewYearMixin(LunarMixin):
     """
     Calendar including toolsets to compute the Chinese New Year holidays.
     """
@@ -739,7 +1015,7 @@ def get_chinese_new_year(self, year):
         """
         days = []
 
-        lunar_first_day = ChineseNewYearCalendar.lunar(year, 1, 1)
+        lunar_first_day = ChineseNewYearMixin.lunar(year, 1, 1)
         # Chinese new year's eve
         if self.include_chinese_new_year_eve:
             days.append((
@@ -792,17 +1068,24 @@ def get_variable_days(self, year):
         days.extend(self.get_chinese_new_year(year))
         return days
 
+    @staticmethod
+    def observance_shift_for_sunday(holiday, calendar):
+        """
+        Taking an existing holiday, return a 'shifted' day to skip on from SUN.
+        """
+        return holiday + timedelta(days=1)
+
     def get_shifted_holidays(self, dates):
         """
         Taking a list of existing holidays, yield a list of 'shifted' days if
         the holiday falls on SUN.
         """
         for holiday, label in dates:
+            if isinstance(holiday, date):
+                holiday = Holiday(holiday, label)
             if holiday.weekday() == SUN:
-                yield (
-                    holiday + timedelta(days=1),
-                    label + ' shift'
-                )
+                holiday.observance_shift = self.observance_shift_for_sunday
+            yield holiday
 
     def get_calendar_holidays(self, year):
         """
@@ -812,13 +1095,19 @@ def get_calendar_holidays(self, year):
         # 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)
+            days = self.get_shifted_holidays(days)
         return days
 
 
-class CalverterMixin(Calendar):
+class ChineseNewYearCalendar(ChineseNewYearMixin, LunarCalendar):
+    """
+    Chinese Calendar, using Chinese New Year computation.
+    """
+    # There are regional exceptions to those week-end days, to define locally.
+    WEEKEND_DAYS = (SAT, SUN)
+
+
+class CalverterMixin:
     conversion_method = None
     ISLAMIC_HOLIDAYS = ()
 
@@ -881,7 +1170,10 @@ def get_variable_days(self, year):
         return days
 
 
-class IslamicMixin(CalverterMixin):
+class IslamicMixin(SeriesShiftMixin, CalverterMixin):
+
+    WEEKEND_DAYS = (FRI, SAT)
+
     conversion_method = 'islamic'
     include_prophet_birthday = False
     include_day_after_prophet_birthday = False
@@ -927,6 +1219,26 @@ def get_islamic_holidays(self):
                           " to compute it. You'll have to add it manually.")
         return tuple(days)
 
+    def get_calendar_holidays(self, year):
+        self.series_requiring_shifts = [self.eid_al_fitr_label,
+                                        self.day_of_sacrifice_label]
+        return super().get_calendar_holidays(year)
+
 
 class JalaliMixin(CalverterMixin):
     conversion_method = 'jalali'
+
+
+class IslamicCalendar(IslamicMixin, Calendar):
+    """
+    Islamic calendar
+    """
+
+
+class IslamoWesternCalendar(IslamicMixin, WesternMixin, Calendar):
+    """
+    Mix of Islamic and Western calendars.
+
+    When countries have both Islamic and Western-Christian holidays.
+    """
+    FIXED_HOLIDAYS = Calendar.FIXED_HOLIDAYS
diff --git a/calendra/europe/__init__.py b/calendra/europe/__init__.py
index d1322e6d..c296a8af 100644
--- a/calendra/europe/__init__.py
+++ b/calendra/europe/__init__.py
@@ -1,4 +1,5 @@
 from .austria import Austria
+from .belarus import Belarus
 from .belgium import Belgium
 from .bulgaria import Bulgaria
 from .cayman_islands import CaymanIslands
@@ -19,7 +20,8 @@
 from .lithuania import Lithuania
 from .luxembourg import Luxembourg
 from .malta import Malta
-from .netherlands import Netherlands
+from .monaco import Monaco
+from .netherlands import Netherlands, NetherlandsWithSchoolHolidays
 from .norway import Norway
 from .poland import Poland
 from .portugal import Portugal
@@ -28,9 +30,13 @@
 from .serbia import Serbia
 from .slovakia import Slovakia
 from .slovenia import Slovenia
-from .spain import Spain, Catalonia
+from .spain import (
+    Andalusia, Aragon, Spain, Catalonia, CastileAndLeon, CastillaLaMancha,
+    CanaryIslands, Extremadura, Galicia, BalearicIslands, LaRioja,
+    CommunityofMadrid, Murcia, Navarre, Asturias, BasqueCountry, Cantabria,
+    ValencianCommunity,
+)
 from .sweden import Sweden
-from .switzerland import Switzerland, Vaud, Geneva
 from .ukraine import Ukraine
 from .united_kingdom import UnitedKingdom, UnitedKingdomNorthernIreland
 from .turkey import Turkey
@@ -52,11 +58,19 @@
     ScottishBorders, SouthLanarkshire, Stirling, WestDunbartonshire
 )
 
+# Switzerland
+from .switzerland import (
+    Switzerland, Aargau, AppenzellInnerrhoden, AppenzellAusserrhoden, Bern,
+    BaselLandschaft, BaselStadt, Fribourg, Geneva, Glarus, Graubunden, Jura,
+    Luzern, Neuchatel, Nidwalden, Obwalden, StGallen, Schaffhausen, Solothurn,
+    Schwyz, Thurgau, Ticino, Uri, Vaud, Valais, Zug, Zurich
+)
+
 __all__ = (
     'Austria',
+    'Belarus',
     'Belgium',
     'Bulgaria',
-    'Catalonia',
     'CaymanIslands',
     'Croatia',
     'Cyprus',
@@ -76,7 +90,9 @@
     'Lithuania',
     'Luxembourg',
     'Malta',
+    'Monaco',
     'Netherlands',
+    'NetherlandsWithSchoolHolidays',
     'Norway',
     'Poland',
     'Portugal',
@@ -85,11 +101,8 @@
     'Serbia',
     'Slovakia',
     'Slovenia',
-    'Spain',
     'Sweden',
     'Switzerland',
-    'Vaud',
-    'Geneva',
     'Ukraine',
     'UnitedKingdom',
     'UnitedKingdomNorthernIreland',
@@ -107,5 +120,19 @@
     'Edinburgh', 'Elgin', 'Falkirk', 'Fife', 'Galashiels', 'Glasgow',
     'Hawick', 'Inverclyde', 'Inverness', 'Kilmarnock', 'Lanark', 'Linlithgow',
     'Lochaber', 'NorthLanarkshire', 'Paisley', 'Perth', 'ScottishBorders',
-    'SouthLanarkshire', 'Stirling', 'WestDunbartonshire'
+    'SouthLanarkshire', 'Stirling', 'WestDunbartonshire',
+
+    # Spain
+    'Spain', 'Andalusia', 'Aragon', 'Catalonia', 'CastileAndLeon',
+    'CastillaLaMancha', 'CanaryIslands', 'Extremadura', 'Galicia',
+    'BalearicIslands', 'LaRioja', 'CommunityofMadrid', 'Murcia', 'Navarre',
+    'Asturias', 'BasqueCountry', 'Cantabria', 'ValencianCommunity',
+
+    # Switzerland
+    'Switzerland',
+    'Aargau', 'AppenzellInnerrhoden', 'AppenzellAusserrhoden', 'Bern',
+    'BaselLandschaft', 'BaselStadt', 'Fribourg', 'Geneva', 'Glarus',
+    'Graubunden', 'Jura', 'Luzern', 'Neuchatel', 'Nidwalden', 'Obwalden',
+    'StGallen', 'Schaffhausen', 'Solothurn', 'Schwyz', 'Thurgau', 'Ticino',
+    'Uri', 'Vaud', 'Valais', 'Zug', 'Zurich',
 )
diff --git a/calendra/europe/austria.py b/calendra/europe/austria.py
index e31ec81a..c59f13f7 100644
--- a/calendra/europe/austria.py
+++ b/calendra/europe/austria.py
@@ -1,16 +1,18 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('AT')
-class Austria(WesternCalendar, ChristianMixin):
+class Austria(WesternCalendar):
     'Austria'
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "National Holiday"),  # Staatsfeiertag
         (10, 26, "National Holiday"),  # Nationalfeiertag
     )
-
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "State Holiday"  # Staatsfeiertag
+    # Christian holidays
     include_epiphany = True
     include_easter_monday = True
     include_ascension = True
diff --git a/calendra/europe/belarus.py b/calendra/europe/belarus.py
new file mode 100644
index 00000000..53abe956
--- /dev/null
+++ b/calendra/europe/belarus.py
@@ -0,0 +1,35 @@
+from datetime import timedelta
+from ..core import OrthodoxCalendar
+from ..registry_tools import iso_register
+
+
+@iso_register('BY')
+class Belarus(OrthodoxCalendar):
+    'Belarus'
+    "as of http://president.gov.by/en/holidays_en/"
+    # Civil holidays
+    include_labour_day = True
+
+    # Christian holidays
+    include_christmas = True
+    christmas_day_label = "Christmas Day (Catholic)"
+    orthodox_christmas_day_label = "Christmas (Orthodox)"
+
+    FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + (
+        (1, 2, "Day After New Year"),
+        (3, 8, "International Women's Day"),
+        (5, 9, "Victory Day"),
+        (7, 3, "Republic Day"),
+        (11, 7, "October Revolution Day"),
+    )
+    # Radonitsa
+    # https://en.wikipedia.org/wiki/Radonitsa
+
+    def get_radonitsa(self, year):
+        # 9 Days after the Orthodox easter sunday
+        return self.get_easter_sunday(year) + timedelta(days=9)
+
+    def get_variable_days(self, year):
+        days = super().get_variable_days(year)
+        days.append((self.get_radonitsa(year), "Radonitsa"))
+        return days
diff --git a/calendra/europe/belgium.py b/calendra/europe/belgium.py
index 750e15ed..9ca4e1ce 100644
--- a/calendra/europe/belgium.py
+++ b/calendra/europe/belgium.py
@@ -1,17 +1,19 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('BE')
-class Belgium(WesternCalendar, ChristianMixin):
+class Belgium(WesternCalendar):
     'Belgium'
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (7, 21, "National Day"),
         (11, 11, "Armistice of 1918"),
     )
+    # Civil holidays
+    include_labour_day = True
 
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
diff --git a/calendra/europe/bulgaria.py b/calendra/europe/bulgaria.py
index 730b4fd4..aa8ec2f6 100644
--- a/calendra/europe/bulgaria.py
+++ b/calendra/europe/bulgaria.py
@@ -1,23 +1,27 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('BG')
-class Bulgaria(WesternCalendar, ChristianMixin):
+class Bulgaria(WesternCalendar):
     'Bulgaria'
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (3, 3, "Liberation Day"),  # Ден на Освобождението на Б
-        (5, 1, "International Workers' Day"),  # Ден на труда и на междунар
         (5, 6, "Saint George's Day"),  # Гергьовден, ден на храброс
         (5, 24, "Saints Cyril & Methodius Day"),  # Ден на българската просвет
         (9, 6, "Unification Day"),  # Ден на Съединението
         (9, 22, "Independence Day"),  # Ден на независимостта на Б
         # wikipedia says Non-attendance day for schools, otherwise a working da
         # (11, 1, "National Awakening Day"),  # Ден на народните будители
-
     )
 
+    # Civil holidays
+    include_labour_day = True
+    # Ден на труда и на междунар
+    labour_day_label = "International Workers' Day"
+
+    # Christian holidays
     include_easter_sunday = True
     include_easter_monday = True
     include_christmas_eve = True  # Бъдни вечер
diff --git a/calendra/europe/cayman_islands.py b/calendra/europe/cayman_islands.py
index 2fbf0eb4..e6575256 100644
--- a/calendra/europe/cayman_islands.py
+++ b/calendra/europe/cayman_islands.py
@@ -1,8 +1,7 @@
 from datetime import date, timedelta
 
-from ..core import WesternCalendar, ChristianMixin, MON, SAT
-from ..registry import iso_register
-
+from ..core import WesternCalendar, MON, SAT
+from ..registry_tools import iso_register
 
 QUEENS_BIRTHDAY_EXCEPTIONS = {
     2013: date(2013, 6, 17),
@@ -11,7 +10,7 @@
 
 
 @iso_register('KY')
-class CaymanIslands(WesternCalendar, ChristianMixin):
+class CaymanIslands(WesternCalendar):
     "Cayman Islands"
     include_ash_wednesday = True
     include_good_friday = True
@@ -21,38 +20,46 @@ class CaymanIslands(WesternCalendar, ChristianMixin):
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
-        days += [
+        days.extend([
             (self.get_national_heroes_day(year), "National Heroes Day"),
             (self.get_discovery_day(year), "Discovery Day"),
             (self.get_queens_birthday(year), "Queen's Birthday"),
             (self.get_constitution_day(year), "Constitution Day"),
             (self.get_remembrance_day(year), "Remembrance Day"),
-        ]
+        ])
 
         shifts = self.shift_christmas_boxing_days(year=year)
         days.extend(shifts)
 
         if year == 2017:
-            days += [(date(2017, 5, 24), "Election day")]
+            days.append((date(2017, 5, 24), "Election day"))
 
         return days
 
     def get_national_heroes_day(self, year):
-        """Fourth Monday in January"""
+        """National Heroes day: Fourth MON in January"""
         return CaymanIslands.get_nth_weekday_in_month(year, 1, MON, 4)
 
     def get_discovery_day(self, year):
-        """Third Monday in May"""
+        """Discovery Day: Third MON in May"""
         return CaymanIslands.get_nth_weekday_in_month(year, 5, MON, 3)
 
     def get_queens_birthday(self, year):
-        """On monday after second saturday in June"""
-        sat = CaymanIslands.get_nth_weekday_in_month(year, 6, SAT, 2)
-        holiday = sat + timedelta(days=2)
+        """
+        Queen's Birthday: On MON after second SAT in June, with exceptions
+        """
+        saturday = CaymanIslands.get_nth_weekday_in_month(year, 6, SAT, 2)
+        holiday = saturday + timedelta(days=2)
         return QUEENS_BIRTHDAY_EXCEPTIONS.get(year, holiday)
 
     def get_constitution_day(self, year):
-        return CaymanIslands.get_nth_weekday_in_month(year, 7, MON, 1)
+        """
+        Constitution Day: First MON of July.
+        """
+        return CaymanIslands.get_nth_weekday_in_month(year, 7, MON)
 
     def get_remembrance_day(self, year):
+        """
+        Remembrance Day: Second MON of November.
+        """
         return CaymanIslands.get_nth_weekday_in_month(year, 11, MON, 2)
diff --git a/calendra/europe/croatia.py b/calendra/europe/croatia.py
index 1abf4ae5..bb5ce796 100644
--- a/calendra/europe/croatia.py
+++ b/calendra/europe/croatia.py
@@ -1,19 +1,23 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from datetime import date
+
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('HR')
-class Croatia(WesternCalendar, ChristianMixin):
+class Croatia(WesternCalendar):
     'Croatia'
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "International Workers' Day"),
         (6, 22, "Anti-Fascist Struggle Day"),
-        (6, 25, "Statehood Day"),
         (8, 5, "Victory & Homeland Thanksgiving & Day of Croatian defenders"),
-        (10, 8, "Independence Day"),
     )
 
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "International Workers' Day"
+
+    # Christian holidays
     include_epiphany = True
     include_easter_sunday = True
     include_easter_monday = True
@@ -23,3 +27,22 @@ class Croatia(WesternCalendar, ChristianMixin):
     include_christmas = True
     include_boxing_day = True
     boxing_day_label = "St. Stephen's Day"
+
+    def get_fixed_holidays(self, year):
+        days = super().get_fixed_holidays(year)
+
+        if year >= 2020:
+            days.extend([
+                # Statehood date has changed in 2020
+                (date(year, 5, 30), "Statehood Day"),
+                # Remembrance Day has been added in 2020
+                (date(year, 11, 18), "Remembrance Day"),
+            ])
+        else:
+            days.extend([
+                (date(year, 6, 25), "Statehood Day"),
+                # Independance day was a holiday until 2020
+                (date(year, 10, 8), "Independence Day"),
+            ])
+
+        return days
diff --git a/calendra/europe/cyprus.py b/calendra/europe/cyprus.py
index fd7da567..d6e65c7b 100644
--- a/calendra/europe/cyprus.py
+++ b/calendra/europe/cyprus.py
@@ -1,12 +1,16 @@
 from datetime import timedelta
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('CY')
-class Cyprus(WesternCalendar, ChristianMixin):
+class Cyprus(WesternCalendar):
     'Cyprus'
 
+    # Civil holidays
+    include_labour_day = True
+
+    # Christian holidays
     include_epiphany = True
     include_clean_monday = True
     include_good_friday = True
@@ -22,7 +26,6 @@ class Cyprus(WesternCalendar, ChristianMixin):
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (3, 25, "Greek Independence Day"),
         (4, 1, "Cyprus National Day"),
-        (5, 1, "Labour Day"),
         (7, 15, "Dormition of the Theotokos"),
         (10, 1, "Cyprus Independence Day"),
         (10, 28, "Greek National Day"),
diff --git a/calendra/europe/czech_republic.py b/calendra/europe/czech_republic.py
index 0f3262e3..85f383f4 100644
--- a/calendra/europe/czech_republic.py
+++ b/calendra/europe/czech_republic.py
@@ -1,17 +1,20 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('CZ')
-class CzechRepublic(WesternCalendar, ChristianMixin):
+class CzechRepublic(WesternCalendar):
     'Czech Republic'
 
+    # Civil holidays
+    include_labour_day = True
+
+    # Christian holidays
     include_easter_monday = True
     include_good_friday = True
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (1, 1, "Restoration Day of the Independent Czech State"),
-        (5, 1, "Labour Day"),
         (5, 8, "Liberation Day"),
         (7, 5, "Saints Cyril and Methodius Day"),
         (7, 6, "Jan Hus Day"),
diff --git a/calendra/europe/denmark.py b/calendra/europe/denmark.py
index 4f4e1fec..47eb14e3 100644
--- a/calendra/europe/denmark.py
+++ b/calendra/europe/denmark.py
@@ -1,10 +1,10 @@
 from datetime import timedelta
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('DK')
-class Denmark(WesternCalendar, ChristianMixin):
+class Denmark(WesternCalendar):
     'Denmark'
 
     include_holy_thursday = True
diff --git a/calendra/europe/estonia.py b/calendra/europe/estonia.py
index 34c2e59f..ff82203e 100644
--- a/calendra/europe/estonia.py
+++ b/calendra/europe/estonia.py
@@ -1,9 +1,9 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('EE')
-class Estonia(WesternCalendar, ChristianMixin):
+class Estonia(WesternCalendar):
     'Estonia'
 
     include_good_friday = True
diff --git a/calendra/europe/european_central_bank.py b/calendra/europe/european_central_bank.py
index 555e6f90..59603a41 100644
--- a/calendra/europe/european_central_bank.py
+++ b/calendra/europe/european_central_bank.py
@@ -1,12 +1,14 @@
-from ..core import WesternCalendar, ChristianMixin
+from ..core import WesternCalendar
 
 
-class EuropeanCentralBank(WesternCalendar, ChristianMixin):
+class EuropeanCentralBank(WesternCalendar):
     "European Central Bank"
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (12, 26, "St. Stephen's Day"),
     )
 
+    # Christian holidays
     include_good_friday = True
     include_easter_monday = True
diff --git a/calendra/europe/finland.py b/calendra/europe/finland.py
index 2855db58..1a868758 100644
--- a/calendra/europe/finland.py
+++ b/calendra/europe/finland.py
@@ -1,12 +1,13 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin, FRI, SAT
-from ..registry import iso_register
+from ..core import WesternCalendar, FRI, SAT
+from ..registry_tools import iso_register
 
 
 @iso_register('FI')
-class Finland(WesternCalendar, ChristianMixin):
+class Finland(WesternCalendar):
     'Finland'
 
+    # Christian holidays
     include_epiphany = True
     include_good_friday = True
     include_easter_sunday = True
@@ -19,8 +20,9 @@ class Finland(WesternCalendar, ChristianMixin):
     boxing_day_label = "St. Stephen's Day"
     observance_shift = None
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (12, 6, "Independence Day"),
     )
 
diff --git a/calendra/europe/france.py b/calendra/europe/france.py
index 905a5e78..b596d670 100644
--- a/calendra/europe/france.py
+++ b/calendra/europe/france.py
@@ -1,19 +1,21 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('FR')
-class France(WesternCalendar, ChristianMixin):
+class France(WesternCalendar):
     'France'
 
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
     include_all_saints = True
     include_assumption = True
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (5, 8, "Victory in Europe Day"),
         (7, 14, "Bastille Day"),
         (11, 11, "Armistice Day"),
diff --git a/calendra/europe/germany.py b/calendra/europe/germany.py
index bb3b4ec5..9c0207af 100644
--- a/calendra/europe/germany.py
+++ b/calendra/europe/germany.py
@@ -1,17 +1,19 @@
 from datetime import date, timedelta
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('DE')
-class Germany(WesternCalendar, ChristianMixin):
+class Germany(WesternCalendar):
     'Germany'
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (10, 3, "Day of German Unity"),
     )
 
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
diff --git a/calendra/europe/greece.py b/calendra/europe/greece.py
index 1347c5fb..03c79d76 100644
--- a/calendra/europe/greece.py
+++ b/calendra/europe/greece.py
@@ -1,16 +1,19 @@
-from ..core import WesternCalendar, OrthodoxMixin
-from ..registry import iso_register
+from ..core import OrthodoxCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('GR')
-class Greece(OrthodoxMixin, WesternCalendar):
+class Greece(OrthodoxCalendar):
     'Greece'
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    # Civil holidays
+    include_labour_day = True
+    FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + (
         (3, 25, "Independence Day"),
-        (5, 1, "Labour Day"),
         (10, 28, "Ohi Day"),
     )
+
+    # Christian holidays
     include_epiphany = True
     include_clean_monday = True
     include_annunciation = True
@@ -23,3 +26,5 @@ class Greece(OrthodoxMixin, WesternCalendar):
     include_assumption = True
     include_boxing_day = True
     boxing_day_label = "Glorifying Mother of God"
+    # No Orthodox Christmas
+    include_orthodox_christmas = False
diff --git a/calendra/europe/hungary.py b/calendra/europe/hungary.py
index 068c683e..a1a9c4ff 100644
--- a/calendra/europe/hungary.py
+++ b/calendra/europe/hungary.py
@@ -1,11 +1,12 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('HU')
-class Hungary(WesternCalendar, ChristianMixin):
+class Hungary(WesternCalendar):
     'Hungary'
 
+    # Christian holidays
     include_easter_sunday = True
     include_easter_monday = True
     include_whit_sunday = True
@@ -16,9 +17,10 @@ class Hungary(WesternCalendar, ChristianMixin):
     boxing_day_label = "Second Day of Christmas"
     include_all_saints = True
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (3, 15, "National Day"),
-        (5, 1, "Labour Day"),
         (8, 20, "St Stephen's Day"),
         (10, 23, "National Day"),
     )
diff --git a/calendra/europe/iceland.py b/calendra/europe/iceland.py
index fec9e83b..8c331e2f 100644
--- a/calendra/europe/iceland.py
+++ b/calendra/europe/iceland.py
@@ -1,12 +1,13 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin, THU, MON
-from ..registry import iso_register
+from ..core import WesternCalendar, THU, MON
+from ..registry_tools import iso_register
 
 
 @iso_register('IS')
-class Iceland(WesternCalendar, ChristianMixin):
+class Iceland(WesternCalendar):
     'Iceland'
 
+    # Christian holidays
     include_holy_thursday = True
     include_good_friday = True
     include_easter_monday = True
@@ -16,8 +17,9 @@ class Iceland(WesternCalendar, ChristianMixin):
     include_boxing_day = True
     boxing_day_label = "St Stephen's Day"
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (6, 17, "Icelandic National Day"),
         (12, 31, "New Year's Eve"),
     )
diff --git a/calendra/europe/ireland.py b/calendra/europe/ireland.py
index cf7862ca..e040229f 100644
--- a/calendra/europe/ireland.py
+++ b/calendra/europe/ireland.py
@@ -1,10 +1,10 @@
 from datetime import date, timedelta
-from ..core import WesternCalendar, ChristianMixin, MON
-from ..registry import iso_register
+from ..core import WesternCalendar, MON
+from ..registry_tools import iso_register
 
 
 @iso_register('IE')
-class Ireland(WesternCalendar, ChristianMixin):
+class Ireland(WesternCalendar):
     'Ireland'
 
     include_easter_monday = True
diff --git a/calendra/europe/italy.py b/calendra/europe/italy.py
index a77e3f99..4bc95ffc 100644
--- a/calendra/europe/italy.py
+++ b/calendra/europe/italy.py
@@ -1,16 +1,20 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('IT')
-class Italy(WesternCalendar, ChristianMixin):
+class Italy(WesternCalendar):
     'Italy'
 
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "International Workers' Day"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (4, 25, "Liberation Day"),
-        (5, 1, "International Workers' Day"),
         (6, 2, "Republic Day"),
     )
+
+    # Christian holidays
     include_immaculate_conception = True
     include_epiphany = True
     include_easter_monday = True
diff --git a/calendra/europe/latvia.py b/calendra/europe/latvia.py
index e87eb06a..dec4567d 100644
--- a/calendra/europe/latvia.py
+++ b/calendra/europe/latvia.py
@@ -1,20 +1,22 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('LV')
-class Latvia(WesternCalendar, ChristianMixin):
+class Latvia(WesternCalendar):
     'Latvia'
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (6, 23, "Midsummer Day"),
         (6, 24, "St. John's Day"),
         (11, 18, "Proclamation Day"),
         (12, 31, "New Years Eve"),
     )
 
+    # Christian holidays
     include_good_friday = True
     include_easter_sunday = True
     include_easter_monday = True
diff --git a/calendra/europe/lithuania.py b/calendra/europe/lithuania.py
index 525dcd47..8931e94b 100644
--- a/calendra/europe/lithuania.py
+++ b/calendra/europe/lithuania.py
@@ -1,23 +1,25 @@
-from ..core import WesternCalendar, ChristianMixin, SUN
-from ..registry import iso_register
+from ..core import WesternCalendar, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('LT')
-class Lithuania(WesternCalendar, ChristianMixin):
+class Lithuania(WesternCalendar):
     'Lithuania'
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (2, 16, "Restoration of the State Day"),
         (3, 11, "Restoration of Independence Day"),
-        (5, 1, "Labour Day"),
         (6, 24, "St. John's Day"),
         (7, 6, "Anniversary of the Coronation of King Mindaugas"),
-        (8, 15, "Assumption Day"),
-        (11, 1, "All Saints' Day"),
     )
 
+    # Christian holidays
     include_easter_sunday = True
     include_easter_monday = True
+    include_assumption = True
+    include_all_saints = True
     include_christmas_eve = True
     include_christmas = True
     include_boxing_day = True
@@ -36,6 +38,9 @@ def get_fathers_day(self, year):
         )
 
     def get_variable_days(self, year):
+        # All Souls day was introduced as of 2020
+        # https://en.wikipedia.org/wiki/Public_holidays_in_Lithuania
+        self.include_all_souls = year >= 2020
         days = super().get_variable_days(year)
         days.append(self.get_mothers_day(year))
         days.append(self.get_fathers_day(year))
diff --git a/calendra/europe/luxembourg.py b/calendra/europe/luxembourg.py
index 1583e525..11209d06 100644
--- a/calendra/europe/luxembourg.py
+++ b/calendra/europe/luxembourg.py
@@ -1,12 +1,13 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('LU')
-class Luxembourg(WesternCalendar, ChristianMixin):
+class Luxembourg(WesternCalendar):
     'Luxembourg'
 
+    # Christian holidays
     include_easter_monday = True
     include_ascension = True
     include_whit_monday = True
@@ -14,8 +15,9 @@ class Luxembourg(WesternCalendar, ChristianMixin):
     include_assumption = True
     include_boxing_day = True
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (6, 23, "Luxembourg National Holiday"),
     )
 
diff --git a/calendra/europe/malta.py b/calendra/europe/malta.py
index e9c0a4a9..5a2b3ad6 100644
--- a/calendra/europe/malta.py
+++ b/calendra/europe/malta.py
@@ -1,16 +1,20 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('MT')
-class Malta(WesternCalendar, ChristianMixin):
+class Malta(WesternCalendar):
     'Malta'
 
+    # Christian holidays
     include_good_friday = True
     include_assumption = True
     include_immaculate_conception = True
     include_christmas = True
 
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Worker's Day"  # (Jum il-Ħaddiem)
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         # National Holidays
         (3, 31, "Freedom Day"),            # (Jum il-Ħelsien)
@@ -19,9 +23,7 @@ class Malta(WesternCalendar, ChristianMixin):
         (9, 21, "Independence Day"),       # (Jum l-Indipendenza)
         (12, 13, "Republic Day"),          # (Jum ir-Repubblika)
         # Public Holidays
-        (1, 1, "New Year's Day"),          # (L-Ewwel tas-Sena)
         (2, 10, "Feast of Saint Paul's Shipwreck"),
         (3, 19, "Feast of Saint Joseph"),  # (San Ġużepp)
-        (5, 1, "Worker's Day"),            # (Jum il-Ħaddiem)
         (6, 29, "Feast of Saint Peter & Saint Paul"),  # (L-Imnarja)
     )
diff --git a/calendra/europe/monaco.py b/calendra/europe/monaco.py
new file mode 100644
index 00000000..916897ff
--- /dev/null
+++ b/calendra/europe/monaco.py
@@ -0,0 +1,23 @@
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
+
+
+@iso_register('MC')
+class Monaco(WesternCalendar):
+    'Monaco'
+
+    # Christian holidays
+    include_easter_monday = True
+    include_ascension = True
+    include_whit_monday = True
+    include_all_saints = True
+    include_assumption = True
+    include_corpus_christi = True
+    include_immaculate_conception = True
+
+    # Civil holidays
+    include_labour_day = True
+    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+        (1, 27, "Saint Dévote's Day"),
+        (11, 19, "H.S.H. the Sovereign Prince's Day"),
+    )
diff --git a/calendra/europe/netherlands.py b/calendra/europe/netherlands.py
index bbbec194..ebd9ca2c 100644
--- a/calendra/europe/netherlands.py
+++ b/calendra/europe/netherlands.py
@@ -1,10 +1,10 @@
-from datetime import date
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from datetime import date, timedelta
+from ..core import WesternCalendar, SUN, SeriesShiftMixin
+from ..registry_tools import iso_register
 
 
 @iso_register('NL')
-class Netherlands(WesternCalendar, ChristianMixin):
+class Netherlands(SeriesShiftMixin, WesternCalendar):
     'Netherlands'
 
     include_good_friday = True
@@ -14,11 +14,16 @@ class Netherlands(WesternCalendar, ChristianMixin):
     include_whit_sunday = True
     include_whit_monday = True
     include_boxing_day = True
+    series_requiring_shifts = ["Carnival"]
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (5, 5, "Liberation Day"),
     )
 
+    def __init__(self, include_carnival=False):
+        self.include_carnival = include_carnival
+        super().__init__()
+
     def get_king_queen_day(self, year):
         """27 April unless this is a Sunday in which case it is the 26th
 
@@ -26,17 +31,318 @@ def get_king_queen_day(self, year):
         30 April, unless this is a Sunday in which case it is the 29th.
         """
         if year > 2013:
-            if date(year, 4, 27).weekday() != 6:
-                return date(year, 4, 27), "King's day"
-            else:
-                return date(year, 4, 26), "King's day"
+            king_day = date(year, 4, 27)
+            if king_day.weekday() != SUN:
+                return (king_day, "King's day")
+            return (king_day - timedelta(days=1), "King's day")
         else:
-            if date(year, 4, 30).weekday() != 6:
-                return date(year, 4, 30), "Queen's day"
-            else:
-                return date(year, 4, 29), "Queen's day"
+            queen_day = date(year, 4, 30)
+            if queen_day.weekday() != SUN:
+                return (queen_day, "Queen's day")
+            return (queen_day - timedelta(days=1), "Queen's day")
+
+    def get_carnival_days(self, year):
+        """Carnival starts 7 weeks before Easter Sunday and lasts 3 days."""
+        n_days = 3
+        start = self.get_easter_sunday(year) - timedelta(weeks=7)
+
+        return [
+            (
+                start + timedelta(days=i), "Carnival"
+            ) for i in range(n_days)
+        ]
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
         days.append(self.get_king_queen_day(year))
+        if self.include_carnival:
+            days.extend(self.get_carnival_days(year))
+        return days
+
+
+FALL_HOLIDAYS_EARLY_REGIONS = {
+    2025: ["south"],
+    2024: ["south"],
+    2023: ["middle", "south"],
+    2022: ["north"],
+    2021: ["north", "middle"],
+    2020: ["north"],
+    2019: ["south"],
+    2018: ["south"],
+    2017: ["middle", "south"],
+    2016: ["north", "middle"],
+}
+
+SPRING_HOLIDAYS_EARLY_REGIONS = {
+    2025: ["north"],
+    2024: ["south"],
+    2023: ["south"],
+    2022: ["north"],
+    2021: ["south"],
+    2020: ["north"],
+    2019: ["north"],
+    2018: ["south"],
+    2017: ["north"],
+    2016: ["middle", "south"],
+}
+
+SUMMER_HOLIDAYS_EARLY_REGIONS = {
+    2025: ["south"],
+    2024: ["south"],
+    2023: ["middle"],
+    2022: ["middle"],
+    2021: ["north"],
+    2020: ["north"],
+    2019: ["south"],
+    2018: ["south"],
+    2017: ["middle"],
+    2016: ["middle"],
+}
+
+SUMMER_HOLIDAYS_LATE_REGIONS = {
+    2025: ["middle"],
+    2024: ["north"],
+    2023: ["north"],
+    2022: ["south"],
+    2021: ["south"],
+    2020: ["middle"],
+    2019: ["middle"],
+    2018: ["north"],
+    2017: ["north"],
+    2016: ["south"],
+}
+
+
+class NetherlandsWithSchoolHolidays(Netherlands):
+    """Netherlands with school holidays (2016 to 2025).
+
+    Data source and regulating body:
+    https://www.rijksoverheid.nl/onderwerpen/schoolvakanties/overzicht-schoolvakanties-per-schooljaar
+    """
+    series_requiring_shifts = [
+        "Carnival", "Carnival holiday",
+        "Fall holiday", "Christmas holiday", "Spring holiday", "May holiday",
+        "Summer holiday"
+    ]
+
+    def __init__(
+            self,
+            region,
+            carnival_instead_of_spring=False,
+            **kwargs,
+    ):
+        """ Set up a calendar incl. school holidays for a specific region
+
+        :param region: either "north", "middle" or "south"
+        """
+        if region not in ("north", "middle", "south"):
+            raise ValueError("Set region to 'north', 'middle' or 'south'.")
+        self.region = region
+        self.carnival_instead_of_spring = carnival_instead_of_spring
+        if carnival_instead_of_spring and "include_carnival" not in kwargs:
+            kwargs["include_carnival"] = True
+        super().__init__(**kwargs)
+
+    def get_fall_holidays(self, year):
+        n_days = 9
+        week = 43
+
+        # Exceptional years
+        if year == 2024:
+            week = 44
+
+        # Holiday starts on the preceding Saturday
+        try:
+            start = date.fromisocalendar(year, week - 1, 6)
+        except AttributeError:
+            # Adapted from https://stackoverflow.com/a/59200842
+            first = date(year, 1, 1)
+            base = 1 if first.isocalendar()[1] == 1 else 8
+            start = first + timedelta(
+                days=base - first.isocalendar()[2] + 7 * (week - 1) - 2
+            )
+
+        # Some regions have their fall holiday 1 week earlier
+        try:
+            if self.region in FALL_HOLIDAYS_EARLY_REGIONS[year]:
+                start = start - timedelta(weeks=1)
+        except KeyError:
+            raise NotImplementedError("Unknown fall holidays for %d." % year)
+
+        return [
+            (start + timedelta(days=i), "Fall holiday") for i in range(n_days)
+        ]
+
+    def get_christmas_holidays(
+            self, year, in_december=True, in_january=True
+    ):
+        """ Christmas holidays run partially in December and partially in January
+        (spillover from previous year).
+        """
+
+        if in_december:
+
+            # 27 December is always in a full week of holidays
+            week = date(year, 12, 27).isocalendar()[1]
+
+            # Holiday starts on the preceding Saturday
+            try:
+                start = date.fromisocalendar(year, week - 1, 6)
+            except AttributeError:
+                # Adapted from https://stackoverflow.com/a/59200842
+                first = date(year, 1, 1)
+                base = 1 if first.isocalendar()[1] == 1 else 8
+                start = first + timedelta(
+                    days=base - first.isocalendar()[2] + 7 * (week - 1) - 2
+                )
+
+            dates = [
+                (
+                    start + timedelta(days=i), "Christmas holiday"
+                ) for i in range((date(year, 12, 31) - start).days + 1)
+            ]
+
+            if in_january:
+                dates.extend(
+                    self.get_christmas_holidays(year, in_december=False)
+                )
+            return dates
+
+        n_days = 16
+        # 27 December is always in a full week of holidays
+        week = date(year - 1, 12, 27).isocalendar()[1]
+
+        # Holiday ends 15 days after the preceding Saturday
+        try:
+            end = date.fromisocalendar(
+                year - 1, week - 1, 6
+            ) + timedelta(days=n_days - 1)
+        except AttributeError:
+            # Adapted from https://stackoverflow.com/a/59200842
+            first = date(year - 1, 1, 1)
+            base = 1 if first.isocalendar()[1] == 1 else 8
+            end = first + timedelta(
+                days=base - first.isocalendar()[2] + 7 * (week - 1)
+                + n_days - 3
+            )
+
+        return [
+            (
+                date(year, 1, 1) + timedelta(days=i), "Christmas holiday"
+            ) for i in range((end - date(year, 1, 1)).days + 1)
+        ]
+
+    def get_spring_holidays(self, year):
+        n_days = 9
+        week = 9
+
+        # Exceptional years
+        if year in [2024, 2021]:
+            week = 8
+
+        # Holiday starts on the preceding Saturday
+        try:
+            start = date.fromisocalendar(year, week - 1, 6)
+        except AttributeError:
+            # Adapted from https://stackoverflow.com/a/59200842
+            first = date(year, 1, 1)
+            base = 1 if first.isocalendar()[1] == 1 else 8
+            start = first + timedelta(
+                days=base - first.isocalendar()[2] + 7 * (week - 1) - 2
+            )
+
+        # Some regions have their spring holiday 1 week earlier
+        try:
+            if self.region in SPRING_HOLIDAYS_EARLY_REGIONS[year]:
+                start = start - timedelta(weeks=1)
+        except KeyError:
+            raise NotImplementedError("Unknown spring holidays for %d." % year)
+
+        return [
+            (
+                start + timedelta(days=i), "Spring holiday"
+            ) for i in range(n_days)
+        ]
+
+    def get_carnival_holidays(self, year):
+        """Carnival holiday starts 7 weeks and 1 day before Easter Sunday
+        and lasts 9 days.
+        """
+        n_days = 9
+        start = self.get_easter_sunday(year) - timedelta(weeks=7, days=1)
+
+        return [
+            (
+                start + timedelta(days=i), "Carnival holiday"
+            ) for i in range(n_days)
+        ]
+
+    def get_may_holidays(self, year):
+        n_days = 9
+        week = 18
+
+        # Exceptional years
+        if year == 2017:
+            week = 17
+
+        # Holiday starts on the preceding Saturday
+        try:
+            start = date.fromisocalendar(year, week - 1, 6)
+        except AttributeError:
+            # Adapted from https://stackoverflow.com/a/59200842
+            first = date(year, 1, 1)
+            base = 1 if first.isocalendar()[1] == 1 else 8
+            start = first + timedelta(
+                days=base - first.isocalendar()[2] + 7 * (week - 1) - 2
+            )
+
+        return [
+            (start + timedelta(days=i), "May holiday") for i in range(n_days)
+        ]
+
+    def get_summer_holidays(self, year):
+        n_days = 44
+        week = 29
+
+        # Holiday starts on the preceding Saturday
+        try:
+            start = date.fromisocalendar(year, week - 1, 6)
+        except AttributeError:
+            # Adapted from https://stackoverflow.com/a/59200842
+            first = date(year, 1, 1)
+            base = 1 if first.isocalendar()[1] == 1 else 8
+            start = first + timedelta(
+                days=base - first.isocalendar()[2] + 7 * (week - 1) - 2
+            )
+
+        # Some regions have their summer holiday 1 week earlier
+        try:
+            if self.region in SUMMER_HOLIDAYS_EARLY_REGIONS[year]:
+                start = start - timedelta(weeks=1)
+        except KeyError:
+            raise NotImplementedError("Unknown summer holidays for %d." % year)
+
+        # Some regions have their summer holiday 1 week later
+        try:
+            if self.region in SUMMER_HOLIDAYS_LATE_REGIONS[year]:
+                start = start + timedelta(weeks=1)
+        except KeyError:
+            raise NotImplementedError("Unknown summer holidays for %d." % year)
+
+        return [
+            (
+                start + timedelta(days=i), "Summer holiday"
+            ) for i in range(n_days)
+        ]
+
+    def get_variable_days(self, year):
+        days = super().get_variable_days(year)
+        days.extend(self.get_fall_holidays(year))
+        days.extend(self.get_christmas_holidays(year))
+        if self.carnival_instead_of_spring:
+            days.extend(self.get_carnival_holidays(year))
+        else:
+            days.extend(self.get_spring_holidays(year))
+        days.extend(self.get_may_holidays(year))
+        days.extend(self.get_summer_holidays(year))
         return days
diff --git a/calendra/europe/norway.py b/calendra/europe/norway.py
index 7d562ae3..dfe3319f 100644
--- a/calendra/europe/norway.py
+++ b/calendra/europe/norway.py
@@ -1,11 +1,12 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('NO')
-class Norway(WesternCalendar, ChristianMixin):
+class Norway(WesternCalendar):
     'Norway'
 
+    # Christian holidays
     include_holy_thursday = True
     include_good_friday = True
     include_easter_sunday = True
@@ -16,7 +17,8 @@ class Norway(WesternCalendar, ChristianMixin):
     include_boxing_day = True
     boxing_day_label = "St Stephen's Day"
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (5, 17, "Constitution Day"),
     )
diff --git a/calendra/europe/poland.py b/calendra/europe/poland.py
index 3e6560b9..76ee25ef 100644
--- a/calendra/europe/poland.py
+++ b/calendra/europe/poland.py
@@ -1,17 +1,20 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('PL')
-class Poland(WesternCalendar, ChristianMixin):
+class Poland(WesternCalendar):
     'Poland'
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (1, 6, 'Trzech Kroli'),
-        (5, 1, 'Labour Day'),
         (5, 3, 'Constitution Day'),
         (11, 11, 'Independence Day'),
     )
+
+    # Christian holidays
     include_easter_sunday = True
     include_easter_monday = True
     include_whit_sunday = True
diff --git a/calendra/europe/portugal.py b/calendra/europe/portugal.py
index fa1d4f06..d187d73e 100644
--- a/calendra/europe/portugal.py
+++ b/calendra/europe/portugal.py
@@ -1,22 +1,26 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('PT')
-class Portugal(WesternCalendar, ChristianMixin):
+class Portugal(WesternCalendar):
     'Portugal'
 
+    # Christian holidays
     include_good_friday = True
     include_easter_sunday = True
     include_christmas = True
+    include_immaculate_conception = True
+    immaculate_conception_label = "Imaculada Conceição"
 
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Dia do Trabalhador"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (4, 25, "Dia da Liberdade"),
-        (5, 1, "Dia do Trabalhador"),
         (6, 10, "Dia de Portugal"),
         (8, 15, "Assunção de Nossa Senhora"),
-        (12, 8, "Imaculada Conceição"),
     )
 
     def get_fixed_holidays(self, year):
diff --git a/calendra/europe/romania.py b/calendra/europe/romania.py
index c04ff7f1..5ecc9ba1 100644
--- a/calendra/europe/romania.py
+++ b/calendra/europe/romania.py
@@ -1,21 +1,23 @@
 from datetime import date
-from ..core import WesternCalendar, OrthodoxMixin
-from ..registry import iso_register
+from ..core import OrthodoxCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('RO')
-class Romania(OrthodoxMixin, WesternCalendar):
+class Romania(OrthodoxCalendar):
     'Romania'
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (1, 2, "Day After New Years"),
+    # Civil holidays
+    include_labour_day = True
+    FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + (
+        (1, 2, "Day After New Year"),
         (1, 24, "Union Day"),
-        (5, 1, "Labour Day"),
         (8, 15, "Dormition of the Theotokos"),
         (11, 30, "St. Andrew's Day"),
         (12, 1, "National Day/Great Union"),
     )
 
+    # Christian holidays
     include_good_friday = True
     include_easter_sunday = True
     include_easter_monday = True
@@ -26,6 +28,8 @@ class Romania(OrthodoxMixin, WesternCalendar):
     include_christmas = True
     include_boxing_day = True
     boxing_day_label = 'Christmas Day'
+    # No Orthodox Christmas
+    include_orthodox_christmas = False
 
     def get_childrens_day(self, year):
         """returns a possibly empty list of (date, holiday_name) tuples"""
@@ -39,7 +43,7 @@ def get_childrens_day(self, year):
     def get_liberation_day(self, year):
         """returns a possibly empty list of (date, holiday_name) tuples"""
         days = []
-        if year >= 1949 and year <= 1990:
+        if 1949 <= year <= 1990:
             actual_date = date(year, 8, 23)
             days = [(actual_date, "Liberation from Fascist Occupation Day")]
 
diff --git a/calendra/europe/russia.py b/calendra/europe/russia.py
index cdfbacec..fd217433 100644
--- a/calendra/europe/russia.py
+++ b/calendra/europe/russia.py
@@ -1,20 +1,104 @@
-from ..core import WesternCalendar
-from ..registry import iso_register
+from datetime import date
+
+from ..core import OrthodoxCalendar, MON, daterange, cleaned_date
+from ..registry_tools import iso_register
 
 
 @iso_register('RU')
-class Russia(WesternCalendar):
+class Russia(OrthodoxCalendar):
     'Russia'
+    # Civil holidays
+    include_labour_day = True
 
-    shift_new_years_day = True
-
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + (
         (1, 2, "Day After New Year"),
-        (1, 7, "Christmas"),
         (2, 23, "Defendence of the Fatherland"),
         (3, 8, "International Women's Day"),
-        (5, 1, "Labour Day"),
         (5, 9, "Victory Day"),
         (6, 12, "National Day"),
         (11, 4, "Day of Unity"),
     )
+
+    # Christian holidays
+    include_christmas = False
+
+    covid19_2020_start = date(2020, 3, 28)
+    covid19_2020_end = date(2020, 4, 30)
+
+    def get_fixed_holidays(self, year):
+
+        if year >= 1992:
+            self.labour_day_label = "The Day of Spring and Labour"
+        else:
+            self.labour_day_label = "International Workers' Day"
+
+        days = super().get_fixed_holidays(year)
+
+        if year >= 2005:
+            days.extend([
+                (date(year, 1, 3), "Third Day after New Year"),
+                (date(year, 1, 4), "Fourth Day after New Year"),
+                (date(year, 1, 5), "Fifth Day after New Year"),
+                (date(year, 1, 6), "Sixth Day after New Year"),
+                (date(year, 1, 8), "Eighth Day after New Year"),
+            ])
+
+        if year == 2020:
+            index = 1
+            for day in daterange(self.covid19_2020_start,
+                                 self.covid19_2020_end):
+                days.append(
+                    (day, f"Non-Working Day (COVID-19) #{index}")
+                )
+                index += 1
+
+            # Adding May extra days
+            days.extend([
+                (date(year, 5, 4), "May 4th, 2020 holiday"),
+                (date(year, 5, 5), "Day of Spring and Labor"),
+            ])
+
+            # Extra COVID-19 in May
+            days.extend([
+                (date(year, 5, 6), "Covid-19 May #1"),
+                (date(year, 5, 7), "Covid-19 May #2"),
+                (date(year, 5, 8), "Covid-19 May #3"),
+            ])
+
+            # Constitution Vote Public Holiday
+            days.append((date(year, 7, 1), "Constitution Vote Public Holiday"))
+        elif year == 2021:
+            days.extend([
+                (date(year, 2, 22), "Day Before Defendence of the Fatherland"),
+                (date(year, 11, 5), 'Day After Day of Unity'),
+                (date(year, 12, 31), "New Year's Eve"),
+            ])
+        return days
+
+    def get_calendar_holidays(self, year):
+        holidays = super().get_calendar_holidays(year)
+        shifts = []
+        for day, label in holidays:
+            if day.month == 1 and day.day in range(1, 9):
+                continue
+            # Add an exception for 2020 non-working days due to COVID-19
+            if self.covid19_2020_start <= day <= self.covid19_2020_end:
+                continue
+            if day.weekday() in self.get_weekend_days():
+                shifts.append((
+                    self.get_first_weekday_after(day, MON),
+                    label + " shift"
+                ))
+        holidays.extend(shifts)
+        return holidays
+
+    def is_working_day(self, day,
+                       extra_working_days=None, extra_holidays=None):
+        day = cleaned_date(day)
+        if day == date(2021, 2, 20):
+            return True
+        return super().is_working_day(
+            day,
+            extra_working_days=extra_working_days,
+            extra_holidays=extra_holidays
+        )
diff --git a/calendra/europe/scotland/__init__.py b/calendra/europe/scotland/__init__.py
index 03a7d9dc..a7a83886 100644
--- a/calendra/europe/scotland/__init__.py
+++ b/calendra/europe/scotland/__init__.py
@@ -24,7 +24,7 @@
 # necessary to split this module and associated tests
 from datetime import date, timedelta
 import warnings
-from ...core import WesternCalendar, ChristianMixin, MON, THU, FRI
+from ...core import WesternCalendar, MON, THU, FRI
 from .mixins import (
     SpringHolidayFirstMondayApril,
     SpringHolidaySecondMondayApril,
@@ -51,7 +51,7 @@
 )
 
 
-class Scotland(WesternCalendar, ChristianMixin):
+class Scotland(WesternCalendar):
     "Scotland"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (1, 2, "New Year Holiday"),
diff --git a/calendra/europe/serbia.py b/calendra/europe/serbia.py
index 98c78d52..ae2bd3a0 100644
--- a/calendra/europe/serbia.py
+++ b/calendra/europe/serbia.py
@@ -1,22 +1,25 @@
-from ..core import WesternCalendar, OrthodoxMixin
-from ..registry import iso_register
+from ..core import OrthodoxCalendar, SeriesShiftMixin
+from ..registry_tools import iso_register
 
 
 @iso_register('RS')
-class Serbia(OrthodoxMixin, WesternCalendar):
+class Serbia(SeriesShiftMixin, OrthodoxCalendar):
     'Serbia'
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + (
         (1, 2, "Day After New Year"),
-        (1, 7, "Christmas"),
         (2, 15, "Statehood Day"),
         (2, 16, "Statehood Day"),
-        (5, 1, "Labour Day"),
         (5, 2, "Labour Day Holiday"),
         (11, 11, "Armistice Day"),
     )
 
+    # Civil holidays
+    include_labour_day = True
+
+    # Christian holidays
     include_good_friday = True
     include_easter_sunday = True
     include_easter_monday = True
     include_christmas = False
+    series_requiring_shifts = ["Statehood Day"]
diff --git a/calendra/europe/slovakia.py b/calendra/europe/slovakia.py
index 7aa1bf36..84f3d9da 100644
--- a/calendra/europe/slovakia.py
+++ b/calendra/europe/slovakia.py
@@ -1,11 +1,12 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('SK')
-class Slovakia(WesternCalendar, ChristianMixin):
+class Slovakia(WesternCalendar):
     'Slovakia'
 
+    # Christian holidays
     include_epiphany = True
     include_easter_monday = True
     include_good_friday = True
@@ -14,9 +15,10 @@ class Slovakia(WesternCalendar, ChristianMixin):
     include_boxing_day = True
     boxing_day_label = "St. Stephen's Day (The Second Christmas Day)"
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (1, 1, "Day of the Establishment of the Slovak Republic"),
-        (5, 1, "Labour Day"),
         (5, 8, "Liberation Day"),
         (7, 5, "Saints Cyril and Methodius Day"),
         (8, 29, "Slovak National Uprising anniversary"),
diff --git a/calendra/europe/slovenia.py b/calendra/europe/slovenia.py
index e9999729..b7dac86b 100644
--- a/calendra/europe/slovenia.py
+++ b/calendra/europe/slovenia.py
@@ -1,23 +1,25 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('SI')
-class Slovenia(WesternCalendar, ChristianMixin):
+class Slovenia(WesternCalendar):
     'Slovenia'
 
+    # Christian holidays
     include_easter_sunday = True
     include_easter_monday = True
     include_whit_sunday = True
     include_assumption = True
     include_christmas = True
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (2, 8, "Preseren Day, the Slovenian Cultural Holiday"),
         (4, 27, "Day of Uprising Against Occupation"),
-        (5, 1, "Labour Day"),
-        (5, 2, "Labour Day"),
+        (5, 2, "Labour Day"),  # In Slovenia, Labour Day is 2-day long.
         (6, 25, "Statehood Day"),
         (10, 31, "Reformation Day"),
         (11, 1, "Day of Remembrance of the Dead"),
diff --git a/calendra/europe/spain.py b/calendra/europe/spain.py
index 840cbc63..d9d443c0 100644
--- a/calendra/europe/spain.py
+++ b/calendra/europe/spain.py
@@ -1,24 +1,79 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('ES')
-class Spain(WesternCalendar, ChristianMixin):
+class Spain(WesternCalendar):
     'Spain'
 
+    # Christian holidays
     include_epiphany = True
-    include_immaculate_conception = True
     include_good_friday = True
     include_assumption = True
     include_all_saints = True
+    include_immaculate_conception = True
 
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Día del trabajador"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Día del trabajador"),
         (10, 12, "Fiesta nacional de España"),
         (12, 6, "Día de la Constitución Española")
     )
 
 
+@iso_register('ES-AN')
+class Andalusia(Spain):
+    "Andalusia"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (2, 28, "Andalusian National Day"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-AR')
+class Aragon(Spain):
+    "Aragon"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (4, 23, "Aragonese National Day"),
+        (12, 20, "Aragon Ombudsman Day"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-CL')
+class CastileAndLeon(Spain):
+    "Castile and León"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (4, 23, "Día de Castilla y León"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-CM')
+class CastillaLaMancha(Spain):
+    "Castilla-La Mancha"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (5, 31, "Día de la Región Castilla-La Mancha"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-CN')
+class CanaryIslands(Spain):
+    "Canary Islands"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (5, 30, "Día de Canarias"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-CT')
 class Catalonia(Spain):
     "Catalonia"
 
@@ -27,6 +82,118 @@ class Catalonia(Spain):
     boxing_day_label = "Sant Esteve"
 
     FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
-        (6, 24, "Sant Joan"),
         (9, 11, "Diada nacional de Catalunya"),
     )
+
+
+@iso_register('ES-EX')
+class Extremadura(Spain):
+    "Extremadura"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (9, 8, "Día de Extremadura"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-GA')
+class Galicia(Spain):
+    "Galicia"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (5, 17, "Día das Letras Galegas"),
+        (7, 25, "Santiago Apóstol o Día da Patria Galega"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-IB')
+class BalearicIslands(Spain):
+    "Balearic Islands"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (3, 1, "Dia de les Illes Balears"),
+    )
+    # Christian holidays
+    include_holy_thursday = True  # Also called Maundy thursday
+    include_easter_monday = True
+
+
+@iso_register('ES-RI')
+class LaRioja(Spain):
+    "La Rioja"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (6, 9, "Dia de La Rioja"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-MD')
+class CommunityofMadrid(Spain):
+    "Community of Madrid"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (5, 2, "Fiesta de la Comunidad de Madrid"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-MC')
+class Murcia(Spain):
+    "Region of Murcia"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (6, 9, "Día de la Región de Murcia"),
+        (3, 19, "San José"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-NA')
+class Navarre(Spain):
+    "Navarre"
+    # Christian holidays
+    include_holy_thursday = True  # Also called Maundy thursday
+    include_easter_monday = True
+
+
+@iso_register('ES-AS')
+class Asturias(Spain):
+    "Asturias"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (9, 8, "Día de Asturias"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-PV')
+class BasqueCountry(Spain):
+    "Basque Country"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (10, 25, "Euskadi Eguna"),
+    )
+    # Christian holidays
+    include_holy_thursday = True  # Also called Maundy thursday
+    include_easter_monday = True
+
+
+@iso_register('ES-CB')
+class Cantabria(Spain):
+    "Cantabria"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (9, 15, "Día de Cantabria o Día de La Montaña"),
+    )
+    # Christian holiday
+    include_holy_thursday = True  # Also called Maundy thursday
+
+
+@iso_register('ES-VC')
+class ValencianCommunity(Spain):
+    "Valencian Community"
+    FIXED_HOLIDAYS = Spain.FIXED_HOLIDAYS + (
+        (3, 19, "San José"),
+        (10, 9, "Dia de la Comunitat Valenciana"),
+    )
+    # Christian holiday
+    include_easter_monday = True
diff --git a/calendra/europe/sweden.py b/calendra/europe/sweden.py
index c9bc612b..4b074409 100644
--- a/calendra/europe/sweden.py
+++ b/calendra/europe/sweden.py
@@ -1,12 +1,13 @@
 from datetime import date
-from ..core import WesternCalendar, ChristianMixin, FRI, SAT
-from ..registry import iso_register
+from ..core import WesternCalendar, FRI, SAT
+from ..registry_tools import iso_register
 
 
 @iso_register('SE')
-class Sweden(WesternCalendar, ChristianMixin):
+class Sweden(WesternCalendar):
     'Sweden'
 
+    # Christian holidays
     include_epiphany = True
     include_good_friday = True
     include_easter_sunday = True
@@ -20,8 +21,9 @@ class Sweden(WesternCalendar, ChristianMixin):
     boxing_day_label = "Second Day of Christmas"
     observance_shift = None
 
+    # Civil holidays
+    include_labour_day = True
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (5, 1, "Labour Day"),
         (6, 6, "National Day"),
         # New Year's Eve is not a holiday but not a work day either
         (12, 31, "New Year's Eve")
diff --git a/calendra/europe/switzerland.py b/calendra/europe/switzerland.py
index 6fe3a650..4d970072 100644
--- a/calendra/europe/switzerland.py
+++ b/calendra/europe/switzerland.py
@@ -1,65 +1,113 @@
 from datetime import date, timedelta
-from ..core import WesternCalendar, ChristianMixin, SUN
-from ..registry import iso_register
+from ..core import WesternCalendar, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('CH')
-class Switzerland(WesternCalendar, ChristianMixin):
+class Switzerland(WesternCalendar):
     'Switzerland'
 
-    include_good_friday = True
+    # ChristianMixin entries common to (most) cantons - opt out
+    include_good_friday = True  # not in TI, VS
     include_easter_sunday = True
-    include_easter_monday = True
+    include_easter_monday = True  # not in VS
     include_ascension = True
     include_whit_sunday = True
-    include_whit_monday = True
+    include_whit_monday = True  # not in VS
     include_christmas = True
-    include_boxing_day = True
-
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (1, 2, "Berchtold's Day"),
-        (5, 1, "Labour Day"),
-        (8, 1, "National Holiday"),
-    )
+    include_boxing_day = True  # not in GE, NE, VS, VD
 
+    # ChristianMixin entries with varying observance - opt in
+    include_epiphany = False
+    include_corpus_christi = False
+    include_assumption = False
+    include_all_saints = False
+    include_immaculate_conception = False
 
-@iso_register('CH-VD')
-class Vaud(Switzerland):
-    'Vaud'
-
-    include_boxing_day = False
-    include_federal_thanksgiving_monday = True
+    # Swiss entries with varying observance - opt in
+    include_berchtolds_day = False
+    include_st_josephs_day = False
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (1, 2, "Berchtold's Day"),
         (8, 1, "National Holiday"),
     )
 
-    def get_federal_thanksgiving_monday(self, year):
-        "Monday following the 3rd sunday of September"
-        september_1st = date(year, 9, 1)
-        return (
-            september_1st +
-            (6 - september_1st.weekday()) * timedelta(days=1) +  # 1st sunday
-            timedelta(days=15)  # Monday following 3rd sunday
-        )
-
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
-        if self.include_federal_thanksgiving_monday:
-            days.append((self.get_federal_thanksgiving_monday(year),
-                         "Federal Thanksgiving Monday"))
+        if self.include_berchtolds_day:
+            days.append((date(year, 1, 2), "Berchtold's Day"))
+        if self.include_st_josephs_day:
+            days.append((date(year, 3, 19), "St Joseph's Day"))
         return days
 
 
+@iso_register('CH-AG')
+class Aargau(Switzerland):
+    'Aargau'
+
+    include_berchtolds_day = True
+    include_corpus_christi = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-AI')
+class AppenzellInnerrhoden(Switzerland):
+    'Appenzell Innerrhoden'
+
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-AR')
+class AppenzellAusserrhoden(Switzerland):
+    'Appenzell Ausserrhoden'
+
+    include_labour_day = True
+
+
+@iso_register('CH-BE')
+class Bern(Switzerland):
+    'Bern'
+
+    include_berchtolds_day = True
+
+
+@iso_register('CH-BL')
+class BaselLandschaft(Switzerland):
+    'Basel-Landschaft'
+
+    include_labour_day = True
+
+
+@iso_register('CH-BS')
+class BaselStadt(Switzerland):
+    'Basel-Stadt'
+
+    include_labour_day = True
+
+
+@iso_register('CH-FR')
+class Fribourg(Switzerland):
+    'Fribourg'
+
+    include_berchtolds_day = True
+    include_labour_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
 @iso_register('CH-GE')
 class Geneva(Switzerland):
     'Geneva'
 
     include_boxing_day = False
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
-        (8, 1, "National Holiday"),
+    FIXED_HOLIDAYS = Switzerland.FIXED_HOLIDAYS + (
         (12, 31, "Creation of Geneva Republic"),
     )
 
@@ -76,3 +124,226 @@ def get_variable_days(self, year):
         days = super().get_variable_days(year)
         days.append(self.get_genevan_fast(year))
         return days
+
+
+@iso_register('CH-GL')
+class Glarus(Switzerland):
+    'Glarus (Glaris)'
+
+    include_berchtolds_day = True
+    include_all_saints = True
+
+    FIXED_HOLIDAYS = Switzerland.FIXED_HOLIDAYS + (
+        (4, 3, "Näfels Ride"),
+    )
+
+
+@iso_register('CH-GR')
+class Graubunden(Switzerland):
+    'Graubünden (Grisons)'
+
+    include_epiphany = True
+    include_st_josephs_day = True
+    include_corpus_christi = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-JU')
+class Jura(Switzerland):
+    'Jura'
+
+    include_berchtolds_day = True
+    include_labour_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_boxing_day = False
+
+    FIXED_HOLIDAYS = Switzerland.FIXED_HOLIDAYS + (
+        (6, 23, "Independence Day"),
+    )
+
+
+@iso_register('CH-LU')
+class Luzern(Switzerland):
+    'Luzern'
+
+    include_berchtolds_day = True
+    include_epiphany = True
+    include_st_josephs_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-NE')
+class Neuchatel(Switzerland):
+    'Neuchâtel'
+
+    include_boxing_day = False
+    include_berchtolds_day = True
+    include_labour_day = True
+
+    FIXED_HOLIDAYS = Switzerland.FIXED_HOLIDAYS + (
+        (3, 1, "Republic Day"),
+    )
+
+
+@iso_register('CH-NW')
+class Nidwalden(Switzerland):
+    'Nidwalden'
+
+    include_st_josephs_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-OW')
+class Obwalden(Switzerland):
+    'Obwalden'
+
+    include_berchtolds_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+    FIXED_HOLIDAYS = Switzerland.FIXED_HOLIDAYS + (
+        (9, 25, "Saint Nicholas of Flüe Day"),
+    )
+
+
+@iso_register('CH-SG')
+class StGallen(Switzerland):
+    'St. Gallen'
+
+    include_all_saints = True
+
+
+@iso_register('CH-SH')
+class Schaffhausen(Switzerland):
+    'Schaffhausen'
+
+    include_berchtolds_day = True
+    include_labour_day = True
+
+
+@iso_register('CH-SO')
+class Solothurn(Switzerland):
+    'Solothurn'
+
+    include_berchtolds_day = True
+    include_st_josephs_day = True
+    include_labour_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-SZ')
+class Schwyz(Switzerland):
+    'Schwyz'
+
+    include_epiphany = True
+    include_st_josephs_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-TG')
+class Thurgau(Switzerland):
+    'Thurgau'
+
+    include_berchtolds_day = True
+    include_labour_day = True
+
+
+@iso_register('CH-TI')
+class Ticino(Switzerland):
+    'Ticino'
+
+    include_good_friday = False
+    include_epiphany = True
+    include_st_josephs_day = True
+    include_labour_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+    FIXED_HOLIDAYS = Switzerland.FIXED_HOLIDAYS + (
+        (6, 29, "Saints Peter and Paul"),
+    )
+
+
+@iso_register('CH-UR')
+class Uri(Switzerland):
+    'Uri'
+
+    include_epiphany = True
+    include_st_josephs_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-VD')
+class Vaud(Switzerland):
+    'Vaud'
+
+    include_berchtolds_day = True
+    include_boxing_day = False
+
+    def get_federal_thanksgiving_monday(self, year):
+        "Monday following the 3rd sunday of September"
+        third_sunday = self.get_nth_weekday_in_month(year, 9, SUN, 3)
+        return (
+            third_sunday + timedelta(days=1),
+            "Federal Thanksgiving Monday"
+        )
+
+    def get_variable_days(self, year):
+        days = super().get_variable_days(year)
+        days.append(self.get_federal_thanksgiving_monday(year))
+        return days
+
+
+@iso_register('CH-VS')
+class Valais(Switzerland):
+    'Valais'
+
+    include_good_friday = False
+    include_easter_monday = False
+    include_whit_monday = False
+    include_st_josephs_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+    include_boxing_day = False
+
+
+@iso_register('CH-ZG')
+class Zug(Switzerland):
+    'Zug'
+
+    include_berchtolds_day = True
+    include_corpus_christi = True
+    include_assumption = True
+    include_all_saints = True
+    include_immaculate_conception = True
+
+
+@iso_register('CH-ZH')
+class Zurich(Switzerland):
+    'Zürich'
+
+    include_berchtolds_day = True
+    include_labour_day = True
diff --git a/calendra/europe/turkey.py b/calendra/europe/turkey.py
index 3c20bfef..a8c729f5 100644
--- a/calendra/europe/turkey.py
+++ b/calendra/europe/turkey.py
@@ -1,13 +1,15 @@
 from datetime import timedelta
-from ..core import WesternCalendar, IslamicMixin
-from ..registry import iso_register
+from ..core import IslamicCalendar, SAT, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('TR')
-class Turkey(WesternCalendar, IslamicMixin):
+class Turkey(IslamicCalendar):
     'Turkey'
-
-    shift_new_years_day = True
+    include_new_years_day = True
+    shift_new_years_day = False
+    # Even though they're using an islamic calendar, the work week is MON->FRI
+    WEEKEND_DAYS = (SAT, SUN)
 
     # Islamic Holidays
     include_eid_al_fitr = True
@@ -15,9 +17,11 @@ class Turkey(WesternCalendar, IslamicMixin):
     include_eid_al_adha = True
     length_eid_al_adha = 4
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Labor and Solidarity Day"
+    FIXED_HOLIDAYS = IslamicCalendar.FIXED_HOLIDAYS + (
         (4, 23, "National Sovereignty and Children's Day"),
-        (5, 1, "Labor and Solidarity Day"),
         (5, 19, "Commemoration of Atatürk, Youth and Sports Day"),
         (7, 15, "Democracy and National Unity Day"),
         (8, 30, "Victory Day"),
diff --git a/calendra/europe/ukraine.py b/calendra/europe/ukraine.py
index 4d781507..a04963db 100644
--- a/calendra/europe/ukraine.py
+++ b/calendra/europe/ukraine.py
@@ -1,21 +1,23 @@
 from datetime import date
-from ..core import WesternCalendar, OrthodoxMixin
-from ..registry import iso_register
+from ..core import OrthodoxCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('UA')
-class Ukraine(OrthodoxMixin, WesternCalendar):
+class Ukraine(OrthodoxCalendar):
     'Ukraine'
 
     shift_sunday_holidays = True
     shift_new_years_day = True
 
-    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
+    FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + (
         (3, 8, "International Women’s Day"),
-        (5, 1, "Workers Solidarity Day"),
         (5, 9, "Victory Day"),
     )
-
+    # Civil holidays
+    include_labour_day = True
+    labour_day_label = "Workers Solidarity Day"
+    # Christian holidays
     include_christmas = False
     include_good_friday = True
     include_easter_sunday = True
diff --git a/calendra/europe/united_kingdom.py b/calendra/europe/united_kingdom.py
index a63a75ba..282bb0b9 100644
--- a/calendra/europe/united_kingdom.py
+++ b/calendra/europe/united_kingdom.py
@@ -3,12 +3,12 @@
 from dateutil import relativedelta as rd
 
 from ..core import Holiday
-from ..core import WesternCalendar, ChristianMixin
-from ..registry import iso_register
+from ..core import WesternCalendar
+from ..registry_tools import iso_register
 
 
 @iso_register('GB')
-class UnitedKingdom(WesternCalendar, ChristianMixin):
+class UnitedKingdom(WesternCalendar):
     'United Kingdom'
 
     include_good_friday = True
diff --git a/calendra/exceptions.py b/calendra/exceptions.py
index 6cab6f61..d4600833 100644
--- a/calendra/exceptions.py
+++ b/calendra/exceptions.py
@@ -19,3 +19,21 @@ class ISORegistryError(CalendarError):
     """
     Raised when you are trying to register a non-Calendar object
     """
+
+
+class ICalExportError(CalendarError):
+    """
+    Raised when the iCal export is impossible
+    """
+
+
+class ICalExportRangeError(ICalExportError):
+    """
+    Raised when the iCal export is impossible due to wrong range
+    """
+
+
+class ICalExportTargetPathError(ICalExportError):
+    """
+    Raised when the iCal export is impossible due to unusable target path
+    """
diff --git a/calendra/oceania/australia.py b/calendra/oceania/australia.py
index 6a545b3c..99cdc40e 100644
--- a/calendra/oceania/australia.py
+++ b/calendra/oceania/australia.py
@@ -1,12 +1,11 @@
 from datetime import date, timedelta
 
-from ..core import WesternCalendar, ChristianMixin
-from ..core import MON, TUE, SAT, SUN
-from ..registry import iso_register
+from ..core import WesternCalendar, MON, TUE, SAT, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('AU')
-class Australia(WesternCalendar, ChristianMixin):
+class Australia(WesternCalendar):
     "Australia"
     include_good_friday = True
     include_easter_monday = True
diff --git a/calendra/oceania/marshall_islands.py b/calendra/oceania/marshall_islands.py
index 900de2e6..f7698269 100644
--- a/calendra/oceania/marshall_islands.py
+++ b/calendra/oceania/marshall_islands.py
@@ -1,10 +1,9 @@
-from ..core import WesternCalendar, ChristianMixin
-from ..core import FRI
-from ..registry import iso_register
+from ..core import WesternCalendar, FRI
+from ..registry_tools import iso_register
 
 
 @iso_register('MH')
-class MarshallIslands(WesternCalendar, ChristianMixin):
+class MarshallIslands(WesternCalendar):
     "Marshall Islands"
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
         (3, 3, "Remembrance Day"),
diff --git a/calendra/oceania/new_zealand.py b/calendra/oceania/new_zealand.py
index 24fd6968..c05619e5 100644
--- a/calendra/oceania/new_zealand.py
+++ b/calendra/oceania/new_zealand.py
@@ -4,7 +4,7 @@
 
 from ..core import WesternCalendar, ChristianMixin
 from ..core import Holiday
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 def _by_name(holidays):
diff --git a/calendra/registry.py b/calendra/registry.py
index 8f6bdf30..6d6baa1d 100644
--- a/calendra/registry.py
+++ b/calendra/registry.py
@@ -1,5 +1,4 @@
 from importlib import import_module
-import warnings
 
 from .core import Calendar
 from .exceptions import ISORegistryError
@@ -33,7 +32,7 @@ def __init__(self, load_standard_modules=False):
         self.region_registry = dict()
         if load_standard_modules:
             for module_name in self.STANDARD_MODULES:
-                module = 'calendra.{}'.format(module_name)
+                module = f'calendra.{module_name}'
                 all_classes = getattr(import_module(module), '__all__')
                 self.load_module_from_items(module, all_classes)
 
@@ -43,7 +42,7 @@ def register(self, iso_code, cls):
         """
         if not issubclass(cls, Calendar):
             raise ISORegistryError(
-                "Class `{}` is not a Calendar class".format(cls)
+                f"Class `{cls}` is not a Calendar class"
             )
         self.region_registry[iso_code] = cls
 
@@ -59,7 +58,7 @@ def load_module_from_items(self, module_name, items):
                 if iso_code and cls.__name__ == class_name:
                     self.register(iso_code, cls)
 
-    def get_calendar_class(self, iso_code):
+    def get(self, iso_code):
         """
         Retrieve calendar class associated with given ``iso_code``.
 
@@ -87,28 +86,10 @@ def get_subregions(self, iso_code):
         """
         items = dict()
         for key, value in self.region_registry.items():
-            if key.startswith("{}-".format(iso_code)):
+            if key.startswith(f"{iso_code}-"):
                 items[key] = value
         return items
 
-    def items(self, region_codes=None, include_subregions=False):
-        """
-        Returns calendar classes for regions
-
-        :param region_codes list of ISO codes for selected regions. If empty,
-                            the function will return all items from the
-                            registry.
-        :param include_subregions boolean if subregions
-        of selected regions should be included in result
-        :rtype dict
-        :return dict where keys are ISO codes strings
-        and values are calendar classes
-        """
-        warnings.warn("The ``items()`` method will soon be deprecated."
-                      " Please use ``get_calendars()`` instead.",
-                      DeprecationWarning)
-        return self.get_calendars(region_codes, include_subregions)
-
     def get_calendars(self, region_codes=None, include_subregions=False):
         """
         Returns calendar classes for regions
@@ -143,29 +124,6 @@ def get_calendars(self, region_codes=None, include_subregions=False):
 
 registry = IsoRegistry()
 
-
-def iso_register(iso_code):
-    """
-    Registers Calendar class as country or region in IsoRegistry.
-
-    Registered country must set class variables ``iso`` using this decorator.
-
-    >>> from calendra.core import Calendar
-    >>> @iso_register('MC-MR')
-    ... class MyRegion(Calendar):
-    ...     'My Region'
-
-    Region calendar is then retrievable from registry:
-
-    >>> calendar = registry.get_calendar_class('MC-MR')
-    """
-
-    def wrapper(cls):
-        registry.register(iso_code, cls)
-        return cls
-    return wrapper
-
-
 # Europe Countries
 from calendra.europe import *  # noqa
 # United States of America
diff --git a/calendra/registry_tools.py b/calendra/registry_tools.py
new file mode 100644
index 00000000..1f47496e
--- /dev/null
+++ b/calendra/registry_tools.py
@@ -0,0 +1,28 @@
+"""
+Tools to update the ISO registry.
+"""
+
+
+def iso_register(iso_code):
+    """
+    Registers Calendar class as country or region in IsoRegistry.
+
+    Registered country must set class variables ``iso`` using this decorator.
+
+    >>> from calendra.core import Calendar
+    >>> from calendra.registry import registry
+    >>> from calendra.registry_tools import iso_register
+    >>> @iso_register('MC-MR')
+    ... class MyRegion(Calendar):
+    ...     'My Region'
+
+    Region calendar is then retrievable from registry:
+
+    >>> calendar = registry.get('MC-MR')
+    """
+
+    def wrapper(cls):
+        from calendra.registry import registry
+        registry.register(iso_code, cls)
+        return cls
+    return wrapper
diff --git a/calendra/usa/alabama.py b/calendra/usa/alabama.py
index 95e3c4af..785fe642 100644
--- a/calendra/usa/alabama.py
+++ b/calendra/usa/alabama.py
@@ -1,6 +1,6 @@
 from .core import UnitedStates
 from ..core import MON
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-AL')
@@ -16,12 +16,12 @@ class Alabama(UnitedStates):
 
 class AlabamaBaldwinCounty(Alabama):
     "Baldwin County, Alabama"
-    include_mardi_gras = True
+    include_fat_tuesday = True
 
 
 class AlabamaMobileCounty(Alabama):
     "Mobile County, Alabama"
-    include_mardi_gras = True
+    include_fat_tuesday = True
 
 
 class AlabamaPerryCounty(Alabama):
diff --git a/calendra/usa/alaska.py b/calendra/usa/alaska.py
index 94698826..a0bd29f1 100644
--- a/calendra/usa/alaska.py
+++ b/calendra/usa/alaska.py
@@ -3,7 +3,7 @@
 from .core import UnitedStates
 from ..core import MON
 from ..core import Holiday
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-AK')
diff --git a/calendra/usa/american_samoa.py b/calendra/usa/american_samoa.py
index ae6fb6e8..490e117e 100644
--- a/calendra/usa/american_samoa.py
+++ b/calendra/usa/american_samoa.py
@@ -1,7 +1,7 @@
 from datetime import date
 
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-AS')
diff --git a/calendra/usa/arizona.py b/calendra/usa/arizona.py
index 0f9ddda7..b8a7f37c 100644
--- a/calendra/usa/arizona.py
+++ b/calendra/usa/arizona.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-AZ')
diff --git a/calendra/usa/arkansas.py b/calendra/usa/arkansas.py
index 2136618e..64dd7ebb 100644
--- a/calendra/usa/arkansas.py
+++ b/calendra/usa/arkansas.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-AR')
diff --git a/calendra/usa/california.py b/calendra/usa/california.py
index a5b002a4..34e0c1c5 100644
--- a/calendra/usa/california.py
+++ b/calendra/usa/california.py
@@ -1,6 +1,8 @@
+from datetime import date
+
 from .core import UnitedStates
-from ..core import MON
-from ..registry import iso_register
+from ..core import MON, SUN
+from ..registry_tools import iso_register
 
 
 @iso_register('US-CA')
@@ -10,6 +12,19 @@ class California(UnitedStates):
     include_cesar_chavez_day = True
     include_columbus_day = False
 
+    shift_exceptions = [
+        (3, 31)  # Cesar Chavez Day should not be shifted the usual way.
+    ]
+
+    def get_cesar_chavez_days(self, year):
+        """
+        Special shift rules for Cesar Chavez Day in California
+        """
+        days = super().get_cesar_chavez_days(year)
+        if date(year, 3, 31).weekday() == SUN:
+            days.append((date(year, 4, 1), "Cesar Chavez Day (Observed)"))
+        return days
+
 
 class CaliforniaEducation(California):
     """California Education
diff --git a/calendra/usa/colorado.py b/calendra/usa/colorado.py
index e94ce939..afc34b43 100644
--- a/calendra/usa/colorado.py
+++ b/calendra/usa/colorado.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-CO')
diff --git a/calendra/usa/connecticut.py b/calendra/usa/connecticut.py
index 16125360..aa6fb476 100644
--- a/calendra/usa/connecticut.py
+++ b/calendra/usa/connecticut.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-CT')
diff --git a/calendra/usa/core.py b/calendra/usa/core.py
index bee36f93..4366b56d 100644
--- a/calendra/usa/core.py
+++ b/calendra/usa/core.py
@@ -2,14 +2,14 @@
 
 from dateutil import relativedelta as rd
 
-from ..core import WesternCalendar, ChristianMixin
+from ..core import WesternCalendar
 from ..core import SUN, MON, TUE, WED, THU, FRI, SAT
 from ..core import Holiday
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US')
-class UnitedStates(WesternCalendar, ChristianMixin):
+class UnitedStates(WesternCalendar):
     "United States of America"
 
     FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
@@ -25,7 +25,7 @@ def observance_shift(self):
         return Holiday.nearest_weekday
 
     # Veterans day label
-    include_veterans_day = True
+    include_veterans_day = False
     veterans_day_label = 'Veterans Day'
 
     # MLK
@@ -72,6 +72,8 @@ def observance_shift(self):
 
     # Some regional variants
     include_mardi_gras = False
+    include_fat_tuesday = False
+    fat_tuesday_label = "Mardi Gras"
 
     # Shift day mechanism
     # These days won't be shifted to next MON or previous FRI
@@ -112,7 +114,7 @@ def get_thanksgiving_friday(self, year):
 
     def get_confederate_day(self, year):
         """
-        Confederation memorial day is on the 4th MON of April.
+        Confederate memorial day is on the 4th MON of April.
         """
         day = self.get_nth_weekday_in_month(year, 4, MON, 4)
         return (day, "Confederate Memorial Day")
@@ -149,13 +151,12 @@ def get_presidents_day(self, year):
 
     def get_cesar_chavez_days(self, year):
         """
-        Cesar Chavez day is on 31st of March, float to 1st April if Monday.
+        Cesar Chavez day is on 31st of March
 
-        Will return a list of days.
+        Will return a list of days, because in some states (California),
+        it can float to MON if it happens on SUN.
         """
         days = [(date(year, 3, 31), "Cesar Chavez Day")]
-        if date(year, 3, 31).weekday() == SUN:
-            days.append((date(year, 4, 1), "Cesar Chavez Day (Observed)"))
         return days
 
     def get_patriots_day(self, year):
@@ -212,7 +213,7 @@ def get_inauguration_date(self, year):
         """
         if ((year - 1) % 4) != 0:
             raise ValueError(
-                "The year {} is not an Inauguration Year".format(year))
+                f"The year {year} is not an Inauguration Year")
         inauguration_day = date(year, 1, 20)
         if inauguration_day.weekday() == SUN:
             inauguration_day = date(year, 1, 21)
@@ -225,13 +226,6 @@ def get_national_memorial_day(self, year):
             indication="Last Monday in May",
         )
 
-    def get_mardi_gras(self, year):
-        """
-        Mardi Gras is the Tuesday happening 47 days before Easter
-        """
-        sunday = self.get_easter_sunday(year)
-        return (sunday - timedelta(days=47), "Mardi Gras")
-
     def get_variable_days(self, year):
         # usual variable days
         days = super().get_variable_days(year)
diff --git a/calendra/usa/delaware.py b/calendra/usa/delaware.py
index 689b5002..fdaebff4 100644
--- a/calendra/usa/delaware.py
+++ b/calendra/usa/delaware.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-DE')
diff --git a/calendra/usa/district_columbia.py b/calendra/usa/district_columbia.py
index e07baced..78b445a7 100644
--- a/calendra/usa/district_columbia.py
+++ b/calendra/usa/district_columbia.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-DC')
diff --git a/calendra/usa/florida.py b/calendra/usa/florida.py
index 3c5618db..bec0770b 100644
--- a/calendra/usa/florida.py
+++ b/calendra/usa/florida.py
@@ -3,7 +3,7 @@
 from pyluach.dates import GregorianDate
 
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 class HebrewHolidays:
@@ -78,7 +78,7 @@ class FloridaLegal(Florida):
         (4, 2, 'Pascua Florida Day'),
         (6, 14, 'Flag Day'),
     )
-    include_mardi_gras = True
+    include_fat_tuesday = True
     include_lincoln_birthday = True
     include_federal_presidents_day = True
     include_good_friday = True
diff --git a/calendra/usa/georgia.py b/calendra/usa/georgia.py
index d4d60ac9..c7761455 100644
--- a/calendra/usa/georgia.py
+++ b/calendra/usa/georgia.py
@@ -1,7 +1,7 @@
 import warnings
 from datetime import date
 from ..core import MON, TUE, WED, THU, FRI, SAT
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
@@ -11,6 +11,7 @@ class Georgia(UnitedStates):
     include_confederation_day = True
     include_federal_presidents_day = False
     label_washington_birthday_december = "Washington's Birthday (Observed)"
+    thanksgiving_friday_label = "State Holiday"
 
     def get_washington_birthday_december(self, year):
         """
@@ -19,15 +20,21 @@ def get_washington_birthday_december(self, year):
         It's only observed in Georgia.
         """
         warnings.warn(
-            "Washington birthday rules for Georgia State are confusing. "
+            "Washington birthday rules for Georgia State are possibly wrong. "
             "Use this calendar with care")
         christmas_day = date(year, 12, 25).weekday()
-        if christmas_day == MON:
+
+        # Special case: 2011 / Source:
+        # Christmas day is SUN, but doesn't follow the same rule as year 2016.
+        # https://web.archive.org/web/20110927122533/http://www.georgia.gov/00/channel_modifieddate/0,2096,4802_64437763,00.html  # noqa
+        if year == 2011:
+            day = date(year, 12, 26)
+        elif christmas_day == MON:
             day = date(year, 12, 26)  # TUE
         elif christmas_day == TUE:
             day = date(year, 12, 24)  # MON
         elif christmas_day == WED:
-            day = date(year, 12, 26)  # TUE
+            day = date(year, 12, 24)  # TUE
         elif christmas_day == THU:
             day = date(year, 12, 26)  # FRI
         elif christmas_day == FRI:
@@ -35,19 +42,39 @@ def get_washington_birthday_december(self, year):
         elif christmas_day == SAT:
             day = date(year, 12, 23)  # THU
         else:  # christmas_day == SUN:
-            day = date(year, 12, 23)  # FRI
+            day = date(year, 12, 27)  # FRI
         return (day, self.label_washington_birthday_december)
 
+    def get_confederate_day(self, year):
+        """
+        Confederate memorial day is on the 4th MON of April.
+
+        Exception: Year 2020, when it happened on April 10th.
+        """
+        if year >= 2016:
+            label = "State Holiday"
+        else:
+            label = "Confederate Memorial Day"
+
+        # At the moment, it's the only exception we know about.
+        if year == 2020:
+            return (date(year, 4, 10), label)
+
+        day = self.get_nth_weekday_in_month(year, 4, MON, 4)
+        return (day, label)
+
     def get_robert_lee_birthday(self, year):
         """
         Robert E. Lee's birthday.
 
-        Happens on the 4 Friday of November.
+        Happens on the day after Thanksgiving.
         """
-        return (
-            self.get_nth_weekday_in_month(year, 11, FRI, 4),
-            "Robert E. Lee's Birthday (Observed)"
-        )
+        if year < 2016:
+            label = "Robert E. Lee's Birthday (Observed)"
+        else:
+            label = "State Holiday"
+        date, _ = self.get_thanksgiving_friday(year)
+        return (date, label)
 
     def get_variable_days(self, year):
         days = super().get_variable_days(year)
diff --git a/calendra/usa/guam.py b/calendra/usa/guam.py
index c900ddc9..40430a40 100644
--- a/calendra/usa/guam.py
+++ b/calendra/usa/guam.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-GU')
diff --git a/calendra/usa/hawaii.py b/calendra/usa/hawaii.py
index 479e51cb..90c47434 100644
--- a/calendra/usa/hawaii.py
+++ b/calendra/usa/hawaii.py
@@ -1,5 +1,5 @@
 from ..core import FRI
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 from .core import UnitedStates
 
diff --git a/calendra/usa/idaho.py b/calendra/usa/idaho.py
index 928dfa03..cb5308ca 100644
--- a/calendra/usa/idaho.py
+++ b/calendra/usa/idaho.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-ID')
diff --git a/calendra/usa/illinois.py b/calendra/usa/illinois.py
index 5a85068d..e16cfbf6 100644
--- a/calendra/usa/illinois.py
+++ b/calendra/usa/illinois.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from ..core import MON
 
 
diff --git a/calendra/usa/indiana.py b/calendra/usa/indiana.py
index 2ecd619d..67787092 100644
--- a/calendra/usa/indiana.py
+++ b/calendra/usa/indiana.py
@@ -1,7 +1,7 @@
 import warnings
 from datetime import date
 from ..core import MON, TUE, WED, THU, FRI, SAT
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 from .core import UnitedStates
 
diff --git a/calendra/usa/iowa.py b/calendra/usa/iowa.py
index 74f6fd5a..58b4cd7d 100644
--- a/calendra/usa/iowa.py
+++ b/calendra/usa/iowa.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/kansas.py b/calendra/usa/kansas.py
index e5db8550..018d636e 100644
--- a/calendra/usa/kansas.py
+++ b/calendra/usa/kansas.py
@@ -1,5 +1,6 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
+
 
 # FIXME: According to wikipedia, Kansas only has all federal holidays, except
 # the Columbus Day and Washington's Birthday.
diff --git a/calendra/usa/kentucky.py b/calendra/usa/kentucky.py
index 2d4dc4bb..59c55f51 100644
--- a/calendra/usa/kentucky.py
+++ b/calendra/usa/kentucky.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-KY')
diff --git a/calendra/usa/louisiana.py b/calendra/usa/louisiana.py
index eae5e4ff..0f5c3499 100644
--- a/calendra/usa/louisiana.py
+++ b/calendra/usa/louisiana.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-LA')
@@ -8,4 +8,4 @@ class Louisiana(UnitedStates):
     include_good_friday = True
     include_election_day_even = True
     include_columbus_day = False
-    include_mardi_gras = True
+    include_fat_tuesday = True
diff --git a/calendra/usa/maine.py b/calendra/usa/maine.py
index a1694d9e..bc8ab109 100644
--- a/calendra/usa/maine.py
+++ b/calendra/usa/maine.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-ME')
diff --git a/calendra/usa/maryland.py b/calendra/usa/maryland.py
index 77554067..cd122169 100644
--- a/calendra/usa/maryland.py
+++ b/calendra/usa/maryland.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-MD')
diff --git a/calendra/usa/massachusetts.py b/calendra/usa/massachusetts.py
index 7e746ed5..cf5e795f 100644
--- a/calendra/usa/massachusetts.py
+++ b/calendra/usa/massachusetts.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-MA')
diff --git a/calendra/usa/michigan.py b/calendra/usa/michigan.py
index 6719044e..07702a2c 100644
--- a/calendra/usa/michigan.py
+++ b/calendra/usa/michigan.py
@@ -1,6 +1,6 @@
 from datetime import date
 from ..core import SUN
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 from .core import UnitedStates
 
diff --git a/calendra/usa/minnesota.py b/calendra/usa/minnesota.py
index 0bbc21c0..1fae6567 100644
--- a/calendra/usa/minnesota.py
+++ b/calendra/usa/minnesota.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/mississippi.py b/calendra/usa/mississippi.py
index a23a5b03..3d8fb754 100644
--- a/calendra/usa/mississippi.py
+++ b/calendra/usa/mississippi.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-MS')
diff --git a/calendra/usa/missouri.py b/calendra/usa/missouri.py
index 9c0f9d47..b128eb57 100644
--- a/calendra/usa/missouri.py
+++ b/calendra/usa/missouri.py
@@ -1,5 +1,5 @@
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-MO')
diff --git a/calendra/usa/montana.py b/calendra/usa/montana.py
index 45212570..809d3078 100644
--- a/calendra/usa/montana.py
+++ b/calendra/usa/montana.py
@@ -1,7 +1,7 @@
 import warnings
 
 from .core import UnitedStates
-from ..registry import iso_register
+from ..registry_tools import iso_register
 
 
 @iso_register('US-MT')
diff --git a/calendra/usa/nebraska.py b/calendra/usa/nebraska.py
index 57f34214..0f370d17 100644
--- a/calendra/usa/nebraska.py
+++ b/calendra/usa/nebraska.py
@@ -1,5 +1,5 @@
 from ..core import FRI
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/nevada.py b/calendra/usa/nevada.py
index dc76928b..bdb7bf13 100644
--- a/calendra/usa/nevada.py
+++ b/calendra/usa/nevada.py
@@ -1,5 +1,5 @@
 from ..core import FRI
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/new_hampshire.py b/calendra/usa/new_hampshire.py
index f4337780..51739ad9 100644
--- a/calendra/usa/new_hampshire.py
+++ b/calendra/usa/new_hampshire.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/new_jersey.py b/calendra/usa/new_jersey.py
index 2e0f5c94..6f777507 100644
--- a/calendra/usa/new_jersey.py
+++ b/calendra/usa/new_jersey.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/new_mexico.py b/calendra/usa/new_mexico.py
index ecf7406c..a28f7eee 100644
--- a/calendra/usa/new_mexico.py
+++ b/calendra/usa/new_mexico.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/new_york.py b/calendra/usa/new_york.py
index 68854854..20026652 100644
--- a/calendra/usa/new_york.py
+++ b/calendra/usa/new_york.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/north_carolina.py b/calendra/usa/north_carolina.py
index ea9efcc3..d483217e 100644
--- a/calendra/usa/north_carolina.py
+++ b/calendra/usa/north_carolina.py
@@ -1,7 +1,7 @@
 from datetime import date
 
 from ..core import MON, TUE, WED, THU, FRI, SAT, SUN
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/north_dakota.py b/calendra/usa/north_dakota.py
index 0a4d4ea6..9380324e 100644
--- a/calendra/usa/north_dakota.py
+++ b/calendra/usa/north_dakota.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/ohio.py b/calendra/usa/ohio.py
index 5abe04b9..a5359415 100644
--- a/calendra/usa/ohio.py
+++ b/calendra/usa/ohio.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/oklahoma.py b/calendra/usa/oklahoma.py
index 532d5c72..83be52c7 100644
--- a/calendra/usa/oklahoma.py
+++ b/calendra/usa/oklahoma.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/oregon.py b/calendra/usa/oregon.py
index b24e84cd..80586b40 100644
--- a/calendra/usa/oregon.py
+++ b/calendra/usa/oregon.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/pennsylvania.py b/calendra/usa/pennsylvania.py
index 5a2eb41f..d6327cf1 100644
--- a/calendra/usa/pennsylvania.py
+++ b/calendra/usa/pennsylvania.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/rhode_island.py b/calendra/usa/rhode_island.py
index 115673e6..b59c5945 100644
--- a/calendra/usa/rhode_island.py
+++ b/calendra/usa/rhode_island.py
@@ -1,5 +1,5 @@
 from ..core import MON
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/south_carolina.py b/calendra/usa/south_carolina.py
index d50323a5..fef300b0 100644
--- a/calendra/usa/south_carolina.py
+++ b/calendra/usa/south_carolina.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/south_dakota.py b/calendra/usa/south_dakota.py
index d5060d01..d04659e8 100644
--- a/calendra/usa/south_dakota.py
+++ b/calendra/usa/south_dakota.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/tennessee.py b/calendra/usa/tennessee.py
index 1ac7d72e..e60a2b41 100644
--- a/calendra/usa/tennessee.py
+++ b/calendra/usa/tennessee.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/texas.py b/calendra/usa/texas.py
index b71bc273..d03206b3 100644
--- a/calendra/usa/texas.py
+++ b/calendra/usa/texas.py
@@ -43,7 +43,7 @@ def get_variable_days(self, year):
 """
 from datetime import date
 
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/utah.py b/calendra/usa/utah.py
index 3707a44f..683d4ad2 100644
--- a/calendra/usa/utah.py
+++ b/calendra/usa/utah.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/vermont.py b/calendra/usa/vermont.py
index 35f332f6..470eb5f4 100644
--- a/calendra/usa/vermont.py
+++ b/calendra/usa/vermont.py
@@ -1,5 +1,5 @@
 from ..core import TUE
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/virginia.py b/calendra/usa/virginia.py
index ae065d41..dac15078 100644
--- a/calendra/usa/virginia.py
+++ b/calendra/usa/virginia.py
@@ -13,7 +13,7 @@ class Virginia(VirginiaBase):
 
 """
 from ..core import WED, FRI
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/washington.py b/calendra/usa/washington.py
index bd43eea0..85aa68f7 100644
--- a/calendra/usa/washington.py
+++ b/calendra/usa/washington.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/west_virginia.py b/calendra/usa/west_virginia.py
index 37794c25..9891c8f3 100644
--- a/calendra/usa/west_virginia.py
+++ b/calendra/usa/west_virginia.py
@@ -14,7 +14,7 @@ class WestVirginiaIncludeEves(WestVirginia):
 """
 from datetime import date
 
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/wisconsin.py b/calendra/usa/wisconsin.py
index cb0fed08..1309e3c1 100644
--- a/calendra/usa/wisconsin.py
+++ b/calendra/usa/wisconsin.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/calendra/usa/wyoming.py b/calendra/usa/wyoming.py
index d3e92884..6681ee51 100644
--- a/calendra/usa/wyoming.py
+++ b/calendra/usa/wyoming.py
@@ -1,4 +1,4 @@
-from ..registry import iso_register
+from ..registry_tools import iso_register
 from .core import UnitedStates
 
 
diff --git a/docs/_config.yml b/docs/_config.yml
deleted file mode 100644
index 8bbf7943..00000000
--- a/docs/_config.yml
+++ /dev/null
@@ -1,1 +0,0 @@
-theme: jekyll-theme-hacker
diff --git a/docs/advanced.md b/docs/advanced.md
deleted file mode 100644
index a8f9cb34..00000000
--- a/docs/advanced.md
+++ /dev/null
@@ -1,85 +0,0 @@
-# Advanced usage
-
-[Home](index.md) / [Basic usage](basic.md) / [ISO Registry](iso-registry.md)
-
-The following examples will better suit people willing to contribute to Calendra or building their custom calendars. They use primitives and methods attached to the core Calendar class to enable computation of complex holidays, that is to say dates that are not fixed, not related to religious calendars (Christmas always happens on December 25th, right?).
-
-## Find the following working day after a date
-
-It's a bit like a shortcut function to find the next working day after the given date.
-
-Please note that if the day you're entering is already a working day, that'll be the returned result.
-
-```python
->>> from datetime import date, datetime
->>> from calendra.europe import France
->>> cal = France()
->>> cal.find_following_working_day(date(2018, 7, 6))  # It's FRI
-datetime.date(2018, 7, 6)
->>> cal.find_following_working_day(date(2018, 7, 7))  # SAT => next MON
-datetime.date(2018, 7, 9)
-```
-
-**WARNING**: this function doesn't take into account the existing holidays in the calendar. If you need this, use the ``add_working_days()`` function as described in the [Basic usage document](basic.md).
-
-## Find the 4th Thursday in November
-
-That's a puzzling question that we needed to address when we had to implement United States of America holidays calendars. Thanksgiving day, for example, which is on the 4th Thursday in November (Thanksgiving Friday is the day after this thursday)... and many others, are defined as:
-
-> the ``nth`` ``Weekday name`` of the month of ``Month name``
-
-Or even better for Election day, which is:
-
-> the Tuesday next after the first Monday in the month of November.
-
-We've got you covered with static methods in the core ``Calendar`` class.
-
-```python
->>> from calendra.core import Calendar, THU
->>> Calendar.get_nth_weekday_in_month(2018, 11, THU)  # by default, find the first
-datetime.date(2018, 11, 2)
->>> Calendar.get_nth_weekday_in_month(2018, 11, THU, 4)  # Thanksgiving
-datetime.date(2018, 11, 23)
-```
-
-If you want to find the 2nd Monday after the 4th of July, you can use the ``start`` argument:
-
-```python
->>> from datetime import date
->>> from calendra.core import Calendar, MON
->>> Calendar.get_nth_weekday_in_month(
-  2018,  # Year
-  7,     # Month
-  MON,   # Monday
-  2,     # The 2nd one
-  start=date(2018, 7, 9))
-datetime.date(2018, 7, 16)
-```
-
-## Find the last Monday in the Month
-
-This one was a bit trickier, because it may happen that you have 4 or 5 `weekday name` in the same month. e.g: during the month of July 2018, you have 5 Mondays (2nd, 9th, 16th, 23rd, 30th), but only 4 Saturdays (7th, 14th, 21st, 28th).
-
-Same as above, there's a static method in the ``Calendar`` class:
-
-```python
->>> from calendra.core import Calendar, MON, FRI
->>> Calendar.get_last_weekday_in_month(2018, 7, MON)
-datetime.date(2018, 7, 30)
->>> Calendar.get_last_weekday_in_month(2018, 7, FRI)
-datetime.date(2018, 7, 27)
-```
-
-## Find the first Monday after a given date
-
-Colombia, as an example, states that the Epiphany Day is set to the "First Monday after the 6th of January". You may use the following function to find this specific formula:
-
-```python
->>> from calendra.core import Calendar, MON
->>> from datetime import date
->>> jan_6th = date(2018, 1, 6)  # It's a Saturday
->>> Calendar.get_first_weekday_after(jan_6th, MON)
-datetime.date(2018, 1, 8)
-```
-
-[Home](index.md) / [Basic usage](basic.md) / [ISO Registry](iso-registry.md)
diff --git a/docs/basic.md b/docs/basic.md
deleted file mode 100644
index dd5479dd..00000000
--- a/docs/basic.md
+++ /dev/null
@@ -1,112 +0,0 @@
-# Basic usage
-
-[Home](index.md) / [Advanced usage](advanced.md) / [ISO Registry](iso-registry.md)
-
-Here are basic examples of what Calendra can do for you. As an integrator or a simple Calendra user, you will use these methods to retrieve calendars, and get basic outputs for a given date.
-
-## Get holidays for a given country and year
-
-```python
->>> from calendra.europe import France
->>> cal = France()
->>> cal.holidays(2012)
-[(datetime.date(2012, 1, 1), 'New year'),
- (datetime.date(2012, 4, 9), 'Easter Monday'),
- (datetime.date(2012, 5, 1), 'Labour Day'),
- (datetime.date(2012, 5, 8), 'Victory in Europe Day'),
- (datetime.date(2012, 5, 17), 'Ascension Day'),
- (datetime.date(2012, 5, 28), 'Whit Monday'),
- (datetime.date(2012, 7, 14), 'Bastille Day'),
- (datetime.date(2012, 8, 15), 'Assumption of Mary to Heaven'),
- (datetime.date(2012, 11, 1), "All Saints' Day"),
- (datetime.date(2012, 11, 11), 'Armistice Day'),
- (datetime.date(2012, 12, 25), 'Christmas')]
-```
-
-As you can see, the output is a simple list of tuples, with the first member being a `datetime.date` object, and the second one is the holiday label as a string. By design, we have chosen to stick to basic types so you can use them in your application and eventually compose your personal classes using them.
-
-## Know if a day is a holiday or not
-
-```python
->>> from datetime import date
->>> from calendra.europe import France
->>> cal = France()
->>> cal.is_working_day(date(2012, 12, 25))  # it's Christmas
-False
->>> cal.is_working_day(date(2012, 12, 30))  # it's a Sunday
-False
->>> cal.is_working_day(date(2012, 12, 26))  # No Boxing day in France
-True
-```
-
-## Compute the "nth" working day after a given date
-
-That was the basic use-case that started this library development: to answer to the following question:
-
-> This task is due in 5 working days, starting of today. Can we calculate that?
-
-In the following example, we want to calculate 5 working days after December the 23rd 2012.
-
-| Date | Weekday | Working Day? | Count |
-|:-----|:-------:|:------------:|:-----:|
-| 23rd |   SUN   |      No      |   0   |
-| 24th |   MON   |     Yes      |  +1   |
-| 25th |   TUE   |  No (XMas)   |   -   |
-| 26th |   WED   |     Yes      |  +2   |
-| 27th |   THU   |     Yes      |  +3   |
-| 28th |   FRI   |     Yes      |  +4   |
-| 29th |   SAT   | No (weekend) |   -   |
-| 30th |   SUN   | No (weekend) |   -   |
-| 31th |   MON   |     Yes      |  +5   |
-
-```python
->>> from datetime import date
->>> from calendra.europe import France
->>> cal = France()
->>> cal.add_working_days(date(2012, 12, 23), 5)
-datetime.date(2012, 12, 31)
-```
-
-If we had requested the 6th day after the 23rd, it would have been January 2nd, 2013, because January 1st is New Year's Day.
-
-## Calculate the number or working days between two given days
-
-Let's say you want to know how many working days there are between April 2nd and June 17th of 2018, in France. Use the following:
-
-```python
->>> from datetime import date
->>> from calendra.europe import France
->>> cal = France()
->>> cal.get_working_days_delta(date(2018, 4, 2), date(2018, 6, 17))
-50
-```
-
-## Standard date(time) types only, please!
-
-For your convenience, we allow both `datetime.date` and `datetime.datetime` types (and their subclasses) when using the core functions.
-
-**WARNING**: We'll only allow "dates" types coming from the Python standard library. If you're manipulating types from external library. Trying to pass a non-standard argument will result in raising a ``UnsupportedDateType`` error.
-
-Example:
-
-```python
->>> from datetime import date, datetime
->>> from calendra.europe import France
->>> cal = France()
->>> cal.is_working_day(datetime(2012, 12, 25, 14, 0, 39))
-False
->>> cal.add_working_days(datetime(2012, 12, 23, 14, 0, 39), 5)
-datetime.datetime(2012, 12, 31)
-```
-
-If you really need it, you can use the ``add_working_days()`` with an option that will keep the ``datetime`` type:
-
-```python
->>> from datetime import date, datetime
->>> from calendra.europe import France
->>> cal = France()
->>> cal.add_working_days(datetime(2012, 12, 23, 14, 0, 39), 5, keep_datetime=True)
-datetime.datetime(2012, 12, 31, 14, 0, 39)
-```
-
-[Home](index.md) / [Advanced usage](advanced.md) / [ISO Registry](iso-registry.md)
diff --git a/docs/index.md b/docs/index.md
deleted file mode 100644
index 0010b20b..00000000
--- a/docs/index.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Calendra documentation
-
-## Overview
-
-Calendra is a Python module that offers classes able to handle calendars, list legal / religious holidays and gives working-day-related computation functions.
-
-## Status
-
-This library is ready for production, although we may warn eventual users: some calendars may not be up-to-date, and this library doesn't cover all the existing countries on earth (yet).
-
-## Available Calendars
-
-See [the repository README](https://github.com/jaraco/calendra#available-calendars) for the most up-to-date catalog of the available calendars.
-
-## Usage examples
-
-* [Basic usage](basic.md)
-* [Advanced usage](advanced.md)
-* [ISO Registry](iso-registry.md)
diff --git a/docs/index.rst b/docs/index.rst
index 9645ff5a..bfbd592f 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -6,6 +6,8 @@ Welcome to calendra documentation!
 
    history
 
+See the `workalendar docs <https://peopledoc.github.io/workalendar>`_
+for more details.
 
 .. automodule:: calendra
     :members:
diff --git a/docs/iso-registry.md b/docs/iso-registry.md
deleted file mode 100644
index e1d6a4f1..00000000
--- a/docs/iso-registry.md
+++ /dev/null
@@ -1,130 +0,0 @@
-# The ISO registry
-
-[Home](index.md) / [Basic usage](basic.md) / [Advanced usage](advanced.md)
-
-As of version 3.0 (August/September 2018), we have introduced a global calendar registry for calendars related to a country or a region that belongs to the [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1) or the [ISO 3166-2](https://en.wikipedia.org/wiki/ISO_3166-2) for sub-regions (such as USA states, Australian territories or Canadian provinces and such).
-
-## Iterate over the whole registry
-
-```python
->>> from workalendar.registry import registry
->>> calendars = registry.get_calendars()
->>> for code, calendar_class in calendars.items():
-...     print("`{}` is code for '{}'".format(code, calendar_class.name))
-`AT` is code for 'Austria'
-`BE` is code for 'Belgium'
-`BG` is code for 'Bulgaria'
-`HR` is code for 'Croatia'
-`CY` is code for 'Cyprus'
-`CZ` is code for 'Czech Republic'
-`EE` is code for 'Estonia'
-`DK` is code for 'Denmark'
-`FI` is code for 'Finland'
-`FR` is code for 'France'
-... continued
-```
-
-The "private property" `registry.region_registry` is a `dict` object, with the ISO code as a key, and the calendar class as the value. As a "workalendar standard", **every** calendar in the registry has a `name` property (derived from the docstring), so you'd probably be able to build a user-friendly list of available calendars, for a dropdown list, for example.
-
-**DEPRECATION WARNING**: the ``get_calendars`` method used to be named ``items()``. In a future release, it'll be deprecated and re-purposed. Please switch to using ``get_calendars()`` for all your queries in the registry.
-
-## Retrieve a collection of regions
-
-If you want the full dictionary of **countries**, you can use the ``get_calendars()`` method.
-
-```python
->>> registry.get_calendars()
-{'AT': <class 'workalendar.europe.austria.Austria'>,
- 'BE': <class 'workalendar.europe.belgium.Belgium'>,
- 'BG': <class 'workalendar.europe.bulgaria.Bulgaria'>,
- 'KY': <class 'workalendar.europe.cayman_islands.CaymanIslands'>,
-  # ... continued
-}
-```
-
-Let's say you'd need only a subset of the ISO registry. For example, France, Switzerland and Canada calendars. You can use the method `get_calendars()` to filter only the calendars you want.
-
-```python
->>> registry.get_calendars(['FR', 'CH', 'CA'])
-{'FR': <class 'workalendar.europe.france.France'>,
- 'CH': <class 'workalendar.europe.switzerland.Switzerland'>,
- 'CA': <class 'workalendar.america.canada.Canada'>}
-```
-
-Also, if you want those regions **and** their subregions, you can use the `include_subregions` flag:
-
-```python
->>> registry.get_calendars(['FR', 'CH', 'CA'], include_subregions=True)
-{'CA': <class 'workalendar.america.canada.Canada'>,
- 'CA-AB': <class 'workalendar.america.canada.Alberta'>,
- 'CA-BC': <class 'workalendar.america.canada.BritishColumbia'>,
- 'CA-MB': <class 'workalendar.america.canada.Manitoba'>,
- 'CA-NB': <class 'workalendar.america.canada.NewBrunswick'>,
- 'CA-NL': <class 'workalendar.america.canada.Newfoundland'>,
- 'CA-NS': <class 'workalendar.america.canada.NovaScotia'>,
- 'CA-NT': <class 'workalendar.america.canada.NorthwestTerritories'>,
- 'CA-NU': <class 'workalendar.america.canada.Nunavut'>,
- 'CA-ON': <class 'workalendar.america.canada.Ontario'>,
- 'CA-PE': <class 'workalendar.america.canada.PrinceEdwardIsland'>,
- 'CA-QC': <class 'workalendar.america.canada.Quebec'>,
- 'CA-SK': <class 'workalendar.america.canada.Saskatchewan'>,
- 'CA-YT': <class 'workalendar.america.canada.Yukon'>,
- 'CH': <class 'workalendar.europe.switzerland.Switzerland'>,
- 'CH-VD': <class 'workalendar.europe.switzerland.Vaud'>,
- 'CH-GE': <class 'workalendar.europe.switzerland.Geneva'>,
- 'FR': <class 'workalendar.europe.france.France'>}
-```
-
-*Note*: if any of the codes is unknown, this function won't raise an error.
-
-You can also get the full dict of all calendars registered in the ISO Registry with all the subregions using the following function call:
-
-```python
->>> registry.get_calendars(include_subregions=True)
-```
-
-## Select only one calendar
-
-Let's say that we only know the ISO code for Switzerland (`CH`). If we want to compute holidays for Switzerland in 2018, we can do as follows:
-
-```python
->>> registry.get_calendar_class('CH')
->>> CalendarClass = registry.get_calendar_class('CH')
->>> calendar = CalendarClass()
->>> calendar.holidays(2018)
-[(datetime.date(2018, 1, 1), 'New year'),
- (datetime.date(2018, 1, 2), "Berchtold's Day"),
- (datetime.date(2018, 3, 30), 'Good Friday'),
- (datetime.date(2018, 4, 1), 'Easter Sunday'),
- (datetime.date(2018, 4, 2), 'Easter Monday'),
- (datetime.date(2018, 5, 1), 'Labour Day'),
- (datetime.date(2018, 5, 10), 'Ascension Thursday'),
- (datetime.date(2018, 5, 20), 'Whit Sunday'),
- (datetime.date(2018, 5, 21), 'Whit Monday'),
- (datetime.date(2018, 8, 1), 'National Holiday'),
- (datetime.date(2018, 12, 25), 'Christmas Day'),
- (datetime.date(2018, 12, 26), 'Boxing Day')]
-```
-
-*Note*: this function would return `None` if the code is unknown.
-
-
-## Select only sub-regions
-
-As an example, the United States of America, or Australia and others are divided into "sub-regions" (states, provinces, territories, etc). There are usually Federal or State holidays, and variants depending on the region you're targeting.
-
-```python
->>> registry.get_subregions('AU')
-{'AU-ACT': <class 'workalendar.oceania.australia.AustralianCapitalTerritory'>,
- 'AU-NSW': <class 'workalendar.oceania.australia.NewSouthWales'>,
- 'AU-NT': <class 'workalendar.oceania.australia.NorthernTerritory'>,
- 'AU-QLD': <class 'workalendar.oceania.australia.Queensland'>,
- 'AU-SA': <class 'workalendar.oceania.australia.SouthAustralia'>,
- 'AU-TAS': <class 'workalendar.oceania.australia.Tasmania'>,
- 'AU-VIC': <class 'workalendar.oceania.australia.Victoria'>,
- 'AU-WA': <class 'workalendar.oceania.australia.WesternAustralia'>}
-```
-
-*Note*: this function will return an empty `dict` if the code is unknown.
-
-[Home](index.md) / [Basic usage](basic.md) / [Advanced usage](advanced.md)
diff --git a/setup.py b/setup.py
index bac24a43..69c15907 100644
--- a/setup.py
+++ b/setup.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-import setuptools
+from setuptools import setup
 
-if __name__ == "__main__":
-    setuptools.setup()
+if __name__ == '__main__':
+    setup()
diff --git a/tox.ini b/tox.ini
index 7347d95f..86b15b15 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,12 +1,12 @@
 [tox]
 envlist = python
-minversion = 3.2
+minversion = 3.6
 # https://github.com/jaraco/skeleton/issues/6
 tox_pip_extensions_ext_venv_update = true
 
 
 [testenv]
-deps =
+deps = freezegun
 commands =
 	pytest {posargs}
 usedevelop = True


Test patch:
diff --git a/calendra/tests/__init__.py b/calendra/tests/__init__.py
index fa37265d..fa2f0c99 100644
--- a/calendra/tests/__init__.py
+++ b/calendra/tests/__init__.py
@@ -1,15 +1,125 @@
+import tempfile
+import os
+from os.path import join
 import warnings
 from datetime import date
 from unittest import TestCase
 
+from freezegun import freeze_time
+import pytest
+
 from ..core import Calendar
+from .. import __version__
 
 
-class GenericCalendarTest(TestCase):
+class CoreCalendarTest(TestCase):
     cal_class = Calendar
+    kwargs = {}
 
     def setUp(self):
         super().setUp()
-        warnings.simplefilter('ignore')
         self.year = date.today().year
-        self.cal = self.cal_class()
+        self.cal = self.cal_class(**self.kwargs)
+
+
+class GenericCalendarTest(CoreCalendarTest):
+    test_include_january_1st = True
+
+    def setUp(self):
+        super().setUp()
+        warnings.simplefilter('ignore')
+
+    def test_weekend_days(self):
+        class_name = self.cal_class.__name__
+        if class_name in ['Calendar']:
+            return
+        try:
+            self.cal.get_weekend_days()
+        except NotImplementedError:
+            assert False, (self.cal, class_name)
+
+    def test_january_1st(self):
+        class_name = self.cal_class.__name__
+        if class_name in ['Calendar']:
+            return
+        holidays = self.cal.holidays_set(self.year)
+        if self.test_include_january_1st:
+            self.assertIn(date(self.year, 1, 1), holidays)
+        else:
+            self.assertNotIn(date(self.year, 1, 1), holidays)
+
+    @pytest.mark.xfail(
+        "platform.system() == 'Windows'",
+        reason="https://github.com/peopledoc/workalendar/issues/607")
+    def test_ical_export(self):
+        """Check that an iCal file can be created according to iCal spec."""
+        class_name = self.cal_class.__name__
+        if class_name in ['Calendar']:
+            return
+
+        holidays = self.cal.holidays(2019) + self.cal.holidays(2020)
+
+        temp_dir = join(tempfile.gettempdir(), "failed_ical_tests")
+        os.makedirs(temp_dir, exist_ok=True)
+        _, test_file_name = tempfile.mkstemp(
+            prefix="%s_" % self.cal_class.__name__,
+            suffix=".ics",
+            dir=temp_dir,
+        )
+
+        self.cal.export_to_ical(
+            period=[2019, 2020],
+            target_path=test_file_name,
+        )
+        # A standard iCal extension should have been added automatically:
+        with open(test_file_name) as ics_file:
+            # check header
+            assert ics_file.readline() == 'BEGIN:VCALENDAR\n'
+            assert ics_file.readline() == 'VERSION:2.0\n'
+            prod_id_line = ics_file.readline()
+            assert prod_id_line == (
+                f'PRODID:-//workalendar//ical {__version__}//EN\n')
+            # check new year
+            assert ics_file.readline() == 'BEGIN:VEVENT\n'
+            first_event_name = holidays[0][1]
+            assert ics_file.readline() == 'SUMMARY:%s\n' % first_event_name
+            if self.test_include_january_1st:
+                assert ics_file.readline() == 'DTSTART;VALUE=DATE:20190101\n'
+                assert ics_file.readline().startswith(
+                    'DTSTAMP;VALUE=DATE-TIME:')
+                first_uid_line = ics_file.readline()
+                uid_lines = [first_uid_line]
+                assert first_uid_line.startswith('UID:2019')
+                assert ics_file.readline() == 'END:VEVENT\n'
+            else:
+                uid_lines = []
+            remaining_lines = ics_file.readlines()
+            uid_lines += [line for line in remaining_lines
+                          if line.startswith('UID:')]
+            # check number of entries
+            assert len(uid_lines) == len(holidays)
+            # check that UIDs are unique within the calendar
+            assert len(uid_lines) == len(set(uid_lines))
+            # check that final year is included
+            assert remaining_lines[-3].startswith(('UID:2020', 'UID:2021'))
+            # check last few lines of file
+            assert remaining_lines[-2] == 'END:VEVENT\n'
+            assert remaining_lines[-1] == 'END:VCALENDAR\n'
+
+        # Freeze time to make sure they both have the same timestamp
+        with freeze_time():
+            # Regenerate the file
+            self.cal.export_to_ical(
+                period=[2019, 2020],
+                target_path=test_file_name,
+            )
+            # Not providing any target => returns the value as a string
+            var_contents = self.cal.export_to_ical(period=[2019, 2020])
+
+        with open(test_file_name) as fd:
+            file_contents = fd.read()
+
+        assert file_contents == var_contents
+
+        # Remove the .ics file if this test passes
+        os.remove(test_file_name)
diff --git a/calendra/tests/test_africa.py b/calendra/tests/test_africa.py
index 76336920..d1b649c0 100644
--- a/calendra/tests/test_africa.py
+++ b/calendra/tests/test_africa.py
@@ -5,6 +5,7 @@
     Angola,
     Benin,
     IvoryCoast,
+    Kenya,
     Madagascar,
     SaoTomeAndPrincipe,
     SouthAfrica,
@@ -427,6 +428,11 @@ def test_special_2000(self):
         # Shift day because Jan 2nd was a sunday
         self.assertIn(date(2000, 1, 3), holidays)
 
+    def test_special_2001(self):
+        holidays = self.cal.holidays_set(2001)
+        # Holiday added to celebrate Y2K
+        self.assertIn(date(2001, 1, 2), holidays)
+
     def test_special_2004(self):
         # National and provincial government elections – 14 April 2004
         holidays = self.cal.holidays_set(2004)
@@ -558,3 +564,55 @@ def test_year_2018(self):
         self.assertIn(date(2018, 11, 11), holidays)
         # Dia do Natal – 25 de Dezembro
         self.assertIn(date(2018, 12, 25), holidays)  # Natal
+
+    def test_carnaval_label(self):
+        holidays = self.cal.holidays(2018)
+        holidays_dict = dict(holidays)
+        label_carnaval = holidays_dict[date(2018, 2, 13)]
+        self.assertEqual(label_carnaval, "Dia de Carnaval")
+
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays_dict = dict(holidays)
+        label = holidays_dict[date(2020, 5, 1)]
+        self.assertEqual(label, "Dia Internacional do Trabalhador")
+
+
+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.assertEqual(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.assertEqual(len(holidays), 14)
diff --git a/calendra/tests/test_america.py b/calendra/tests/test_america.py
index bd377516..80fba53e 100644
--- a/calendra/tests/test_america.py
+++ b/calendra/tests/test_america.py
@@ -70,7 +70,12 @@ def test_holidays_2020(self):
         self.assertIn(date(2020, 2, 24), holidays)
         self.assertIn(date(2020, 2, 25), holidays)
         self.assertIn(date(2020, 3, 24), holidays)
-        self.assertIn(date(2020, 4, 2), holidays)
+        # Special case: Argentina has shifted this holiday due to
+        # Coronavirus lockdown in 2020.
+        self.assertNotIn(date(2020, 4, 2), holidays)
+        self.assertIn(date(2020, 3, 31), holidays)
+
+        # Back to normal, I hope...
         self.assertIn(date(2020, 4, 10), holidays)
         self.assertIn(date(2020, 5, 1), holidays)
         self.assertIn(date(2020, 5, 25), holidays)
@@ -97,6 +102,40 @@ def test_holidays_2021(self):
         # The day before
         self.assertIn(date(2021, 10, 11), holidays)
 
+    def test_dia_malvinas_label(self):
+        _, label = self.cal.get_malvinas_day(2020)
+        self.assertEqual(
+            label,
+            "Día del Veterano y de los Caídos en la Guerra de Malvinas"
+        )
+
+    def test_dia_memoria_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        label_memoria = holidays[date(2020, 3, 24)]
+        self.assertEqual(
+            label_memoria,
+            "Día Nacional de la Memoria por la Verdad y la Justicia"
+        )
+
+    def test_carnival_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        label_carnival = holidays[date(2020, 2, 25)]
+        self.assertEqual(label_carnival, "Carnival")
+
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        label = holidays[date(2020, 5, 1)]
+        self.assertEqual(label, "Día del Trabajador")
+
+    def test_immaculate_conception_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        label = holidays[date(2020, 12, 8)]
+        self.assertEqual(label, "Día de la Inmaculada Concepción de María")
+
 
 class ChileTest(GenericCalendarTest):
     cal_class = Chile
@@ -136,28 +175,100 @@ class ColombiaTest(GenericCalendarTest):
 
     def test_holidays_2015(self):
         holidays = self.cal.holidays_set(2015)
-        self.assertIn(date(2015, 1, 1), holidays)
-        self.assertIn(date(2015, 1, 12), holidays)
-        self.assertIn(date(2015, 3, 23), holidays)
-        self.assertIn(date(2015, 3, 29), holidays)
-        self.assertIn(date(2015, 4, 2), holidays)
-        self.assertIn(date(2015, 4, 3), holidays)
-        self.assertIn(date(2015, 4, 5), holidays)
-        self.assertIn(date(2015, 5, 1), holidays)
-        self.assertIn(date(2015, 5, 18), holidays)
-        self.assertIn(date(2015, 6, 8), holidays)
-        self.assertIn(date(2015, 6, 15), holidays)
-        self.assertIn(date(2015, 6, 29), holidays)
-        self.assertIn(date(2015, 7, 20), holidays)
-        self.assertIn(date(2015, 8, 7), holidays)
-        self.assertIn(date(2015, 8, 17), holidays)
-        self.assertIn(date(2015, 10, 12), holidays)
-        self.assertIn(date(2015, 11, 2), holidays)
-        self.assertIn(date(2015, 11, 16), holidays)
-        self.assertIn(date(2015, 12, 8), holidays)
-        self.assertIn(date(2015, 12, 25), holidays)
+        self.assertIn(date(2015, 1, 1), holidays)    # New year
+        self.assertIn(date(2015, 1, 12), holidays)   # Epiphany (shifted)
+        self.assertIn(date(2015, 3, 23), holidays)   # Saint Joseph
+        self.assertIn(date(2015, 3, 29), holidays)   # Palm Sunday
+        self.assertIn(date(2015, 4, 2), holidays)    # Holy Thursday
+        self.assertIn(date(2015, 4, 3), holidays)    # Good Friday
+        self.assertIn(date(2015, 4, 5), holidays)    # Easter (SUN)
+        self.assertIn(date(2015, 5, 1), holidays)    # Labour Day
+        self.assertIn(date(2015, 5, 18), holidays)   # Ascension (shifted)
+        self.assertIn(date(2015, 6, 8), holidays)    # Corpus Christi
+        self.assertIn(date(2015, 6, 15), holidays)   # Sacred Heart
+        self.assertIn(date(2015, 6, 29), holidays)   # St Peter & St Paul
+        self.assertIn(date(2015, 7, 20), holidays)   # Independance Day
+        self.assertIn(date(2015, 8, 7), holidays)    # Boyacá battle
+        self.assertIn(date(2015, 8, 17), holidays)   # Assumption (shifted)
+        self.assertIn(date(2015, 10, 12), holidays)  # Day of the Races
+        self.assertIn(date(2015, 11, 2), holidays)   # All Saints (shifted)
+        self.assertIn(date(2015, 11, 16), holidays)  # Cartagena independence
+        self.assertIn(date(2015, 12, 8), holidays)   # Immaculate Conception
+        self.assertIn(date(2015, 12, 25), holidays)  # XMas
+        self.assertEqual(len(holidays), 20)
+
+    def test_holidays_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)    # New year
+        self.assertIn(date(2020, 1, 6), holidays)    # Epiphany
+        self.assertIn(date(2020, 3, 23), holidays)   # Saint Joseph
+        self.assertIn(date(2020, 4, 5), holidays)    # Palm Sunday
+        self.assertIn(date(2020, 4, 9), holidays)    # Holy Thursday
+        self.assertIn(date(2020, 4, 10), holidays)   # Good Friday
+        self.assertIn(date(2020, 4, 12), holidays)   # Easter (SUN)
+        self.assertIn(date(2020, 5, 1), holidays)    # Labour Day
+        self.assertIn(date(2020, 5, 25), holidays)   # Ascension (shifted)
+        self.assertIn(date(2020, 6, 15), holidays)   # Corpus Christi
+        self.assertIn(date(2020, 6, 22), holidays)   # Sacred Heart
+        self.assertIn(date(2020, 6, 29), holidays)   # St Peter & St Paul
+        self.assertIn(date(2020, 7, 20), holidays)   # Independance Day
+        self.assertIn(date(2020, 8, 7), holidays)    # Boyacá battle
+        self.assertIn(date(2020, 8, 17), holidays)   # Assumption (shifted)
+        self.assertIn(date(2020, 10, 12), holidays)  # Day of the Races
+        self.assertIn(date(2020, 11, 2), holidays)   # All Saints (shifted)
+        self.assertIn(date(2020, 11, 16), holidays)  # Cartagena independence
+        self.assertIn(date(2020, 12, 8), holidays)   # Immaculate Conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
         self.assertEqual(len(holidays), 20)
 
+    def test_epiphany_monday(self):
+        # In 2020, Epiphany falls on MON
+        epiphany_2020 = self.cal.get_epiphany(2020)
+        self.assertEqual(epiphany_2020, date(2020, 1, 6))
+        # In 2021, it does not, so it's shifted to the next MON
+        epiphany_2021 = self.cal.get_epiphany(2021)
+        self.assertEqual(epiphany_2021, date(2021, 1, 11))
+
+    def test_saint_peter_and_saint_paul_monday(self):
+        # In 2020, Saint Peter and Saint Paul falls on MON
+        st_peter_paul_2020 = self.cal.get_saint_peter_and_saint_paul(2020)
+        self.assertEqual(st_peter_paul_2020, date(2020, 6, 29))
+        # In 2021, it does not, so it's shifted to the next MON
+        st_peter_paul_2021 = self.cal.get_saint_peter_and_saint_paul(2021)
+        self.assertEqual(st_peter_paul_2021, date(2021, 7, 5))
+
+    def test_assumption_monday(self):
+        # In 2021, Assumption falls on SUN, so it's shifted to MON
+        assumption_2021 = self.cal.get_assumption(2021)
+        self.assertEqual(assumption_2021, date(2021, 8, 16))
+        # In 2022, Assumption falls on MON
+        assumption_2022 = self.cal.get_assumption(2022)
+        self.assertEqual(assumption_2022, date(2022, 8, 15))
+
+    def test_day_of_the_races_monday(self):
+        # In 2020, Day of the races and hispanity falls on MON
+        day_races_2020 = self.cal.get_day_of_the_races(2020)
+        self.assertEqual(day_races_2020, date(2020, 10, 12))
+        # In 2021, It does not, so it's shifted to the next MON
+        day_races_2021 = self.cal.get_day_of_the_races(2021)
+        self.assertEqual(day_races_2021, date(2021, 10, 18))
+
+    def test_all_saints_monday(self):
+        # In 2021, The All Saints falls on MON
+        all_saints_2021 = self.cal.get_all_saints(2021)
+        self.assertEqual(all_saints_2021, date(2021, 11, 1))
+        # In 2022, It does not, so it's shifted to the next MON
+        all_saints_2022 = self.cal.get_all_saints(2022)
+        self.assertEqual(all_saints_2022, date(2022, 11, 7))
+
+    def test_cartagena_independence_monday(self):
+        # In 2019, The Cartagena Independance falls on MON
+        cartagena_2019 = self.cal.get_cartagena_independence(2019)
+        self.assertEqual(cartagena_2019, date(2019, 11, 11))
+        # In 2020, It does not, so it's shifted to the next MON
+        cartagena_2020 = self.cal.get_cartagena_independence(2020)
+        self.assertEqual(cartagena_2020, date(2020, 11, 16))
+
 
 class MexicoTest(GenericCalendarTest):
     cal_class = Mexico
@@ -245,6 +356,12 @@ def test_holidays_2017(self):
         self.assertNotIn(date(2017, 9, 29), holidays)
         self.assertIn(date(2017, 10, 2), holidays)
 
+    def test_immaculate_conception_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        label = holidays[date(2020, 12, 8)]
+        self.assertEqual(label, "Virgin of Caacupé Day")
+
 
 class BarbadosTest(GenericCalendarTest):
     cal_class = Barbados
diff --git a/calendra/tests/test_asia.py b/calendra/tests/test_asia.py
index 98a8ffe4..faf00e73 100644
--- a/calendra/tests/test_asia.py
+++ b/calendra/tests/test_asia.py
@@ -1,8 +1,11 @@
+from unittest.mock import patch
 from datetime import date
+import time
 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
@@ -60,6 +63,30 @@ def test_year_2020(self):
         self.assertNotIn(date(2020, 9, 27), holidays)  # National Day Shift
         self.assertNotIn(date(2020, 10, 10), holidays)  # National Day Shift
 
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New Year
+        self.assertIn(date(2021, 2, 11), holidays)  # Spring Festival
+        self.assertIn(date(2021, 2, 17), holidays)  # Spring Festival
+        self.assertIn(date(2021, 4, 3), holidays)  # Ching Ming Festival
+        self.assertIn(date(2021, 4, 5), holidays)  # Ching Ming Festival
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day Holiday
+        self.assertIn(date(2021, 5, 5), holidays)  # Labour Day Holiday
+        self.assertIn(date(2021, 6, 12), holidays)  # Dragon Boat Festival
+        self.assertIn(date(2021, 6, 14), holidays)  # Dragon Boat Festival
+        self.assertIn(date(2021, 9, 19), holidays)  # Mid-Autumn Festival
+        self.assertIn(date(2021, 9, 21), holidays)  # Mid-Autumn Festival
+        self.assertIn(date(2021, 10, 1), holidays)  # National Day
+        self.assertIn(date(2021, 10, 7), holidays)  # National Day
+
+        self.assertNotIn(date(2021, 2, 7), holidays)  # Spring Festival Shift
+        self.assertNotIn(date(2021, 2, 20), holidays)  # Spring Festival Shift
+        self.assertNotIn(date(2021, 4, 25), holidays)  # Labour Day Shift
+        self.assertNotIn(date(2021, 5, 8), holidays)  # Labour Day Shift
+        self.assertNotIn(date(2021, 9, 18), holidays)  # Mid-Autumn Shift
+        self.assertNotIn(date(2021, 9, 26), holidays)  # National Day Shift
+        self.assertNotIn(date(2021, 10, 9), holidays)  # National Day Shift
+
     def test_missing_holiday_year(self):
         save_2018 = china_holidays[2018]
         del china_holidays[2018]
@@ -67,6 +94,14 @@ def test_missing_holiday_year(self):
             self.cal.holidays_set(2018)
         china_holidays[2018] = save_2018
 
+    def test_warning(self):
+        year = date.today().year
+        with patch('warnings.warn') as patched:
+            self.cal.get_calendar_holidays(year)
+        patched.assert_called_with(
+            'Support years 2018-2021 currently, need update every year.'
+        )
+
     def test_is_working_day(self):
         # It's a SAT, but it's a working day this year
         self.assertTrue(self.cal.is_working_day(date(2019, 2, 2)))
@@ -95,9 +130,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)
@@ -116,9 +152,11 @@ def test_year_2010(self):
         self.assertIn(date(2010, 10, 1), holidays)   # National Day
         self.assertIn(date(2010, 10, 16), holidays)  # Chung Yeung Festival
         self.assertIn(date(2010, 12, 25), holidays)  # Christmas Day
-        self.assertIn(date(2010, 12, 27), holidays)  # Boxing Day (shifted)
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2010, 12, 27), observed)  # 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
@@ -134,11 +172,13 @@ def test_year_2013(self):
         self.assertIn(date(2013, 7, 1), holidays)    # HK SAR Establishment Day
         self.assertIn(date(2013, 9, 20), holidays)   # Day after Mid-Autumn
         self.assertIn(date(2013, 10, 1), holidays)   # National Day
-        self.assertIn(date(2013, 10, 14), holidays)  # Chung Yeung Festival
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2013, 10, 14), observed)  # Chung Yeung Festival
         self.assertIn(date(2013, 12, 25), holidays)  # Christmas Day
         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
@@ -148,20 +188,24 @@ def test_year_2016(self):
         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, 2), holidays)    # Labour Day (shifted)
+        self.assertIn(date(2016, 5, 1), holidays)    # Labour Day (SUN)
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2016, 5, 2), observed)    # Labour Day (shifted)
         self.assertIn(date(2016, 5, 14), holidays)   # Buddha's Birthday
         self.assertIn(date(2016, 6, 9), holidays)    # Tuen Ng Festival
         self.assertIn(date(2016, 7, 1), holidays)    # HK SAR Establishment Day
         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
-        observed = set(map(self.cal.get_observed_date, holidays))
-        self.assertIn(date(2016, 12, 26), observed)  # Christmas Day (shifted)
-        self.assertIn(date(2016, 12, 27), observed)  # Boxing Day (shifted)
+        self.assertIn(date(2016, 10, 10), observed)  # Chung Yeung Festival
+        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)
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2017, 1, 2), observed)    # New Year (shifted)
         self.assertIn(date(2017, 1, 28), holidays)   # Chinese new year
         self.assertIn(date(2017, 1, 30), holidays)   # Chinese new year
         self.assertIn(date(2017, 1, 31), holidays)   # Chinese new year
@@ -173,7 +217,7 @@ def test_year_2017(self):
         self.assertIn(date(2017, 5, 3), holidays)    # Buddha's Birthday
         self.assertIn(date(2017, 5, 30), holidays)   # Tuen Ng Festival
         self.assertIn(date(2017, 7, 1), holidays)    # HK SAR Establishment Day
-        self.assertIn(date(2017, 10, 2), holidays)   # National Day (shifted)
+        self.assertIn(date(2017, 10, 2), observed)   # National Day (shifted)
         self.assertIn(date(2017, 10, 5), holidays)   # Day after Mid-Autumn
         self.assertIn(date(2017, 10, 28), holidays)  # Chung Yeung Festival
         self.assertIn(date(2017, 12, 25), holidays)  # Christmas Day
@@ -196,6 +240,82 @@ def test_chingming_festival(self):
         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
+        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
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2020, 10, 26), observed)  # 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_holidays_2021(self):
+        # https://www.gov.hk/en/about/abouthk/holiday/2021.htm
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)    # New Year
+        self.assertIn(date(2021, 2, 12), holidays)   # Chinese new year
+        self.assertIn(date(2021, 2, 13), holidays)   # Chinese new year
+        self.assertIn(date(2021, 2, 14), holidays)   # Chinese new year (SUN)
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2021, 2, 15), observed)   # Chinese new year
+        self.assertIn(date(2021, 4, 2), holidays)    # Good Friday
+        self.assertIn(date(2021, 4, 3), holidays)    # Day after Good Friday
+        self.assertIn(date(2021, 4, 4), holidays)    # Ching Ming
+        self.assertIn(date(2021, 4, 5), holidays)    # Ching Ming shift+Easter
+        self.assertIn(date(2021, 4, 6), holidays)    # Day after Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)    # Labour Day
+        self.assertIn(date(2021, 5, 19), holidays)   # Buddha's Birthday
+        self.assertIn(date(2021, 6, 14), holidays)   # Tuen Ng Festival
+        self.assertIn(date(2021, 7, 1), holidays)    # HK SAR Establishment Day
+        self.assertIn(date(2021, 9, 22), holidays)   # Day after Mid-Autumn
+        self.assertIn(date(2021, 10, 1), holidays)   # National Day
+        self.assertIn(date(2021, 10, 14), observed)  # Chung Yeung Festival
+        self.assertIn(date(2021, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2021, 12, 27), observed)  # First weekday after Xmas
+
+    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)
+        )
+
+    def test_no_duplicate_days(self):
+        holidays = self.cal.holidays(2021)
+        labels = list(day[1] for day in holidays)
+        labels_dedup = list(set(labels))
+        assert sorted(labels) == sorted(labels_dedup)
+
+
+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):
 
@@ -347,9 +467,16 @@ def test_missing_thaipusam(self):
         # Back to normal, to avoid breaking further tests
         self.cal.MSIA_THAIPUSAM[2020] = save_2020
 
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "Workers' Day")
+
 
 class QatarTest(GenericCalendarTest):
     cal_class = Qatar
+    test_include_january_1st = False
 
     def test_year_2013(self):
         holidays = self.cal.holidays_set(2013)
@@ -394,7 +521,8 @@ def test_year_2013(self):
         self.assertIn(date(2013, 8, 9), holidays)  # National Day
         self.assertIn(date(2013, 10, 15), holidays)  # Hari Raya Haji
         self.assertIn(date(2013, 11, 3), holidays)  # Deepavali
-        self.assertIn(date(2013, 11, 4), holidays)  # Deepavali shift
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2013, 11, 4), observed)  # Deepavali shift
         self.assertIn(date(2013, 12, 25), holidays)  # Christmas Day
 
     def test_year_2018(self):
@@ -417,7 +545,8 @@ def test_fixed_holiday_shift(self):
         # Labour Day (sunday)
         self.assertIn(date(2016, 5, 1), holidays)
         # Shifted day (Monday)
-        self.assertIn(date(2016, 5, 2), holidays)
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2016, 5, 2), observed)
 
     def test_deepavali(self):
         # At the moment, we have values for deepavali only until year 2021
@@ -430,6 +559,15 @@ def test_deepavali_current_year(self):
         current_year = date.today().year
         self.assertIn(current_year, self.cal.DEEPAVALI)
 
+    def test_deepavali_missing_year(self):
+        with self.assertRaises(KeyError) as context:
+            self.cal.holidays_set(1999)
+        self.assertEqual(
+            # Equivalent of the error msg
+            context.exception.args[0],
+            'Missing date for Singapore Deepavali for year: 1999',
+        )
+
 
 class SouthKoreaTest(GenericCalendarTest):
 
@@ -490,69 +628,132 @@ def test_qingming_festival(self):
 class IsraelTest(GenericCalendarTest):
 
     cal_class = Israel
+    test_include_january_1st = False
 
     def test_holidays_2017(self):
-        holidays = self.cal.holidays_set(2017)
-
-        self.assertIn(date(2017, 4, 11), holidays)  # Passover (Pesach)
-        self.assertIn(date(2017, 4, 17), holidays)  # Passover (Pesach)
-        self.assertIn(
-            date(2017, 5, 2), holidays
-        )  # Independence Day (Yom Ha-Atzmaut), was early in 2017
-        self.assertIn(date(2017, 5, 31), holidays)  # Shavuot
-        self.assertIn(
-            date(2017, 9, 21), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(
-            date(2017, 9, 22), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(
-            date(2017, 9, 30), holidays
-        )  # Yom Kippur (already a Saturday - Shabbat, weekend day)
-        self.assertIn(date(2017, 10, 5), holidays)  # Sukkot
-        self.assertIn(date(2017, 10, 12), holidays)  # Sukkot
+        calculated_holidays = self.cal.holidays_set(2017)
+        known_holidays = {
+            date(2017, 4, 10),  # Passover (Pesach)
+            date(2017, 4, 11),
+            date(2017, 4, 16),
+            date(2017, 4, 17),
+            date(2017, 5, 1),  # Independence Day (Yom Ha-Atzmaut)
+            date(2017, 5, 2),
+            date(2017, 9, 20),  # Jewish New Year (Rosh Ha-Shana)
+            date(2017, 9, 21),
+            date(2017, 9, 22),
+            date(2017, 9, 29),  # Yom Kippur
+            date(2017, 9, 30),
+            date(2017, 10, 4),  # Sukkot
+            date(2017, 10, 5),
+            date(2017, 10, 11),
+            date(2017, 10, 12),
+            date(2017, 5, 30),  # Shavuot
+            date(2017, 5, 31),
+        }
+        self.assertEqual(calculated_holidays, known_holidays)
 
     def test_holidays_2018(self):
-        holidays = self.cal.holidays_set(2018)
-
-        self.assertIn(date(2018, 3, 31), holidays)  # Passover (Pesach)
-        self.assertIn(date(2018, 4, 6), holidays)  # Passover (Pesach)
-        self.assertIn(
-            date(2018, 4, 19), holidays
-        )  # Independence Day (Yom Ha-Atzmaut), was delayed in 2018
-        self.assertIn(date(2018, 5, 20), holidays)  # Shavuot
-        self.assertIn(
-            date(2018, 9, 10), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(
-            date(2018, 9, 11), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(date(2018, 9, 19), holidays)  # Yom Kippur
-        self.assertIn(date(2018, 9, 24), holidays)  # Sukkot
-        self.assertIn(date(2018, 9, 30), holidays)  # Sukkot
+        calculated_holidays = self.cal.holidays_set(2018)
+        known_holidays = {
+            date(2018, 3, 30),  # Passover (Pesach)
+            date(2018, 3, 31),
+            date(2018, 4, 5),
+            date(2018, 4, 6),
+            date(2018, 4, 18),  # Independence Day (Yom Ha-Atzmaut)
+            date(2018, 4, 19),
+            date(2018, 9, 9),  # Rosh Hashana
+            date(2018, 9, 10),
+            date(2018, 9, 11),
+            date(2018, 9, 18),  # Yom Kippur
+            date(2018, 9, 19),
+            date(2018, 9, 23),  # Sukkot
+            date(2018, 9, 24),
+            date(2018, 9, 30),
+            date(2018, 10, 1),
+            date(2018, 5, 19),  # Shavuot
+            date(2018, 5, 20),
+        }
+        self.assertEqual(calculated_holidays, known_holidays)
 
     def test_holidays_2019(self):
-        holidays = self.cal.holidays_set(2019)
-
-        self.assertIn(date(2019, 4, 20), holidays)  # Passover (Pesach)
-        self.assertIn(date(2019, 4, 26), holidays)  # Passover (Pesach)
-        self.assertIn(
-            date(2019, 5, 9), holidays
-        )  # Independence Day (Yom Ha-Atzmaut), was delayed in 2019
-        self.assertIn(date(2019, 6, 9), holidays)  # Shavuot
-        self.assertIn(
-            date(2019, 9, 30), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(
-            date(2019, 10, 1), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(
-            date(2019, 10, 2), holidays
-        )  # Jewish New Year (Rosh Ha-Shana)
-        self.assertIn(date(2019, 10, 9), holidays)  # Yom Kippur
-        self.assertIn(date(2019, 10, 14), holidays)  # Sukkot
-        self.assertIn(date(2019, 10, 20), holidays)  # Sukkot
-
-        # Leap year Purim
-        self.assertIn(date(2019, 3, 21), holidays)  # purim
-        self.assertIn(date(2019, 3, 22), holidays)  # shushan purim
+        calculated_holidays = self.cal.holidays_set(2019)
+        known_holidays = {
+            date(2019, 4, 19),  # Passover (Pesach)
+            date(2019, 4, 20),  # Passover (Pesach)
+            date(2019, 4, 25),  # Passover (Pesach)
+            date(2019, 4, 26),  # Passover (Pesach)
+            date(2019, 5, 8),  # Independence Day (Yom Ha-Atzmaut)
+            date(2019, 5, 9),  # Independence Day (Yom Ha-Atzmaut)
+            date(2019, 6, 8),  # Shavuot
+            date(2019, 6, 9),  # Shavuot
+            date(2019, 9, 29),  # Rosh Hashana
+            date(2019, 9, 30),  # Rosh Hashana
+            date(2019, 10, 1),  # Rosh Hashana
+            date(2019, 10, 8),  # Yom Kippur
+            date(2019, 10, 9),  # Yom Kippur
+            date(2019, 10, 13),  # Sukkot
+            date(2019, 10, 14),  # Sukkot
+            date(2019, 10, 20),  # Sukkot
+            date(2019, 10, 21),  # Sukkot
+        }
+        self.assertEqual(calculated_holidays, known_holidays)
+
+    def test_holidays_2020(self):
+        calculated_holidays = self.cal.holidays_set(2020)
+        known_holidays = {
+            date(2020, 9, 18),  # Rosh Hashana Eve
+            date(2020, 9, 19),  # Rosh Hashana A
+            date(2020, 9, 20),  # Rosh Hashana B
+            date(2020, 9, 27),  # Kippur Eve
+            date(2020, 9, 28),  # Kippur
+            date(2020, 10, 2),  # Sukot A Eve
+            date(2020, 10, 3),  # Sukot A
+            date(2020, 10, 9),  # Sukot B Eve
+            date(2020, 10, 10),  # Sukot B
+            date(2020, 4, 8),  # Pesach A Eve
+            date(2020, 4, 9),  # Pesach A
+            date(2020, 4, 14),  # Pesach B Eve
+            date(2020, 4, 15),  # Pesach B
+            date(2020, 4, 28),  # Independence Day Eve
+            date(2020, 4, 29),  # Independence Day
+            date(2020, 5, 28),  # Shavuot Eve
+            date(2020, 5, 29),  # Shavuot
+        }
+        self.assertEqual(calculated_holidays, known_holidays)
+
+    def test_holidays_2021(self):
+        calculated_holidays = self.cal.holidays_set(2021)
+        known_holidays = {
+            date(2021, 9, 6),  # Rosh Hashana Eve
+            date(2021, 9, 7),  # Rosh Hashana A
+            date(2021, 9, 8),  # Rosh Hashana B
+            date(2021, 9, 15),  # Kippur Eve
+            date(2021, 9, 16),  # Kippur
+            date(2021, 9, 20),  # Sukot A Eve
+            date(2021, 9, 21),  # Sukot A
+            date(2021, 9, 27),  # Sukot B Eve
+            date(2021, 9, 28),  # Sukot B
+            date(2021, 3, 27),  # Pesach A Eve
+            date(2021, 3, 28),  # Pesach A
+            date(2021, 4, 2),  # Pesach B Eve
+            date(2021, 4, 3),  # Pesach B
+            date(2021, 4, 14),  # Independence Day Eve
+            date(2021, 4, 15),  # Independence Day
+            date(2021, 5, 16),  # Shavuot Eve
+            date(2021, 5, 17),  # Shavuot
+        }
+        self.assertEqual(calculated_holidays, known_holidays)
+
+    def test_is_holiday_performance(self):
+        random_date = date(2019, 10, 9)
+        japan_cal = Japan()
+        timer = time.time()
+        for i in range(30):
+            japan_cal.is_holiday(random_date)
+        japan_time = time.time() - timer
+        timer = time.time()
+        for i in range(30):
+            self.cal.is_holiday(random_date)
+        israel_time = time.time() - timer
+        self.assertGreater(japan_time * 3, israel_time)
diff --git a/calendra/tests/test_brazil.py b/calendra/tests/test_brazil.py
index ba85f710..d708eaca 100644
--- a/calendra/tests/test_brazil.py
+++ b/calendra/tests/test_brazil.py
@@ -30,6 +30,8 @@
 
 class BrazilTest(GenericCalendarTest):
     cal_class = Brazil
+    test_include_consciencia_negra = False
+    test_include_immaculate_conception = False
 
     def test_year_2013_federal(self):
         holidays = self.cal.holidays_set(2013)
@@ -42,6 +44,37 @@ def test_year_2013_federal(self):
         self.assertIn(date(2013, 11, 15), holidays)  # Proclamação da República
         self.assertIn(date(2013, 12, 25), holidays)  # Natal
 
+    def test_consciencia_negra(self):
+        # Consciência Negra day is not a national holiday
+        # It's triggered in the appropriate classes, so this test needs to
+        # be overwritten.
+        month, day = self.cal.consciencia_negra_day
+        consciencia_negra_day = date(self.year, month, day)
+        holidays = self.cal.holidays_set(self.year)
+        if self.test_include_consciencia_negra:
+            # Included where needed
+            self.assertIn(consciencia_negra_day, holidays)
+        else:
+            # By default, not in the holidays.
+            self.assertNotIn(consciencia_negra_day, holidays)
+
+    def test_immaculate_conception(self):
+        # Immaculate Conception is not a national holiday
+        # It's triggered in the appropriate classes, so this test needs to
+        # be overwritten.
+        immaculate_conception_day = date(self.year, 12, 8)
+        holidays = dict(self.cal.holidays(self.year))
+        if self.test_include_immaculate_conception:
+            # Included where needed
+            self.assertIn(immaculate_conception_day, holidays)
+            # Test its label
+            self.assertEqual(
+                holidays[immaculate_conception_day],
+                "Dia de Nossa Senhora da Conceição")
+        else:
+            # By default, not in the holidays.
+            self.assertNotIn(immaculate_conception_day, holidays)
+
 
 class BrazilAcreTest(BrazilTest):
     cal_class = BrazilAcre
@@ -59,6 +92,7 @@ def test_year_2017_state(self):
 
 class BrazilAlagoasTest(BrazilTest):
     cal_class = BrazilAlagoas
+    test_include_consciencia_negra = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -71,6 +105,7 @@ def test_year_2017_state(self):
 
 class BrazilAmapaTest(BrazilTest):
     cal_class = BrazilAmapa
+    test_include_consciencia_negra = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -89,6 +124,8 @@ def test_year_2017_state(self):
 
 class BrazilAmazonasTest(BrazilTest):
     cal_class = BrazilAmazonas
+    test_include_consciencia_negra = True
+    test_include_immaculate_conception = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -97,6 +134,8 @@ def test_year_2017_state(self):
         self.assertIn(date(2017, 11, 20), holidays)  # Consciência Negra
         # Dia de Nossa Senhora da Conceição
         self.assertIn(date(2017, 12, 8), holidays)
+        # Label test
+        holidays = self.cal.holidays(2017)
 
 
 class BrazilBahiaTest(BrazilTest):
@@ -144,6 +183,7 @@ def test_year_2017_state(self):
 
 class BrazilMaranhaoTest(BrazilTest):
     cal_class = BrazilMaranhao
+    test_include_immaculate_conception = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -164,6 +204,7 @@ def test_year_2017_state(self):
 
 class BrazilMatoGrossoTest(BrazilTest):
     cal_class = BrazilMatoGrosso
+    test_include_consciencia_negra = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -180,6 +221,7 @@ def test_year_2017_state(self):
 
 class BrazilParaTest(BrazilTest):
     cal_class = BrazilPara
+    test_include_immaculate_conception = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -226,6 +268,8 @@ def test_year_2017_state(self):
 
 class BrazilRioDeJaneiroTest(BrazilTest):
     cal_class = BrazilRioDeJaneiro
+    test_include_consciencia_negra = True
+    test_include_immaculate_conception = True
 
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
@@ -246,6 +290,12 @@ def test_year_2017_state(self):
         # Dia de Nossa Senhora da Conceição
         self.assertIn(date(2017, 12, 8), holidays)
 
+    def test_carnaval_label(self):
+        holidays = self.cal.holidays(2017)
+        holidays_dict = dict(holidays)
+        label_carnaval = holidays_dict[date(2017, 2, 28)]
+        self.assertEqual(label_carnaval, "Carnaval")
+
 
 class BrazilRioGrandeDoNorteTest(BrazilTest):
     cal_class = BrazilRioGrandeDoNorte
@@ -312,6 +362,7 @@ def test_year_2013_state(self):
 
 class SaoPauloCityTest(SaoPauloStateTest):
     cal_class = BrazilSaoPauloCity
+    test_include_consciencia_negra = True
 
     def test_year_2013_city(self):
         holidays = self.cal.holidays_set(2013)
@@ -338,6 +389,12 @@ def test_year_2013_city(self):
             "Sexta-feira da Paixão",
         )
 
+    def test_carnaval_label(self):
+        holidays = self.cal.holidays(2013)
+        holidays_dict = dict(holidays)
+        label_carnaval = holidays_dict[date(2013, 2, 12)]
+        self.assertEqual(label_carnaval, "Carnaval")
+
 
 class BrazilSergipeTest(BrazilTest):
     cal_class = BrazilSergipe
@@ -437,6 +494,8 @@ class BrazilGuarapariCityTest(BrazilEspiritoSantoTest):
     Guarapari city is in the Espírito Santo state
     """
     cal_class = BrazilGuarapariCity
+    test_include_consciencia_negra = True
+    test_include_immaculate_conception = True
 
     def test_year_2017_city(self):
         holidays = self.cal.holidays_set(2017)
@@ -451,6 +510,7 @@ class BrazilSerraCityTest(BrazilEspiritoSantoTest):
     Serra city is in the Espírito Santo state
     """
     cal_class = BrazilSerraCity
+    test_include_immaculate_conception = True
 
     def test_year_2017_city(self):
         holidays = self.cal.holidays_set(2017)
@@ -481,6 +541,12 @@ def test_year_2017_city(self):
             "Paixão do Cristo",
         )
 
+    def test_carnaval_label(self):
+        holidays = self.cal.holidays(2017)
+        holidays_dict = dict(holidays)
+        label_carnaval = holidays_dict[date(2017, 2, 28)]
+        self.assertEqual(label_carnaval, "Carnaval")
+
 
 class BrazilRioBrancoCityTest(BrazilAcreTest):
     """
@@ -815,6 +881,7 @@ def test_year_2017_holidays(self):
         self.assertIn(date(2017, 1, 1), holidays)  # New year
         self.assertIn(date(2017, 2, 27), holidays)  # Monday carnaval
         self.assertIn(date(2017, 2, 28), holidays)  # Tuesday carnaval
+        self.assertIn(date(2017, 3, 1), holidays)  # Ash wednesday
         self.assertIn(date(2017, 4, 14), holidays)  # Good friday
         self.assertIn(date(2017, 4, 21), holidays)  # Tiradentes
         self.assertIn(date(2017, 5, 1), holidays)  # Labour day
@@ -824,77 +891,98 @@ def test_year_2017_holidays(self):
         self.assertIn(date(2017, 11, 2), holidays)  # All Souls' Day
         self.assertIn(date(2017, 11, 15), holidays)  # Republic day
         self.assertIn(date(2017, 12, 25), holidays)  # Christmas Day
+        self.assertIn(date(2017, 12, 29), holidays)  # Last working day of year
+        self.assertEqual(14, len(holidays))
 
     def test_year_2017_find_next_working_day_for_new_year(self):
         new_year = date(2017, 1, 1)
         working_day = self.cal.find_following_working_day(new_year)
-        self.assertEquals(working_day, date(2017, 1, 2))
+        self.assertEqual(working_day, date(2017, 1, 2))
 
     def test_year_2017_find_next_working_day_for_monday_carnaval(self):
         monday_carnaval = date(2017, 2, 27)
         working_day = self.cal.find_following_working_day(monday_carnaval)
-        self.assertEquals(working_day, date(2017, 3, 2))
+        self.assertEqual(working_day, date(2017, 3, 2))
 
     def test_year_2017_find_next_working_day_for_tuesday_carnaval(self):
         tuesday_carnaval = date(2017, 2, 28)
         working_day = self.cal.find_following_working_day(tuesday_carnaval)
-        self.assertEquals(working_day, date(2017, 3, 2))
+        self.assertEqual(working_day, date(2017, 3, 2))
 
     def test_year_2017_find_next_working_day_for_good_friday(self):
         good_friday = date(2017, 4, 14)
         working_day = self.cal.find_following_working_day(good_friday)
-        self.assertEquals(working_day, date(2017, 4, 17))
+        self.assertEqual(working_day, date(2017, 4, 17))
 
     def test_year_2017_find_next_working_day_for_tiradentes(self):
         tiradentes = date(2017, 4, 21)
         working_day = self.cal.find_following_working_day(tiradentes)
-        self.assertEquals(working_day, date(2017, 4, 24))
+        self.assertEqual(working_day, date(2017, 4, 24))
 
     def test_year_2017_find_next_working_day_for_labour_day(self):
         labour_day = date(2017, 5, 1)
         working_day = self.cal.find_following_working_day(labour_day)
-        self.assertEquals(working_day, date(2017, 5, 2))
+        self.assertEqual(working_day, date(2017, 5, 2))
 
     def test_year_2017_find_next_working_day_for_corpus_christi(self):
         corpus_christi = date(2017, 6, 15)
         working_day = self.cal.find_following_working_day(corpus_christi)
-        self.assertEquals(working_day, date(2017, 6, 16))
+        self.assertEqual(working_day, date(2017, 6, 16))
 
     def test_year_2017_find_next_working_day_for_independence_day(self):
         independence_day = date(2017, 9, 7)
         working_day = self.cal.find_following_working_day(independence_day)
-        self.assertEquals(working_day, date(2017, 9, 8))
+        self.assertEqual(working_day, date(2017, 9, 8))
 
     def test_year_2017_find_next_working_day_for_our_lady_aparecida(self):
         our_lady_aparecida = date(2017, 10, 12)
         working_day = self.cal.find_following_working_day(our_lady_aparecida)
-        self.assertEquals(working_day, date(2017, 10, 13))
+        self.assertEqual(working_day, date(2017, 10, 13))
 
     def test_year_2017_find_next_working_day_for_our_all_souls(self):
         all_souls = date(2017, 11, 2)
         working_day = self.cal.find_following_working_day(all_souls)
-        self.assertEquals(working_day, date(2017, 11, 3))
+        self.assertEqual(working_day, date(2017, 11, 3))
 
     def test_year_2017_find_next_working_day_for_our_republic_day(self):
         republic_day = date(2017, 11, 15)
         working_day = self.cal.find_following_working_day(republic_day)
-        self.assertEquals(working_day, date(2017, 11, 16))
+        self.assertEqual(working_day, date(2017, 11, 16))
 
     def test_year_2017_find_next_working_day_for_our_christmas_day(self):
         christmas_day = date(2017, 12, 25)
         working_day = self.cal.find_following_working_day(christmas_day)
-        self.assertEquals(working_day, date(2017, 12, 26))
+        self.assertEqual(working_day, date(2017, 12, 26))
 
     def test_year_2017_find_next_working_day_for_last_day(self):
         # last day of year for only internal bank transactions
         last_day = date(2017, 12, 29)
         working_day = self.cal.find_following_working_day(last_day)
-        self.assertEquals(working_day, date(2018, 1, 2))
+        self.assertEqual(working_day, date(2018, 1, 2))
 
     def test_year_2017_find_next_working_day_for_already_working_day(self):
         already_working_day = date(2017, 7, 25)
         working_day = self.cal.find_following_working_day(already_working_day)
-        self.assertEquals(working_day, date(2017, 7, 25))
+        self.assertEqual(working_day, date(2017, 7, 25))
+
+    def test_carnaval_label(self):
+        holidays = self.cal.holidays(2017)
+        holidays_dict = dict(holidays)
+        label_carnaval = holidays_dict[date(2017, 2, 28)]
+        self.assertEqual(label_carnaval, "Tuesday carnaval")
+
+    def test_shift_last_day_of_the_year(self):
+        # New Year's eve is on SAT
+        holidays = self.cal.holidays_set(2022)
+        # Shifted to the FRI before
+        self.assertIn(date(2022, 12, 30), holidays)
+        self.assertNotIn(date(2022, 12, 29), holidays)
+
+        # New Year's eve is on SUN
+        holidays = self.cal.holidays_set(2017)
+        # Shifted to the FRI before
+        self.assertNotIn(date(2017, 12, 30), holidays)
+        self.assertIn(date(2017, 12, 29), holidays)
 
 
 class TestIBGERegister(TestCase):
diff --git a/calendra/tests/test_canada.py b/calendra/tests/test_canada.py
index 4d544567..0985873f 100644
--- a/calendra/tests/test_canada.py
+++ b/calendra/tests/test_canada.py
@@ -1,5 +1,6 @@
 from datetime import date
 from . import GenericCalendarTest
+from ..core import MON
 from ..america.canada import (
     Canada, Ontario, Quebec, BritishColumbia, Alberta, Saskatchewan, Manitoba,
     NewBrunswick, NovaScotia, PrinceEdwardIsland, Newfoundland, Yukon,
@@ -104,7 +105,9 @@ def test_holidays_2012(self):
         holidays = self.cal.holidays_set(2012)
         observed = set(map(self.cal.get_observed_date, holidays))
         self.assertIn(date(2012, 1, 2), observed)
-        self.assertIn(date(2012, 2, 13), holidays)  # Family Day BC
+        # Family Day BC was not set in 2012
+        self.assertNotIn(date(2012, 2, 13), holidays)
+
         self.assertIn(date(2012, 4, 6), holidays)  # Good Friday
         self.assertNotIn(date(2012, 4, 9), holidays)  # Easter Monday
         self.assertIn(date(2012, 5, 21), holidays)  # Victoria Day
@@ -115,6 +118,21 @@ def test_holidays_2012(self):
         self.assertIn(date(2012, 11, 11), holidays)  # Remembrance Day
         self.assertIn(date(2012, 12, 25), holidays)  # Christmas day
 
+    def test_family_day(self):
+        # From 2013 to 2018, Family Day was on 2nd MON of February
+        for year in range(2013, 2019):
+            holidays = dict(self.cal.holidays(year))
+            day = self.cal.get_nth_weekday_in_month(year, 2, MON, 2)
+            self.assertIn(day, holidays)
+            self.assertEqual(holidays[day], "Family Day")
+
+        # As of 2019, it happens on 3rd MON of February
+        for year in (2019, 2020, 2021):
+            holidays = dict(self.cal.holidays(year))
+            day = self.cal.get_nth_weekday_in_month(year, 2, MON, 3)
+            self.assertIn(day, holidays)
+            self.assertEqual(holidays[day], "Family Day")
+
 
 class AlbertaTest(GenericCalendarTest):
     cal_class = Alberta
@@ -321,3 +339,14 @@ def test_holidays_2012(self):
         self.assertIn(date(2012, 11, 12), holidays)  # Remembrance Day Shift
         self.assertIn(date(2012, 12, 25), holidays)  # Christmas day
         self.assertNotIn(date(2012, 12, 26), holidays)  # Boxing day
+
+    def test_nunavut_shift(self):
+        # Nunavut day happens on SAT, no shift
+        holidays = self.cal.holidays_set(2016)
+        self.assertIn(date(2016, 7, 9), holidays)
+        self.assertNotIn(date(2016, 7, 10), holidays)
+
+        # Nunavut day happens on SUN, shift to the next day
+        holidays = self.cal.holidays_set(2017)
+        self.assertIn(date(2017, 7, 9), holidays)
+        self.assertIn(date(2017, 7, 10), holidays)
diff --git a/calendra/tests/test_core.py b/calendra/tests/test_core.py
index c0fd4394..db5e9bce 100644
--- a/calendra/tests/test_core.py
+++ b/calendra/tests/test_core.py
@@ -1,21 +1,23 @@
 from datetime import date
 from datetime import datetime
 from unittest import TestCase
+from unittest.mock import patch
 
 import dateutil.relativedelta as rd
 import pandas
 
-from . import GenericCalendarTest
+from . import CoreCalendarTest
+from ..core import Holiday
 from ..core import (
     MON, TUE, THU, FRI, WED, SAT, SUN,
-    Calendar, LunarCalendar, WesternCalendar,
-    IslamicMixin, JalaliMixin, ChristianMixin
+    Calendar, LunarMixin, WesternCalendar,
+    CalverterMixin, IslamicMixin, JalaliMixin,
+    daterange,
 )
-from ..core import Holiday
-from ..exceptions import UnsupportedDateType
+from ..exceptions import UnsupportedDateType, CalendarError
 
 
-class CalendarTest(GenericCalendarTest):
+class CalendarTest(CoreCalendarTest):
 
     def test_private_variables(self):
         self.assertTrue(hasattr(self.cal, '_holidays'))
@@ -31,12 +33,12 @@ def test_private_variables(self):
     def test_year(self):
         holidays = self.cal.holidays()
         self.assertTrue(isinstance(holidays, (tuple, list)))
-        self.assertEquals(self.cal._holidays[self.year], holidays)
+        self.assertEqual(self.cal._holidays[self.year], holidays)
 
     def test_another_year(self):
         holidays = self.cal.holidays(2011)
         self.assertTrue(isinstance(holidays, (tuple, list)))
-        self.assertEquals(self.cal._holidays[2011], holidays)
+        self.assertEqual(self.cal._holidays[2011], holidays)
 
     def test_is_working_day(self):
         self.assertRaises(
@@ -45,23 +47,23 @@ def test_is_working_day(self):
 
     def test_nth_weekday(self):
         # first monday in january 2013
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_nth_weekday_in_month(2013, 1, MON),
             date(2013, 1, 7)
         )
         # second monday in january 2013
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_nth_weekday_in_month(2013, 1, MON, 2),
             date(2013, 1, 14)
         )
         # let's test the limits
         # Jan 1st is a TUE
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_nth_weekday_in_month(2013, 1, TUE),
             date(2013, 1, 1)
         )
         # There's no 6th MONday
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_nth_weekday_in_month(2013, 1, MON, 6),
             None
         )
@@ -69,49 +71,48 @@ def test_nth_weekday(self):
     def test_nth_weekday_start(self):
         # first thursday after 18th april
         start = date(2013, 4, 18)
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_nth_weekday_in_month(2013, 4, THU, start=start),
             date(2013, 4, 18)
         )
         # first friday after 18th april
         start = date(2013, 4, 18)
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_nth_weekday_in_month(2013, 4, FRI, start=start),
             date(2013, 4, 19)
         )
 
     def test_last_weekday(self):
         # last monday in january 2013
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_last_weekday_in_month(2013, 1, MON),
             date(2013, 1, 28)
         )
         # last thursday
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_last_weekday_in_month(2013, 1, THU),
             date(2013, 1, 31)
         )
 
     def test_get_next_weekday_after(self):
         # the first monday after Apr 1 2015
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_first_weekday_after(date(2015, 4, 1), MON),
             date(2015, 4, 6)
         )
 
         # the first tuesday after Apr 14 2015
-        self.assertEquals(
+        self.assertEqual(
             Calendar.get_first_weekday_after(date(2015, 4, 14), TUE),
             date(2015, 4, 14)
         )
 
 
-class LunarCalendarTest(GenericCalendarTest):
-    cal_class = LunarCalendar
+class LunarCalendarTest(TestCase):
 
-    def test_new_year(self):
-        self.assertEquals(
-            self.cal.lunar(2014, 1, 1),
+    def test_lunar_new_year(self):
+        self.assertEqual(
+            LunarMixin.lunar(2014, 1, 1),
             date(2014, 1, 31)
         )
 
@@ -128,7 +129,7 @@ def get_weekend_days(self):
         return []  # no week-end, yes, it's sad
 
 
-class MockCalendarTest(GenericCalendarTest):
+class MockCalendarTest(CoreCalendarTest):
     cal_class = MockCalendar
 
     def test_holidays_set(self):
@@ -147,26 +148,26 @@ def test_sorted_dates(self):
 
     def test_add_workingdays_simple(self):
         # day is out of non-working-day
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(date(self.year, 12, 20), 0),
             date(self.year, 12, 20)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(date(self.year, 12, 20), 1),
             date(self.year, 12, 21)
         )
 
     def test_add_workingdays_on_holiday(self):
         # day is in holidays
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(date(self.year, 12, 25), 0),
             date(self.year, 12, 25)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(date(self.year, 12, 24), 1),
             date(self.year, 12, 26)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(date(self.year, 12, 24), 2),
             date(self.year, 12, 27)
         )
@@ -174,7 +175,7 @@ def test_add_workingdays_on_holiday(self):
     def test_add_workingdays_span(self):
         day = date(self.year, 12, 20)
         # since this calendar has no weekends, we'll just have a 2-day-shift
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(day, 20),
             date(self.year + 1, 1, 11)
         )
@@ -184,12 +185,12 @@ def test_add_working_days_exceptions(self):
         christmas = date(self.year, 12, 25)
         boxing = date(self.year, 12, 26)
         # exceptional workday
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(day, 20, extra_working_days=[christmas]),
             date(self.year + 1, 1, 10)
         )
         # exceptional holiday + exceptional workday
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(day, 20,
                                       extra_working_days=[christmas],
                                       extra_holidays=[boxing]),
@@ -226,16 +227,16 @@ def test_get_holiday_label(self):
     def test_add_working_days_backwards(self):
         day = date(self.year, 1, 3)
         # since this calendar has no weekends, we'll just have a 1-day-shift
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(day, -7),
             date(self.year - 1, 12, 26)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.sub_working_days(day, 7),
             date(self.year - 1, 12, 26)
         )
         # Negative argument to sub_working_days -> converted to positive.
-        self.assertEquals(
+        self.assertEqual(
             self.cal.sub_working_days(day, -7),
             date(self.year - 1, 12, 26)
         )
@@ -247,6 +248,8 @@ class SimpleObservanceCalendar(Calendar):
     If a holiday falls on a weekend, then its observance is shifted to a
     nearby weekday.
     """
+    include_new_years_day = False
+
     FIXED_HOLIDAYS = (
         Holiday(
             date(2000, 12, 24), 'Christmas Eve', indication='December 24th',
@@ -259,7 +262,7 @@ def get_weekend_days(self):
         return SAT, SUN
 
 
-class ObservanceCalendarTest(GenericCalendarTest):
+class ObservanceCalendarTest(CoreCalendarTest):
     """
     A simple calendar with days shifted for observance.
     """
@@ -279,27 +282,65 @@ def test_observance(self):
         assert self.cal.get_observed_date(xmas_day) == date(2011, 12, 26)
 
 
-class IslamicMixinTest(GenericCalendarTest):
+class IslamicMixinTest(CoreCalendarTest):
     cal_class = IslamicMixin
 
     def test_year_conversion(self):
         days = self.cal.converted(2013)
-        self.assertEquals(len(days), 365)
+        self.assertEqual(len(days), 365)
 
 
-class JalaliMixinTest(GenericCalendarTest):
+class JalaliMixinTest(CoreCalendarTest):
     cal_class = JalaliMixin
 
     def test_year_conversion(self):
         days = self.cal.converted(2013)
-        self.assertEquals(len(days), 365)
+        self.assertEqual(len(days), 365)
 
 
-class MockChristianCalendar(WesternCalendar, ChristianMixin):
+class CalverterClassNoConversionMethod(CalverterMixin):
     pass
 
 
-class MockChristianCalendarTest(GenericCalendarTest):
+class NoConversionMethodTest(TestCase):
+    def test_no_conversion_method(self):
+        with self.assertRaises(NotImplementedError):
+            CalverterClassNoConversionMethod()
+
+
+class IncludeLaylatAlQadr(IslamicMixin):
+    include_laylat_al_qadr = True
+
+
+class DoesNotIncludeLaylatAlQadr(IslamicMixin):
+    include_laylat_al_qadr = False
+
+
+class LaylatAlQadrTest(TestCase):
+
+    def test_warning_laylat_al_qadr(self):
+        cal = IncludeLaylatAlQadr()
+        with patch('warnings.warn') as patched:
+            cal.get_islamic_holidays()
+        patched.assert_called_with(
+            'The Islamic holiday named Laylat al-Qadr is decided by the '
+            'religious authorities. It is not possible to compute it. '
+            "You'll have to add it manually."
+        )
+
+    def test_no_warning_laylat_al_qadr(self):
+        cal = DoesNotIncludeLaylatAlQadr()
+        with patch('warnings.warn') as patched:
+            cal.get_islamic_holidays()
+        patched.assert_not_called()
+
+
+class MockChristianCalendar(WesternCalendar):
+    # WesternCalendar inherits from ChristianMixin
+    pass
+
+
+class MockChristianCalendarTest(CoreCalendarTest):
     cal_class = MockChristianCalendar
 
     def test_year_2014(self):
@@ -327,7 +368,7 @@ def test_year_2014(self):
         self.assertIn(date(2014, 12, 25), holidays)  # XMas
 
         # Only 2 days: Jan 1st and Christmas
-        self.assertEquals(len(holidays), 2)
+        self.assertEqual(len(holidays), 2)
 
 
 class NoWeekendCalendar(Calendar):
@@ -337,7 +378,7 @@ class NoWeekendCalendar(Calendar):
     """
 
 
-class NoWeekendCalendarTest(GenericCalendarTest):
+class NoWeekendCalendarTest(CoreCalendarTest):
     cal_class = NoWeekendCalendar
 
     def test_weekend(self):
@@ -357,7 +398,7 @@ class WeekendOnWednesdayCalendar(Calendar):
     WEEKEND_DAYS = (WED,)
 
 
-class WeekendOnWednesdayCalendarTest(GenericCalendarTest):
+class WeekendOnWednesdayCalendarTest(CoreCalendarTest):
     cal_class = WeekendOnWednesdayCalendar
 
     def test_weekend(self):
@@ -377,7 +418,7 @@ def get_weekend_days(self):
         return (WED,)
 
 
-class OverwriteGetWeekendDaysCalendarTest(GenericCalendarTest):
+class OverwriteGetWeekendDaysCalendarTest(CoreCalendarTest):
     cal_class = OverwriteGetWeekendDaysCalendar
 
     def test_weekend(self):
@@ -388,6 +429,7 @@ def test_weekend(self):
 
 
 class NoHolidayCalendar(Calendar):
+    include_new_years_day = False
     WEEKEND_DAYS = (SAT, SUN)
 
 
@@ -508,7 +550,7 @@ class MultipleLineEmptyFirstDocstring(Calendar):
     """
 
 
-class CalendarClassName(TestCase):
+class CalendarClassNameTest(TestCase):
     def test_no_docstring(self):
         self.assertEqual(NoDocstring.name, "NoDocstring")
 
@@ -527,7 +569,7 @@ def test_multiple_line_empty_first_docstring(self):
         )
 
 
-class TestAcceptableDateTypes(GenericCalendarTest):
+class TestAcceptableDateTypes(CoreCalendarTest):
     """
     Test cases about accepted date and datetime types.
     """
@@ -623,25 +665,25 @@ def test_datetime(self):
 
     def test_add_working_days_datetime(self):
         # datetime inside, date outside
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56), 0),
             date(self.year, 12, 20)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56), 1),
             date(self.year, 12, 21)
         )
 
         # Use the `keep_datetime` option
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56),
                 0, keep_datetime=True),
             datetime(self.year, 12, 20, 12, 34, 56)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56),
                 1, keep_datetime=True),
@@ -650,25 +692,25 @@ def test_add_working_days_datetime(self):
 
     def test_sub_working_days_datetime(self):
         # datetime inside, date outside
-        self.assertEquals(
+        self.assertEqual(
             self.cal.sub_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56), 0),
             date(self.year, 12, 20)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.sub_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56), 1),
             date(self.year, 12, 19)
         )
 
         # Use the `keep_datetime` option
-        self.assertEquals(
+        self.assertEqual(
             self.cal.sub_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56),
                 0, keep_datetime=True),
             datetime(self.year, 12, 20, 12, 34, 56)
         )
-        self.assertEquals(
+        self.assertEqual(
             self.cal.sub_working_days(
                 datetime(self.year, 12, 20, 12, 34, 56),
                 1, keep_datetime=True),
@@ -682,7 +724,7 @@ def test_get_holiday_label_with_datetime(self):
             self.cal.get_holiday_label(datetime(2014, 1, 2)))
 
 
-class PandasTimestampTest(GenericCalendarTest):
+class PandasTimestampTest(CoreCalendarTest):
     cal_class = MockCalendar
 
     def test_panda_type_is_working_day(self):
@@ -764,3 +806,58 @@ def test_unsupported_type_get_working_days_delta(self):
         self.assertEqual(delta, 2)
         delta = self.cal.get_working_days_delta(end, start)
         self.assertEqual(delta, 2)
+
+
+class MockCalendarNoFatTuesdayLabel(WesternCalendar):
+    fat_tuesday_label = None
+
+
+class FatTuesdayLabelTest(TestCase):
+
+    def test_fat_tuesday_label(self):
+        cal = MockCalendarNoFatTuesdayLabel()
+        with self.assertRaises(CalendarError):
+            cal.get_fat_tuesday(2020)
+
+
+def test_daterange_start_end():
+    start = date(2020, 4, 1)
+    end = date(2020, 4, 10)
+    date_list = list(daterange(start, end))
+    assert date_list == [
+        date(2020, 4, 1),
+        date(2020, 4, 2),
+        date(2020, 4, 3),
+        date(2020, 4, 4),
+        date(2020, 4, 5),
+        date(2020, 4, 6),
+        date(2020, 4, 7),
+        date(2020, 4, 8),
+        date(2020, 4, 9),
+        date(2020, 4, 10),
+    ]
+
+
+def test_daterange_end_start():
+    end = date(2020, 4, 1)
+    start = date(2020, 4, 10)
+    date_list = list(daterange(start, end))
+    assert date_list == [
+        date(2020, 4, 1),
+        date(2020, 4, 2),
+        date(2020, 4, 3),
+        date(2020, 4, 4),
+        date(2020, 4, 5),
+        date(2020, 4, 6),
+        date(2020, 4, 7),
+        date(2020, 4, 8),
+        date(2020, 4, 9),
+        date(2020, 4, 10),
+    ]
+
+
+def test_daterange_same_date():
+    # Stupid usecase, but nonetheless
+    start = end = date(2020, 4, 1)
+    date_list = list(daterange(start, end))
+    assert date_list == [date(2020, 4, 1)]
diff --git a/calendra/tests/test_europe.py b/calendra/tests/test_europe.py
index 5462ac62..f4de9465 100644
--- a/calendra/tests/test_europe.py
+++ b/calendra/tests/test_europe.py
@@ -1,10 +1,12 @@
-from datetime import date
+from datetime import date, timedelta, datetime
 from collections import Counter
 
 from . import GenericCalendarTest
+from ..core import daterange, Holiday
 from ..europe import (
     Austria,
     Bulgaria,
+    Belarus,
     Belgium,
     CaymanIslands,
     Croatia,
@@ -25,16 +27,16 @@
     Lithuania,
     Luxembourg,
     Malta,
+    Monaco,
     Netherlands,
+    NetherlandsWithSchoolHolidays,
     Norway,
     Poland,
     Portugal,
     Romania,
     Russia,
     Serbia,
-    Spain, Catalonia,
     Slovenia,
-    Switzerland, Vaud, Geneva,
     Ukraine,
     UnitedKingdom,
     UnitedKingdomNorthernIreland,
@@ -61,6 +63,42 @@ def test_year_2016(self):
         self.assertIn(date(2016, 12, 25), holidays)  # Xmas
         self.assertIn(date(2016, 12, 26), holidays)  # St Stephens
 
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(holidays[date(2020, 5, 1)], "State Holiday")
+        # a.k.a. Staatsfeiertag
+
+
+class BelarusTest(GenericCalendarTest):
+    cal_class = Belarus
+
+    def test_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 1, 1), holidays)  # New Years day
+        self.assertIn(date(2019, 1, 2), holidays)  # Day after NYE
+        self.assertIn(date(2019, 1, 7), holidays)  # Christmas (Orthodox)
+        self.assertIn(date(2019, 3, 8), holidays)  # International Women's Day
+        self.assertIn(date(2019, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2019, 5, 7), holidays)  # Radonitsa
+        self.assertIn(date(2019, 5, 9), holidays)  # Victory Day
+        self.assertIn(date(2019, 7, 3), holidays)  # Republic Day
+        self.assertIn(date(2019, 11, 7), holidays)  # October Revolution Day
+        self.assertIn(date(2019, 12, 25), holidays)  # Christmas (Catholic)
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New Years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Day after NYE
+        self.assertIn(date(2020, 1, 7), holidays)  # Christmas (Orthodox)
+        self.assertIn(date(2020, 3, 8), holidays)  # International Women's Day
+        self.assertIn(date(2020, 4, 28), holidays)  # Radonitsa
+        self.assertIn(date(2020, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2020, 5, 9), holidays)  # Victory Day
+        self.assertIn(date(2020, 7, 3), holidays)  # Republic Day
+        self.assertIn(date(2020, 11, 7), holidays)  # October Revolution Day
+        self.assertIn(date(2020, 12, 25), holidays)  # Christmas (Catholic)
+
 
 class BulgariaTest(GenericCalendarTest):
     cal_class = Bulgaria
@@ -82,6 +120,12 @@ def test_year_2016(self):
         # Non-attendance day for schools, otherwise a working day.
         self.assertNotIn(date(2016, 11, 1), holidays)   # National Awakening
 
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "International Workers' Day")
+
 
 class CaymanIslandsTest(GenericCalendarTest):
     cal_class = CaymanIslands
@@ -207,23 +251,75 @@ class CroatiaTest(GenericCalendarTest):
 
     def test_year_2016(self):
         holidays = self.cal.holidays_set(2016)
-        self.assertIn(date(2016, 1, 1), holidays)   # New Year's Day Nova Godin
-        self.assertIn(date(2016, 1, 6), holidays)   # Epiphany Bogojavljenje,
-        self.assertIn(date(2016, 3, 27), holidays)  # Easter Sunday Uskrs i us
+        self.assertIn(date(2016, 1, 1), holidays)   # New Year's Day
+        self.assertIn(date(2016, 1, 6), holidays)   # Epiphany / Bogojavljenje
+        self.assertIn(date(2016, 3, 27), holidays)  # Easter Sunday
         self.assertIn(date(2016, 3, 28), holidays)  # Easter Monday
-        self.assertIn(date(2016, 5, 1), holidays)   # Intl Workers' Day Međunar
-        self.assertIn(date(2016, 5, 26), holidays)  # Corpus Christi Tijelovo
-        self.assertIn(date(2016, 6, 22), holidays)  # Anti-Fascist Day Dan anti
-        self.assertIn(date(2016, 6, 25), holidays)  # Statehood Day 	Dan drž
+        self.assertIn(date(2016, 5, 1), holidays)   # Intl Workers' Day
+        self.assertIn(date(2016, 5, 26), holidays)  # Corpus Christi / Tijelovo
+        self.assertIn(date(2016, 6, 22), holidays)  # Anti-Fascist Day
+        self.assertIn(date(2016, 6, 25), holidays)  # Statehood Day
         self.assertIn(date(2016, 8, 5), holidays)   # Victory & Homeland Thanks
-        self.assertIn(date(2016, 8, 15), holidays)  # Assumption of Mary 	Vel
+        self.assertIn(date(2016, 8, 15), holidays)  # Assumption of Mary
         self.assertIn(date(2016, 10, 8), holidays)  # Independence Day Dan neov
         self.assertIn(date(2016, 11, 1), holidays)  # All Saints' Day Dan svih
         self.assertIn(date(2016, 12, 25), holidays)  # Christmas Božić
         self.assertIn(date(2016, 12, 26), holidays)  # St. Stephen's Day Prvi d
 
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)   # New Year's Day
+        self.assertIn(date(2020, 1, 6), holidays)   # Epiphany / Bogojavljenje
+        self.assertIn(date(2020, 4, 12), holidays)  # Easter Sunday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2020, 5, 30), holidays)  # Statehood day
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 6, 22), holidays)  # Anti-Fascist Day
+        # Victory & Homeland Thanksgiving
+        self.assertIn(date(2020, 8, 5), holidays)
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption of Mary
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints' Day
+        self.assertIn(date(2020, 11, 18), holidays)  # Remembrance Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen
+
+    def test_statehood_day(self):
+        holidays_2019 = self.cal.holidays_set(2019)
+        holidays_2020 = self.cal.holidays_set(2020)
+
+        # Statehood day was a holiday on June 25th until 2020
+        self.assertIn(date(2019, 6, 25), holidays_2019)
+        self.assertNotIn(date(2020, 6, 25), holidays_2020)
+
+        # Statehood day as of 2020 happens on May 30th
+        self.assertNotIn(date(2019, 5, 30), holidays_2019)
+        self.assertIn(date(2020, 5, 30), holidays_2020)
+
+    def test_independance_day(self):
+        # Independence Day was a holiday until 2020
+        holidays_2019 = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 10, 8), holidays_2019)
+        holidays_2020 = self.cal.holidays_set(2020)
+        self.assertNotIn(date(2020, 10, 8), holidays_2020)
+
+    def test_remembrance_day(self):
+        # Remembrance Day was introduced as of 2020
+        holidays_2018 = self.cal.holidays_set(2018)
+        self.assertNotIn(date(2018, 11, 18), holidays_2018)
+        holidays_2019 = self.cal.holidays_set(2019)
+        self.assertNotIn(date(2019, 11, 18), holidays_2019)
+        holidays_2020 = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 11, 18), holidays_2020)
+
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "International Workers' Day")
+
 
-class Cyprus(GenericCalendarTest):
+class CyprusTest(GenericCalendarTest):
     cal_class = Cyprus
 
     def test_year_2017(self):
@@ -460,12 +556,12 @@ def test_working_days(self):
 
     def test_business_days_computations(self):
         day = date(2013, 10, 30)
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(day, 0), date(2013, 10, 30))
-        self.assertEquals(
+        self.assertEqual(
             self.cal.add_working_days(day, 1), date(2013, 10, 31))
-        self.assertEquals(self.cal.add_working_days(day, 2), date(2013, 11, 4))
-        self.assertEquals(self.cal.add_working_days(day, 3), date(2013, 11, 5))
+        self.assertEqual(self.cal.add_working_days(day, 2), date(2013, 11, 4))
+        self.assertEqual(self.cal.add_working_days(day, 3), date(2013, 11, 5))
 
 
 class FranceAlsaceMoselleTest(FranceTest):
@@ -505,6 +601,29 @@ def test_year_2013(self):
         self.assertIn(date(2013, 12, 25), holidays)  # XMas
         self.assertIn(date(2013, 12, 26), holidays)  # Glorifying mother of God
 
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # new year
+        self.assertIn(date(2020, 1, 6), holidays)  # epiphany
+        self.assertIn(date(2020, 3, 2), holidays)  # Clean monday
+        # Annunciation & Independence day
+        self.assertIn(date(2020, 3, 25), holidays)
+        self.assertIn(date(2020, 4, 17), holidays)  # good friday
+        self.assertIn(date(2020, 4, 19), holidays)  # easter
+        self.assertIn(date(2020, 4, 20), holidays)  # easter monday
+        self.assertIn(date(2020, 5, 1), holidays)  # labour day
+        self.assertIn(date(2020, 6, 7), holidays)  # pentecost sunday
+        self.assertIn(date(2020, 6, 8), holidays)  # whit monday
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 10, 28), holidays)  # Ochi Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # Glorifying mother of God
+
+    def test_no_orthodox_christmas(self):
+        # This calendar is an Orthodox calendar, but there's no Jan XMas
+        holidays = self.cal.holidays_set(2020)
+        self.assertNotIn(date(2020, 1, 7), holidays)
+
 
 class HungaryTest(GenericCalendarTest):
     cal_class = Hungary
@@ -559,6 +678,48 @@ def test_year_2017(self):
         self.assertIn(date(2017, 12, 8), holidays)  # Il-Kunċizzjoni
         self.assertIn(date(2017, 12, 25), holidays)  # Il-Milied
 
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "Worker's Day")
+
+
+class MonacoTest(GenericCalendarTest):
+    cal_class = Monaco
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        # National Holidays
+        self.assertIn(date(2020, 1, 1), holidays)  # New Year's Day
+        self.assertIn(date(2020, 1, 27), holidays)  # Saint Dévote's Day
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension Day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption Day
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints' Day
+        self.assertIn(date(2020, 11, 19), holidays)  # Sovereign Prince's Day
+        self.assertIn(date(2020, 12, 8), holidays)  # Conception Day
+        self.assertIn(date(2020, 12, 25), holidays)  # Christmas Day
+
+    def test_year_2018(self):
+        holidays = self.cal.holidays_set(2018)
+        # National Holidays
+        self.assertIn(date(2018, 1, 1), holidays)  # New Year's Day
+        self.assertIn(date(2018, 1, 27), holidays)  # Saint Dévote's Day
+        self.assertIn(date(2018, 4, 2), holidays)  # Easter Monday
+        self.assertIn(date(2018, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2018, 5, 10), holidays)  # Ascension Day
+        self.assertIn(date(2018, 5, 21), holidays)  # Whit Monday
+        self.assertIn(date(2018, 5, 31), holidays)  # Corpus Christi
+        self.assertIn(date(2018, 8, 15), holidays)  # Assumption Day
+        self.assertIn(date(2018, 11, 1), holidays)  # All Saints' Day
+        self.assertIn(date(2018, 11, 19), holidays)  # Sovereign Prince's Day
+        self.assertIn(date(2018, 12, 8), holidays)  # Conception Day
+        self.assertIn(date(2018, 12, 25), holidays)  # Christmas Day
+
 
 class NorwayTest(GenericCalendarTest):
     cal_class = Norway
@@ -705,6 +866,12 @@ def test_year_2013(self):
         self.assertIn(date(2013, 12, 25), holidays)  # christmas
         self.assertIn(date(2013, 12, 26), holidays)  # San Stefano
 
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "International Workers' Day")
+
 
 class LatviaTest(GenericCalendarTest):
 
@@ -764,7 +931,7 @@ class NetherlandsTest(GenericCalendarTest):
 
     cal_class = Netherlands
 
-    def test_year_2016(self):
+    def test_year_2015(self):
         holidays = self.cal.holidays_set(2015)
         self.assertIn(date(2015, 1, 1), holidays)   # New Year
         self.assertIn(date(2015, 4, 3), holidays)   # Good friday
@@ -778,15 +945,62 @@ def test_year_2016(self):
         self.assertIn(date(2015, 12, 25), holidays)  # Christmas
         self.assertIn(date(2015, 12, 26), holidays)  # St. Stephen´s Day
 
-    def test_year_2025(self):
-        """ In 2025 King's Day is on 26 April """
+    def test_king_day_2014(self):
+        # As of 2014, King's Day replaces Queen's Day.
+        # It's normally happening on April 27th, except when it's on SUN
+        queen_day = date(2014, 4, 30)
+        real_king_day = date(2014, 4, 27)
+        king_day = date(2014, 4, 26)
+        holidays = dict(self.cal.holidays(2014))
+        self.assertNotIn(queen_day, holidays)
+        self.assertNotIn(real_king_day, holidays)
+        self.assertIn(king_day, holidays)
+        self.assertEqual(holidays[king_day], "King's day")
+
+    def test_king_day_2020(self):
+        # Regular King's day
+        king_day = date(2020, 4, 27)
+        holidays = dict(self.cal.holidays(2020))
+        self.assertIn(king_day, holidays)
+        self.assertEqual(holidays[king_day], "King's day")
+
+    def test_king_day_2025(self):
+        # In 2025, it's shifted to the 26th, because it happens on SUN
         holidays = self.cal.holidays_set(2025)
-        self.assertIn(date(2025, 4, 26), holidays)   # King's Day
-
-    def test_year_1990(self):
-        """ In 1990 Queen's day was on 30 April """
-        holidays = self.cal.holidays_set(1990)
-        self.assertIn(date(1990, 4, 30), holidays)   # Queen's Day
+        real_king_day = date(2025, 4, 27)
+        king_day = date(2025, 4, 26)
+        self.assertNotIn(real_king_day, holidays)
+        self.assertIn(king_day, holidays)
+
+    def test_queen_day(self):
+        # In 1990 Queen's day was on 30 April. So it was until 2013.
+        queen_day = date(1990, 4, 30)
+        holidays = dict(self.cal.holidays(1990))
+        self.assertIn(queen_day, holidays)
+        # Check for label
+        self.assertEqual(holidays[queen_day], "Queen's day")
+
+        # Check in 2010
+        queen_day = date(2010, 4, 30)
+        holidays = dict(self.cal.holidays(2010))
+        self.assertIn(queen_day, holidays)
+        # Check for label
+        self.assertEqual(holidays[queen_day], "Queen's day")
+
+        # No more in 2014
+        queen_day = date(2014, 4, 30)
+        holidays = self.cal.holidays_set(2014)
+        self.assertNotIn(queen_day, holidays)
+
+    def test_queen_day_on_sunday(self):
+        # On April 30th, 2006 Queen's day happened on SUN.
+        queen_day = date(2006, 4, 29)
+        holidays = dict(self.cal.holidays(2006))
+        # The regular holiday
+        self.assertNotIn(date(2006, 4, 30), holidays)
+        # It's shifted to the previous day
+        self.assertIn(queen_day, holidays)
+        self.assertEqual(holidays[queen_day], "Queen's day")
 
     def test_new_years_eve(self):
         # For some reason, the new year's eve was added to the list of fixed
@@ -795,7 +1009,360 @@ def test_new_years_eve(self):
         self.assertNotIn(date(2016, 12, 31), holidays)
 
 
-class Romania(GenericCalendarTest):
+class NetherlandsWithSchoolHolidaysWithInvalidRegionTest(GenericCalendarTest):
+
+    cal_class = NetherlandsWithSchoolHolidays
+    kwargs = dict(region="east")
+
+    def setUp(self):
+        with self.assertRaises(ValueError) as cm:
+            super().setUp()
+        assert "Set region" in str(cm.exception)
+
+    def test_weekend_days(self):
+        """Skip test as calendar setup is expected to fail"""
+        pass
+
+    def test_january_1st(self):
+        """Skip test as calendar setup is expected to fail"""
+        pass
+
+    def test_ical_export(self):
+        """Skip test as calendar setup is expected to fail"""
+        pass
+
+
+class NetherlandsNorthWithSchoolHolidaysTest(GenericCalendarTest):
+
+    cal_class = NetherlandsWithSchoolHolidays
+    kwargs = dict(region="north")
+
+    def test_year_2010(self):
+        with self.assertRaises(NotImplementedError):
+            dict(self.cal.holidays(2010))
+
+    def test_year_2020(self):
+        year = 2020
+        christmas_holiday_end = date(2020, 1, 5)
+        spring_holiday_start = date(2020, 2, 15)
+        may_holiday_start = date(2020, 4, 25)
+        summer_holiday_start = date(2020, 7, 4)
+        fall_holiday_start = date(2020, 10, 10)
+        christmas_holiday_start = date(2020, 12, 19)
+
+        self._test_school_holidays(
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+        )
+
+    def test_year_2021(self):
+        year = 2021
+        christmas_holiday_end = date(2021, 1, 3)
+        spring_holiday_start = date(2021, 2, 20)
+        may_holiday_start = date(2021, 5, 1)
+        summer_holiday_start = date(2021, 7, 10)
+        fall_holiday_start = date(2021, 10, 16)
+        christmas_holiday_start = date(2021, 12, 25)
+
+        self._test_school_holidays(
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+        )
+
+    def _test_school_holidays(
+            self,
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+    ):
+        holidays = {}
+        for h in self.cal.holidays(year):
+            holidays.setdefault(h[0], []).append(h[1])
+
+        for d in range(christmas_holiday_end.day):
+            self.assertIn(
+                "Christmas holiday",
+                holidays[christmas_holiday_end - timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "Spring holiday",
+                holidays[spring_holiday_start + timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "May holiday",
+                holidays[may_holiday_start + timedelta(days=d)],
+            )
+        for d in range(44):
+            self.assertIn(
+                "Summer holiday",
+                holidays[summer_holiday_start + timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "Fall holiday",
+                holidays[fall_holiday_start + timedelta(days=d)],
+            )
+        for d in range(31 - christmas_holiday_start.day + 1):
+            self.assertIn(
+                "Christmas holiday",
+                holidays[christmas_holiday_start + timedelta(days=d)],
+            )
+
+
+class NetherlandsMiddleWithSchoolHolidaysTest(GenericCalendarTest):
+
+    cal_class = NetherlandsWithSchoolHolidays
+    kwargs = dict(region="middle")
+
+    def test_year_2010(self):
+        with self.assertRaises(NotImplementedError):
+            dict(self.cal.holidays(2010))
+
+    def test_year_2020(self):
+        year = 2020
+        christmas_holiday_end = date(2020, 1, 5)
+        spring_holiday_start = date(2020, 2, 22)
+        may_holiday_start = date(2020, 4, 25)
+        summer_holiday_start = date(2020, 7, 18)
+        fall_holiday_start = date(2020, 10, 17)
+        christmas_holiday_start = date(2020, 12, 19)
+
+        self._test_school_holidays(
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+        )
+
+    def test_year_2021(self):
+        year = 2021
+        christmas_holiday_end = date(2021, 1, 3)
+        spring_holiday_start = date(2021, 2, 20)
+        may_holiday_start = date(2021, 5, 1)
+        summer_holiday_start = date(2021, 7, 17)
+        fall_holiday_start = date(2021, 10, 16)
+        christmas_holiday_start = date(2021, 12, 25)
+
+        self._test_school_holidays(
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+        )
+
+    def _test_school_holidays(
+            self,
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+    ):
+        holidays = {}
+        for h in self.cal.holidays(year):
+            holidays.setdefault(h[0], []).append(h[1])
+
+        for d in range(christmas_holiday_end.day):
+            self.assertIn(
+                "Christmas holiday",
+                holidays[christmas_holiday_end - timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "Spring holiday",
+                holidays[spring_holiday_start + timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "May holiday",
+                holidays[may_holiday_start + timedelta(days=d)],
+            )
+        for d in range(44):
+            self.assertIn(
+                "Summer holiday",
+                holidays[summer_holiday_start + timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "Fall holiday",
+                holidays[fall_holiday_start + timedelta(days=d)],
+            )
+        for d in range(31 - christmas_holiday_start.day + 1):
+            self.assertIn(
+                "Christmas holiday",
+                holidays[christmas_holiday_start + timedelta(days=d)],
+            )
+
+
+class NetherlandsSouthWithSchoolHolidaysTest(GenericCalendarTest):
+
+    cal_class = NetherlandsWithSchoolHolidays
+    kwargs = dict(region="south")
+
+    def test_year_2010(self):
+        with self.assertRaises(NotImplementedError):
+            dict(self.cal.holidays(2010))
+
+    def test_year_2020(self):
+        year = 2020
+        christmas_holiday_end = date(2020, 1, 5)
+        spring_holiday_start = date(2020, 2, 22)
+        may_holiday_start = date(2020, 4, 25)
+        summer_holiday_start = date(2020, 7, 11)
+        fall_holiday_start = date(2020, 10, 17)
+        christmas_holiday_start = date(2020, 12, 19)
+
+        self._test_school_holidays(
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+        )
+
+    def test_year_2021(self):
+        year = 2021
+        christmas_holiday_end = date(2021, 1, 3)
+        spring_holiday_start = date(2021, 2, 13)
+        may_holiday_start = date(2021, 5, 1)
+        summer_holiday_start = date(2021, 7, 24)
+        fall_holiday_start = date(2021, 10, 23)
+        christmas_holiday_start = date(2021, 12, 25)
+
+        self._test_school_holidays(
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+        )
+
+    def _test_school_holidays(
+            self,
+            year,
+            christmas_holiday_start,
+            spring_holiday_start,
+            may_holiday_start,
+            summer_holiday_start,
+            fall_holiday_start,
+            christmas_holiday_end,
+    ):
+        holidays = {}
+        for h in self.cal.holidays(year):
+            holidays.setdefault(h[0], []).append(h[1])
+
+        for d in range(christmas_holiday_end.day):
+            self.assertIn(
+                "Christmas holiday",
+                holidays[christmas_holiday_end - timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "Spring holiday",
+                holidays[spring_holiday_start + timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "May holiday",
+                holidays[may_holiday_start + timedelta(days=d)],
+            )
+        for d in range(44):
+            self.assertIn(
+                "Summer holiday",
+                holidays[summer_holiday_start + timedelta(days=d)],
+            )
+        for d in range(9):
+            self.assertIn(
+                "Fall holiday",
+                holidays[fall_holiday_start + timedelta(days=d)],
+            )
+        for d in range(31 - christmas_holiday_start.day + 1):
+            self.assertIn(
+                "Christmas holiday",
+                holidays[christmas_holiday_start + timedelta(days=d)],
+            )
+
+
+class NetherlandsWithCarnivalTest(GenericCalendarTest):
+
+    cal_class = Netherlands
+    kwargs = dict(include_carnival=True)
+
+    def test_year_2020(self):
+        holidays = dict(self.cal.holidays(2020))
+        self.assertEqual(
+            holidays[date(2020, 2, 23)], "Carnival"
+        )
+        self.assertEqual(
+            holidays[date(2020, 2, 24)], "Carnival"
+        )
+        self.assertEqual(
+            holidays[date(2020, 2, 25)], "Carnival"
+        )
+
+    def test_year_2021(self):
+        holidays = dict(self.cal.holidays(2021))
+        self.assertEqual(
+            holidays[date(2021, 2, 14)], "Carnival"
+        )
+        self.assertEqual(
+            holidays[date(2021, 2, 15)], "Carnival"
+        )
+        self.assertEqual(
+            holidays[date(2021, 2, 16)], "Carnival"
+        )
+
+
+class NetherlandsWithSchoolHolidaysAndCarnivalTest(GenericCalendarTest):
+    cal_class = NetherlandsWithSchoolHolidays
+    kwargs = dict(region="south", carnival_instead_of_spring=True)
+
+    def test_year_2020(self):
+        holidays = dict(self.cal.holidays(2020))
+        for d in range(9):
+            self.assertIn(
+                "Carnival holiday",
+                holidays[date(2020, 2, 22) + timedelta(days=d)],
+            )
+
+    def test_year_2021(self):
+        holidays = dict(self.cal.holidays(2021))
+        for d in range(9):
+            self.assertIn(
+                "Carnival holiday",
+                holidays[date(2021, 2, 13) + timedelta(days=d)],
+            )
+
+
+class RomaniaTest(GenericCalendarTest):
     cal_class = Romania
 
     def test_year_2017(self):
@@ -834,8 +1401,27 @@ def test_year_2019(self):
         self.assertIn(date(2019, 12, 25), holidays)  # Crăciunul Christmas
         self.assertIn(date(2019, 12, 26), holidays)  # Crăciunul Christmas
 
+    def test_liberation_day(self):
+        # Liberation day only happened between 1949 and 1990 (incl.)
+        liberation_day_1989 = date(1989, 8, 23)
+        holidays = self.cal.holidays_set(1989)
+        self.assertIn(liberation_day_1989, holidays)
+
+        liberation_day_1990 = date(1990, 8, 23)
+        holidays = self.cal.holidays_set(1990)
+        self.assertIn(liberation_day_1990, holidays)
+
+        liberation_day_1991 = date(1991, 8, 23)
+        holidays = self.cal.holidays_set(1991)
+        self.assertNotIn(liberation_day_1991, holidays)
+
+    def test_no_orthodox_christmas(self):
+        # This calendar is an Orthodox calendar, but there's no Jan XMas
+        holidays = self.cal.holidays_set(2020)
+        self.assertNotIn(date(2020, 1, 7), holidays)
+
 
-class Russia(GenericCalendarTest):
+class RussiaTest(GenericCalendarTest):
     cal_class = Russia
 
     def test_year_2018(self):
@@ -850,6 +1436,137 @@ def test_year_2018(self):
         self.assertIn(date(2018, 6, 12), holidays)  # National Day
         self.assertIn(date(2018, 11, 4), holidays)  # Independence Day
 
+    def test_year_2020_shift(self):
+        holidays = dict(self.cal.holidays(2020))
+        # Defender of the Fatherland Day
+        self.assertIn(date(2020, 2, 23), holidays)
+        # SUN => Shift to MON
+        self.assertIn(date(2020, 2, 24), holidays)
+        self.assertEqual(
+            holidays[date(2020, 2, 24)],
+            "Defendence of the Fatherland shift"
+        )
+        # International Women's Day
+        self.assertIn(date(2020, 3, 8), holidays)
+        # SUN => Shift to MON
+        self.assertIn(date(2020, 3, 9), holidays)
+        self.assertEqual(
+            holidays[date(2020, 3, 9)],
+            "International Women's Day shift"
+        )
+        # Victory Day
+        self.assertIn(date(2020, 5, 9), holidays)
+        # SAT => Shift to MON
+        self.assertIn(date(2020, 5, 11), holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 11)],
+            "Victory Day shift"
+        )
+
+    def test_covid19_2020(self):
+        # By decree, several days of March and April were defined
+        # as non-working days because of the COVID-19 crisis.
+        # It spans from March 28th to April 30th (included).
+        holidays = self.cal.holidays_set(2020)
+        start = date(2020, 3, 28)
+        end = date(2020, 4, 30)
+        for day in daterange(start, end):
+            self.assertIn(day, holidays)
+        # COVID-19 days in May
+        self.assertIn(date(2020, 5, 6), holidays)  # Covid-19 May #1
+        self.assertIn(date(2020, 5, 7), holidays)  # Covid-19 May #2
+        self.assertIn(date(2020, 5, 8), holidays)  # Covid-19 May #3
+
+        # Not for 2019, though
+        holidays = self.cal.holidays_set(2019)
+        start = date(2019, 3, 28)
+        end = date(2019, 4, 30)
+        for day in daterange(start, end):
+            self.assertNotIn(day, holidays)
+
+    def test_covid19_2020_label(self):
+        # no "shift" for those days, all of them were non-working days
+        holidays = self.cal.holidays(2020)
+        start = date(2020, 3, 28)
+        end = date(2020, 4, 30)
+        for day, label in holidays:
+            if start <= day <= end:
+                self.assertNotIn("shift", label)
+
+    def test_extra_days_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 5, 4), holidays)  # extra day, no other info
+        self.assertIn(date(2020, 5, 5), holidays)  # Day of spring/labor day
+        # Constitution Vote Public Holiday
+        self.assertIn(date(2020, 7, 1), holidays)
+        # Not in 2019
+        holidays = self.cal.holidays_set(2019)
+        self.assertNotIn(date(2019, 5, 4), holidays)
+        self.assertNotIn(date(2019, 5, 5), holidays)
+        self.assertNotIn(date(2019, 7, 1), holidays)
+
+    def test_new_year_holidays_2004(self):
+        # At that time, only Jan 1st/2nd were holidays.
+        holidays = self.cal.holidays_set(2004)
+        self.assertIn(date(2004, 1, 1), holidays)  # New Year's Day
+        self.assertIn(date(2004, 1, 2), holidays)  # Day after New Year
+        self.assertNotIn(date(2004, 1, 3), holidays)  # Shift of Jan 2nd
+        self.assertNotIn(date(2004, 1, 4), holidays)  # Not until 2005
+        self.assertNotIn(date(2004, 1, 5), holidays)  # Not until 2005
+        self.assertNotIn(date(2004, 1, 6), holidays)  # Not until 2005
+        self.assertIn(date(2004, 1, 7), holidays)  # XMas
+        self.assertNotIn(date(2004, 1, 8), holidays)  # Not until 2005
+
+    def test_new_year_holidays_2005(self):
+        holidays = self.cal.holidays_set(2005)
+        self.assertIn(date(2005, 1, 1), holidays)  # New Year's Day
+        self.assertIn(date(2005, 1, 2), holidays)  # Day after New Year
+        self.assertIn(date(2005, 1, 3), holidays)  # Holiday since 2005
+        self.assertIn(date(2005, 1, 4), holidays)  # Holiday since 2005
+        self.assertIn(date(2005, 1, 5), holidays)  # Holiday since 2005
+        self.assertIn(date(2005, 1, 6), holidays)  # Holiday since 2005
+        self.assertIn(date(2005, 1, 7), holidays)  # XMas
+        self.assertIn(date(2005, 1, 8), holidays)  # Part of the holiday week
+
+    def test_no_december_christmas(self):
+        # This calendar is an Orthodox calendar & it doesn't include Dec 25th
+        holidays = self.cal.holidays_set(2020)
+        self.assertNotIn(date(2020, 12, 25), holidays)
+
+    def test_holidays_2021(self):
+        holidays = self.cal.holidays(2021)
+        holidays_2021 = [
+            (date(2021, 1, 1), "New year"),
+            (date(2021, 1, 2), "Day After New Year"),  # SAT
+            (date(2021, 1, 3), "Third Day after New Year"),  # SUN
+            (date(2021, 1, 4), "Fourth Day after New Year"),
+            (date(2021, 1, 5), "Fifth Day after New Year"),
+            (date(2021, 1, 6), "Sixth Day after New Year"),
+            (date(2021, 1, 7), "Christmas"),
+            (date(2021, 1, 8), "Eighth Day after New Year"),
+            (date(2021, 2, 22), "Day Before Defendence of the Fatherland"),
+            (date(2021, 2, 23), "Defendence of the Fatherland"),
+            (date(2021, 3, 8), "International Women's Day"),
+            (date(2021, 5, 1), "The Day of Spring and Labour"),
+            (date(2021, 5, 3), "The Day of Spring and Labour shift"),
+            (date(2021, 5, 9), "Victory Day"),
+            (date(2021, 5, 10), "Victory Day shift"),
+            (date(2021, 6, 12), "National Day"),
+            (date(2021, 6, 14), "National Day shift"),
+            (date(2021, 11, 4), "Day of Unity"),
+            (date(2021, 11, 5), "Day After Day of Unity"),
+            (date(2021, 12, 31), "New Year's Eve")
+        ]
+        holidays_2021 = [Holiday(*h) for h in holidays_2021]
+        self.assertEqual(holidays, holidays_2021)
+
+    def test_year_2021_extra_working_day(self):
+        self.assertTrue(self.cal.is_working_day(date(2021, 2, 20)))
+        self.assertTrue(self.cal.is_working_day(datetime(2021, 2, 20)))
+
+        self.assertTrue(self.cal.is_working_day(date(2021, 2, 19)))
+        self.assertTrue(self.cal.is_working_day(datetime(2020, 2, 19)))
+
 
 class UkraineTest(GenericCalendarTest):
     cal_class = Ukraine
@@ -906,6 +1623,11 @@ def test_year_2018(self):
         holidays = self.cal.holidays_set(2018)
         self.assertNotIn(date(2018, 5, 2), holidays)  # Workers Solidarity Day
 
+    def test_may_1st_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(holidays[date(2020, 5, 1)], "Workers Solidarity Day")
+
 
 class UnitedKingdomTest(GenericCalendarTest):
     cal_class = UnitedKingdom
@@ -1123,68 +1845,16 @@ def test_year_2016(self):
         # Restauração da Independência
         self.assertIn(date(2016, 12, 1), holidays)
 
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "Dia do Trabalhador")
 
-class SpainTest(GenericCalendarTest):
-    cal_class = Spain
-
-    def test_year_2015(self):
-        holidays = self.cal.holidays_set(2015)
-        self.assertIn(date(2015, 1, 1), holidays)
-        self.assertIn(date(2015, 1, 6), holidays)
-        self.assertIn(date(2015, 4, 3), holidays)
-        self.assertIn(date(2015, 5, 1), holidays)
-        self.assertIn(date(2015, 8, 15), holidays)
-        self.assertIn(date(2015, 10, 12), holidays)
-        self.assertIn(date(2015, 12, 8), holidays)
-        self.assertIn(date(2015, 12, 25), holidays)
-
-    def test_year_2016(self):
-        holidays = self.cal.holidays_set(2016)
-        self.assertIn(date(2016, 1, 1), holidays)
-        self.assertIn(date(2016, 1, 6), holidays)
-        self.assertIn(date(2016, 3, 25), holidays)
-        self.assertIn(date(2016, 8, 15), holidays)
-        self.assertIn(date(2016, 10, 12), holidays)
-        self.assertIn(date(2016, 11, 1), holidays)
-        self.assertIn(date(2016, 12, 6), holidays)
-        self.assertIn(date(2016, 12, 8), holidays)
-
-
-class CataloniaTest(GenericCalendarTest):
-    cal_class = Catalonia
-
-    def test_year_2015(self):
-        holidays = self.cal.holidays_set(2015)
-        self.assertIn(date(2015, 1, 1), holidays)
-        self.assertIn(date(2015, 1, 6), holidays)
-        self.assertIn(date(2015, 4, 3), holidays)
-        self.assertIn(date(2015, 4, 6), holidays)
-        self.assertIn(date(2015, 5, 1), holidays)
-        self.assertIn(date(2015, 6, 24), holidays)
-        self.assertIn(date(2015, 8, 15), holidays)
-        self.assertIn(date(2015, 9, 11), holidays)
-        self.assertIn(date(2015, 10, 12), holidays)
-        self.assertIn(date(2015, 11, 1), holidays)
-        self.assertIn(date(2015, 12, 6), holidays)
-        self.assertIn(date(2015, 12, 8), holidays)
-        self.assertIn(date(2015, 12, 25), holidays)
-        self.assertIn(date(2015, 12, 26), holidays)
-
-    def test_year_2016(self):
-        holidays = self.cal.holidays_set(2016)
-        self.assertIn(date(2016, 1, 1), holidays)
-        self.assertIn(date(2016, 1, 6), holidays)
-        self.assertIn(date(2016, 3, 25), holidays)
-        self.assertIn(date(2016, 3, 28), holidays)
-        self.assertIn(date(2016, 6, 24), holidays)
-        self.assertIn(date(2016, 8, 15), holidays)
-        self.assertIn(date(2016, 9, 11), holidays)
-        self.assertIn(date(2016, 10, 12), holidays)
-        self.assertIn(date(2016, 11, 1), holidays)
-        self.assertIn(date(2016, 12, 6), holidays)
-        self.assertIn(date(2016, 12, 8), holidays)
-        self.assertIn(date(2016, 12, 25), holidays)
-        self.assertIn(date(2016, 12, 26), holidays)
+    def test_immaculate_conception_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(holidays[date(2020, 12, 8)], "Imaculada Conceição")
 
 
 class SerbiaTest(GenericCalendarTest):
@@ -1230,6 +1900,11 @@ def test_year_2017(self):
         self.assertIn(date(2017, 5, 2), holidays)
         self.assertIn(date(2017, 11, 11), holidays)
 
+    def test_no_december_christmas(self):
+        # This calendar is an Orthodox calendar & it doesn't include Dec 25th
+        holidays = self.cal.holidays_set(2020)
+        self.assertNotIn(date(2020, 12, 25), holidays)
+
 
 class SloveniaTest(GenericCalendarTest):
     cal_class = Slovenia
@@ -1295,80 +1970,6 @@ def test_january_second(self):
         self.assertIn(date(2017, 1, 2), holidays)
 
 
-class SwitzerlandTest(GenericCalendarTest):
-    cal_class = Switzerland
-
-    def test_year_2015(self):
-        holidays = self.cal.holidays_set(2015)
-        self.assertIn(date(2015, 1, 1), holidays)
-        self.assertIn(date(2015, 1, 2), holidays)
-        self.assertIn(date(2015, 4, 3), holidays)
-        self.assertIn(date(2015, 4, 5), holidays)
-        self.assertIn(date(2015, 4, 6), holidays)
-        self.assertIn(date(2015, 5, 1), holidays)
-        self.assertIn(date(2015, 5, 14), holidays)
-        self.assertIn(date(2015, 5, 24), holidays)
-        self.assertIn(date(2015, 5, 25), holidays)
-        self.assertIn(date(2015, 8, 1), holidays)
-        self.assertIn(date(2015, 12, 25), holidays)
-        self.assertIn(date(2015, 12, 26), holidays)
-
-    def test_year_2016(self):
-        holidays = self.cal.holidays_set(2016)
-        self.assertIn(date(2016, 1, 1), holidays)
-        self.assertIn(date(2016, 1, 2), holidays)
-        self.assertIn(date(2016, 3, 25), holidays)
-        self.assertIn(date(2016, 3, 28), holidays)
-        self.assertIn(date(2016, 5, 1), holidays)
-        self.assertIn(date(2016, 5, 5), holidays)
-        self.assertIn(date(2016, 5, 15), holidays)
-        self.assertIn(date(2016, 5, 16), holidays)
-        self.assertIn(date(2016, 8, 1), holidays)
-        self.assertIn(date(2016, 12, 25), holidays)
-        self.assertIn(date(2016, 12, 26), holidays)
-
-
-class VaudTest(GenericCalendarTest):
-    cal_class = Vaud
-
-    def test_year_2016(self):
-        holidays = self.cal.holidays_set(2016)
-        self.assertIn(date(2016, 9, 19), holidays)
-        self.assertNotIn(date(2016, 5, 1), holidays)
-        self.assertNotIn(date(2016, 12, 26), holidays)
-
-    def test_year_2017(self):
-        holidays = self.cal.holidays_set(2017)
-        self.assertIn(date(2017, 9, 18), holidays)
-        self.assertNotIn(date(2017, 5, 1), holidays)
-        self.assertNotIn(date(2017, 12, 26), holidays)
-
-
-class GenevaTest(GenericCalendarTest):
-    cal_class = Geneva
-
-    def test_year_2018(self):
-        holidays = self.cal.holidays_set(2018)
-        self.assertIn(date(2018, 9, 6), holidays)
-        self.assertIn(date(2018, 12, 31), holidays)
-        self.assertNotIn(date(2018, 5, 1), holidays)
-        self.assertNotIn(date(2018, 12, 26), holidays)
-
-    def test_year_2019(self):
-        holidays = self.cal.holidays_set(2019)
-        self.assertIn(date(2019, 9, 5), holidays)
-        self.assertIn(date(2019, 12, 31), holidays)
-        self.assertNotIn(date(2019, 5, 1), holidays)
-        self.assertNotIn(date(2019, 12, 26), holidays)
-
-    def test_year_2020(self):
-        holidays = self.cal.holidays_set(2020)
-        self.assertIn(date(2020, 9, 10), holidays)
-        self.assertIn(date(2020, 12, 31), holidays)
-        self.assertNotIn(date(2020, 5, 1), holidays)
-        self.assertNotIn(date(2020, 12, 26), holidays)
-
-
 class EstoniaTest(GenericCalendarTest):
     cal_class = Estonia
 
@@ -1440,3 +2041,33 @@ def test_year_2017(self):
         self.assertIn(date(2017, 12, 24), holidays)  # Xmas eve
         self.assertIn(date(2017, 12, 25), holidays)  # Xmas day
         self.assertIn(date(2017, 12, 26), holidays)  # 2nd day of xmas
+
+    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, 16), holidays)  # restoration of the state
+        self.assertIn(date(2020, 3, 11), holidays)  # restoration of independ.
+        self.assertIn(date(2020, 4, 12), holidays)  # easter sunday
+        self.assertIn(date(2020, 4, 13), holidays)  # easter monday
+        self.assertIn(date(2020, 5, 1), holidays)  # labour day
+        self.assertIn(date(2020, 5, 3), holidays)  # mother's day
+        self.assertIn(date(2020, 6, 7), holidays)  # father's day
+        self.assertIn(date(2020, 6, 24), holidays)  # st john's day
+        self.assertIn(date(2020, 7, 6), holidays)  # Anniversary of King Mind.
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption day
+        self.assertIn(date(2020, 11, 1), holidays)  # All saints day
+        self.assertIn(date(2020, 11, 2), holidays)  # All souls day
+        self.assertIn(date(2020, 12, 24), holidays)  # Xmas eve
+        self.assertIn(date(2020, 12, 25), holidays)  # Xmas day
+        self.assertIn(date(2020, 12, 26), holidays)  # 2nd day of xmas
+
+    def test_all_souls_day(self):
+        # All Souls day was introduced as of 2020
+        # https://en.wikipedia.org/wiki/Public_holidays_in_Lithuania
+        holidays = self.cal.holidays_set(2018)
+        self.assertNotIn(date(2018, 11, 2), holidays)
+        holidays = self.cal.holidays_set(2019)
+        self.assertNotIn(date(2019, 11, 2), holidays)
+
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 11, 2), holidays)
diff --git a/calendra/tests/test_ical_export.py b/calendra/tests/test_ical_export.py
new file mode 100644
index 00000000..2e3f3a03
--- /dev/null
+++ b/calendra/tests/test_ical_export.py
@@ -0,0 +1,141 @@
+import tempfile
+import os
+from os.path import join
+
+from ..core import Calendar
+from ..exceptions import ICalExportRangeError, ICalExportTargetPathError
+
+from . import CoreCalendarTest
+
+
+class FakeCalendar(Calendar):
+    """Fake Calendar"""
+
+
+class IcalExportPeriodTest(CoreCalendarTest):
+    cal_class = FakeCalendar
+
+    def test_empty_range(self):
+        # No period means 2000-2030
+        result = self.cal._get_ical_period()
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2000)
+        self.assertEqual(result[1], 2030)
+        # No period means 2000-2030
+        result = self.cal._get_ical_period(period=())
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2000)
+        self.assertEqual(result[1], 2030)
+        # No period means 2000-2030
+        result = self.cal._get_ical_period(period=[])
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2000)
+        self.assertEqual(result[1], 2030)
+
+    def test_range_lists(self):
+        # Period: 2010-2020
+        result = self.cal._get_ical_period(period=[2010, 2020])
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2010)
+        self.assertEqual(result[1], 2020)
+
+        # Bad period: 2020-2010 => Swap terms
+        result = self.cal._get_ical_period(period=[2020, 2010])
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2010)
+        self.assertEqual(result[1], 2020)
+
+        # Too long of a list => take the extremes
+        result = self.cal._get_ical_period(period=[2010, 2015, 2020])
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2010)
+        self.assertEqual(result[1], 2020)
+
+        # Assume that the range is only one year
+        result = self.cal._get_ical_period(period=[2020, 2020])
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2020)
+        self.assertEqual(result[1], 2020)
+
+    def test_range_tuples(self):
+        # Period: 2010-2020
+        result = self.cal._get_ical_period(period=(2010, 2020))
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2010)
+        self.assertEqual(result[1], 2020)
+
+        # Bad period: 2020-2010 => Swap terms
+        result = self.cal._get_ical_period(period=(2020, 2010))
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2010)
+        self.assertEqual(result[1], 2020)
+
+        # Too long of a list => take the extremes
+        result = self.cal._get_ical_period(period=(2010, 2015, 2020))
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2010)
+        self.assertEqual(result[1], 2020)
+
+        # Assume that the range is only one year
+        result = self.cal._get_ical_period(period=(2020, 2020))
+        self.assertEqual(len(result), 2)
+        self.assertEqual(result[0], 2020)
+        self.assertEqual(result[1], 2020)
+
+    def test_bad_ranges(self):
+        # Bad period: String
+        with self.assertRaises(ICalExportRangeError):
+            self.cal._get_ical_period(period="something")
+
+        # Bad period: Dict
+        with self.assertRaises(ICalExportRangeError):
+            self.cal._get_ical_period(period={"a": 2000, "b": "foobar"})
+
+        # Bad period: List of non-integers
+        with self.assertRaises(ICalExportRangeError):
+            self.cal._get_ical_period(period=["foo", "bar"])
+
+        # Bad period: Tuple of non-integers
+        with self.assertRaises(ICalExportRangeError):
+            self.cal._get_ical_period(period=("foo", "bar"))
+
+
+class ICalExportTargetPath(CoreCalendarTest):
+    cal_class = FakeCalendar
+
+    def test_empty_path(self):
+        with self.assertRaises(ICalExportTargetPathError):
+            self.cal._get_ical_target_path("")
+
+        with self.assertRaises(ICalExportTargetPathError):
+            self.cal._get_ical_target_path(None)
+
+    def test_target_is_directory(self):
+        temp_dir = join(tempfile.gettempdir(), "failed_ical_tests")
+        os.makedirs(temp_dir, exist_ok=True)
+
+        with self.assertRaises(ICalExportTargetPathError):
+            self.cal._get_ical_target_path(temp_dir)
+
+    def test_no_extension(self):
+        # Relative paths
+        self.assertEqual(self.cal._get_ical_target_path("a"), "a.ics")
+        self.assertEqual(self.cal._get_ical_target_path("a/b"), "a/b.ics")
+        # Absolute path
+        self.assertEqual(
+            self.cal._get_ical_target_path("/path/to/a"),
+            "/path/to/a.ics"
+        )
+
+    def test_known_extensions(self):
+        for ext in ('ical', 'ics', 'ifb', 'icalendar'):
+            filename = f"a.{ext}"
+            self.assertEqual(
+                self.cal._get_ical_target_path(filename),
+                filename
+            )
+
+    def test_added_extensions(self):
+        self.assertEqual(self.cal._get_ical_target_path('a.'), 'a..ics')
+        self.assertEqual(self.cal._get_ical_target_path('a.txt'), 'a.txt.ics')
+        self.assertEqual(self.cal._get_ical_target_path('.test'), '.test.ics')
diff --git a/calendra/tests/test_mozambique.py b/calendra/tests/test_mozambique.py
new file mode 100644
index 00000000..0f68b87d
--- /dev/null
+++ b/calendra/tests/test_mozambique.py
@@ -0,0 +1,154 @@
+from datetime import date
+
+from . import GenericCalendarTest
+from ..africa.mozambique import Mozambique
+
+
+class MozambiqueTest(GenericCalendarTest):
+    cal_class = Mozambique
+
+    def test_year_new_year_shift(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 1, 1), holidays)
+        self.assertNotIn(date(2019, 1, 2), holidays)
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)
+        self.assertNotIn(date(2020, 1, 2), holidays)
+
+    def test_n_holidays(self):
+        n_holidays = len(self.cal.holidays_set(2019))
+        for holiday in self.cal.get_calendar_holidays(2020):
+            print(holiday)
+        assert n_holidays == 10
+
+    def test_year_2018(self):
+        holidays = self.cal.holidays_set(2018)
+        # Fixed days section:
+        # 1. New Year's Day
+        self.assertIn(date(2018, 1, 1), holidays)
+        # 2. Mozambican Heroes' Day
+        self.assertIn(date(2018, 2, 3), holidays)
+        # 3. Mozambican Women's Day
+        self.assertIn(date(2018, 4, 7), holidays)
+        # 4. Good Friday
+        self.assertIn(date(2018, 3, 30), holidays)
+        # 5. Labour Day
+        self.assertIn(date(2018, 5, 1), holidays)
+        # 6. Independence Day
+        self.assertIn(date(2018, 6, 25), holidays)
+        # 7. Victory Day
+        self.assertIn(date(2018, 9, 7), holidays)
+        # 8. Armed Forces Day
+        self.assertIn(date(2018, 9, 25), holidays)
+        # 9. Peace And Reconciliation Day
+        self.assertIn(date(2018, 10, 4), holidays)
+        # 10. Christmas day
+        self.assertIn(date(2018, 12, 25), holidays)
+
+    def test_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        # Fixed days section:
+        # 1. New Year's Day
+        self.assertIn(date(2019, 1, 1), holidays)
+        # 2. Mozambican Heroes' Day
+        self.assertIn(date(2019, 2, 3), holidays)
+        # 3. Mozambican Women's Day
+        self.assertIn(date(2019, 4, 7), holidays)
+        # 4. Good Friday
+        self.assertIn(date(2019, 4, 19), holidays)
+        # 5. Labour Day
+        self.assertIn(date(2019, 5, 1), holidays)
+        # 6. Independence Day
+        self.assertIn(date(2019, 6, 25), holidays)
+        # 7. Victory Day
+        self.assertIn(date(2019, 9, 7), holidays)
+        # 8. Armed Forces Day
+        self.assertIn(date(2019, 9, 25), holidays)
+        # 9. Peace And Reconciliation Day
+        self.assertIn(date(2019, 10, 4), holidays)
+        # 10. Christmas day
+        self.assertIn(date(2019, 12, 25), holidays)
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        # Fixed days section:
+        # 1. New Year's Day
+        self.assertIn(date(2020, 1, 1), holidays)
+        # 2. Mozambican Heroes' Day
+        self.assertIn(date(2020, 2, 3), holidays)
+        # 3. Mozambican Women's Day
+        self.assertIn(date(2020, 4, 7), holidays)
+        # 4. Good Friday
+        self.assertIn(date(2020, 4, 10), holidays)
+        # 5. Labour Day
+        self.assertIn(date(2020, 5, 1), holidays)
+        # 6. Independence Day
+        self.assertIn(date(2020, 6, 25), holidays)
+        # 7. Victory Day
+        self.assertIn(date(2020, 9, 7), holidays)
+        # 8. Armed Forces Day
+        self.assertIn(date(2020, 9, 25), holidays)
+        # 9. Peace And Reconciliation Day
+        self.assertIn(date(2020, 10, 4), holidays)
+        # 10. Christmas day
+        self.assertIn(date(2020, 12, 25), holidays)
+
+    def test_2020_new_years_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 1, 1)], "New year")
+
+    def test_2020_heroes_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 2, 3)], "Mozambican Heroes' Day")
+
+    def test_2020_women_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 4, 7)], "Mozambican Women's Day")
+
+    def test_2020_good_friday_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 4, 10)], "Good Friday")
+
+    def test_2020_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "Labour Day")
+
+    def test_2020_independence_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 6, 25)], "Independence Day")
+
+    def test_2020_victory_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 9, 7)], "Victory Day")
+
+    def test_2020_armed_forces_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 9, 25)], "Armed Forces Day")
+
+    def test_2020_peace_and_reconciliation_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 10, 4)], "Peace And Reconciliation Day")
+
+    def test_2020_christmas_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 12, 25)], "Christmas Day")
diff --git a/calendra/tests/test_oceania.py b/calendra/tests/test_oceania.py
index 5220e901..6278fdd7 100644
--- a/calendra/tests/test_oceania.py
+++ b/calendra/tests/test_oceania.py
@@ -285,13 +285,26 @@ def test_year_2018(self):
         self.assertIn(date(2018, 12, 25), holidays)  # Christmas Day
         self.assertIn(date(2018, 12, 26), holidays)  # Boxing Day
 
-    def test_new_year_shift(self):
+    def test_new_year_shift_on_sunday(self):
         holidays = self.cal.holidays_set(2012)
-        # New Years was on a sunday
+        # New Years was on a SUN
         # Day After New Years is on the 2nd
         self.assertIn(date(2012, 1, 2), holidays)
         # New Years observed on the 3rd
         assert self.cal.is_observed_holiday(date(2012, 1, 3))
+        # No shift of a shift
+        self.assertNotIn(date(2012, 1, 4), holidays)
+
+    def test_new_year_shift_on_saturday(self):
+        holidays = self.cal.holidays_set(2022)
+        # New Years is on a SAT
+        # The day after is still a holiday
+        self.assertIn(date(2022, 1, 2), holidays)
+        # The shift is on the MON, 3rd
+        observed = set(map(self.cal.get_observed_date, holidays))
+        self.assertIn(date(2022, 1, 3), observed)
+        # And there's a shift of the shift
+        self.assertIn(date(2022, 1, 4), observed)
 
     def test_anzac_shift(self):
         holidays = self.cal.holidays_set(2010)
diff --git a/calendra/tests/test_registry.py b/calendra/tests/test_registry.py
index ad4e8abd..43d733f9 100644
--- a/calendra/tests/test_registry.py
+++ b/calendra/tests/test_registry.py
@@ -1,5 +1,4 @@
 from unittest import TestCase
-import warnings
 
 from ..core import Calendar
 from ..exceptions import ISORegistryError
@@ -36,30 +35,17 @@ def test_register_non_calendar(self):
         with self.assertRaises(ISORegistryError):
             registry.register("NAC", NotACalendarClass)
 
-    def test_get_calendar_class(self):
+    def test_get(self):
         registry = IsoRegistry(load_standard_modules=False)
         registry.register('RE', self.region)
         registry.register('RE-SR', self.subregion)
-        calendar_class = registry.get_calendar_class('RE')
+        calendar_class = registry.get('RE')
         self.assertEqual(calendar_class, RegionCalendar)
         # Subregion
-        calendar_class = registry.get_calendar_class('RE-SR')
+        calendar_class = registry.get('RE-SR')
         self.assertEqual(calendar_class, SubRegionCalendar)
         # Unknown code/region
-        self.assertIsNone(registry.get_calendar_class('XX'))
-
-    def test_items_deprecation(self):
-        registry = IsoRegistry(load_standard_modules=False)
-        with warnings.catch_warnings(record=True) as w:
-            # Cause all warnings to always be triggered.
-            warnings.simplefilter("always")
-            # Trigger a warning.
-            registry.items()
-            # Verify some things
-            self.assertEqual(len(w), 1)
-            warning = w[0]
-            self.assertTrue(issubclass(warning.category, DeprecationWarning))
-            self.assertIn("deprecated", str(warning.message))
+        self.assertIsNone(registry.get('XX'))
 
     def test_get_subregions(self):
         registry = IsoRegistry(load_standard_modules=False)
@@ -144,7 +130,7 @@ def test_get_calendars_no_arg(self):
         self.assertEqual(set({"RE", "RE2", "RE3"}), set(calendars.keys()))
 
         # Should be equivalent to [] + include subregions
-        calendars = registry.items(include_subregions=True)
+        calendars = registry.get_calendars(include_subregions=True)
         self.assertEqual(len(calendars), 4)
         self.assertEqual(
             set({"RE", "RE2", "RE3", "RE-SR"}),
diff --git a/calendra/tests/test_registry_europe.py b/calendra/tests/test_registry_europe.py
index dafbcf67..7bc5b3f0 100644
--- a/calendra/tests/test_registry_europe.py
+++ b/calendra/tests/test_registry_europe.py
@@ -4,13 +4,29 @@
     Denmark, Finland, France,
     # FranceAlsaceMoselle,  # TODO: Should we add it to the registry?
     Greece, Hungary, Iceland, Ireland, Italy, Latvia, Lithuania, Luxembourg,
-    Malta, Netherlands, Norway, Poland, Portugal, Romania, Russia, Slovakia,
-    Slovenia, Spain,
-    # Catalonia,  # TODO: Add it to registry
-    Sweden, Switzerland, Vaud, Geneva, UnitedKingdom,
+    Malta, Monaco, Netherlands, Norway, Poland, Portugal, Romania, Russia,
+    Slovakia, Slovenia,
+    Sweden, UnitedKingdom,
     UnitedKingdomNorthernIreland,
 )
 
+# Spain & regions
+from ..europe import (
+    Spain, Andalusia, Aragon, Catalonia, CastileAndLeon, CastillaLaMancha,
+    CanaryIslands, Extremadura, Galicia, BalearicIslands, LaRioja,
+    CommunityofMadrid, Murcia, Navarre, Asturias, BasqueCountry, Cantabria,
+    ValencianCommunity,
+)
+
+# Switzerland
+from ..europe import (
+    Switzerland,
+    Aargau, AppenzellInnerrhoden, AppenzellAusserrhoden, Bern, BaselLandschaft,
+    BaselStadt, Fribourg, Geneva, Glarus, Graubunden, Jura, Luzern, Neuchatel,
+    Nidwalden, Obwalden, StGallen, Schaffhausen, Solothurn, Schwyz, Thurgau,
+    Ticino, Uri, Vaud, Valais, Zug, Zurich
+)
+
 # Germany
 from ..europe import (
     Germany, BadenWurttemberg, Bavaria, Berlin, Brandenburg, Bremen,
@@ -31,6 +47,20 @@
     SaxonyAnhalt, SchleswigHolstein, Thuringia
 )
 
+SWITZERLAND_REGION_CLASSES = (
+    Aargau, AppenzellInnerrhoden, AppenzellAusserrhoden, Bern, BaselLandschaft,
+    BaselStadt, Fribourg, Geneva, Glarus, Graubunden, Jura, Luzern, Neuchatel,
+    Nidwalden, Obwalden, StGallen, Schaffhausen, Solothurn, Schwyz, Thurgau,
+    Ticino, Uri, Vaud, Valais, Zug, Zurich
+)
+
+SPAIN_REGION_CLASSES = (
+    Andalusia, Aragon, Catalonia, CastileAndLeon, CastillaLaMancha,
+    CanaryIslands, Extremadura, Galicia, BalearicIslands, LaRioja,
+    CommunityofMadrid, Murcia, Navarre, Asturias, BasqueCountry, Cantabria,
+    ValencianCommunity,
+)
+
 
 class RegistryEurope(TestCase):
     def test_europe(self):
@@ -53,6 +83,7 @@ def test_europe(self):
         self.assertIn(Lithuania, classes)
         self.assertIn(Luxembourg, classes)
         self.assertIn(Malta, classes)
+        self.assertIn(Monaco, classes)
         self.assertIn(Netherlands, classes)
         self.assertIn(Norway, classes)
         self.assertIn(Poland, classes)
@@ -72,14 +103,32 @@ def test_europe(self):
         self.assertIn(Germany, classes)
         for klass in GERMANY_REGION_CLASSES:
             self.assertIn(klass, classes)
+        for klass in SWITZERLAND_REGION_CLASSES:
+            self.assertIn(klass, classes)
+        for klass in SPAIN_REGION_CLASSES:
+            self.assertIn(klass, classes)
 
-    def test_germany_subregion(self):
+    def test_germany_subregions(self):
         # Get all the subregions
         classes = (v for k, v in registry.get_subregions('DE').items())
         classes = list(classes)
         for klass in GERMANY_REGION_CLASSES:
             self.assertIn(klass, classes)
 
+    def test_switzerland_subregions(self):
+        # Get all the subregions
+        classes = (v for k, v in registry.get_subregions('CH').items())
+        classes = list(classes)
+        for klass in SWITZERLAND_REGION_CLASSES:
+            self.assertIn(klass, classes)
+
+    def test_spain_subregions(self):
+        # Get all the subregions
+        classes = (v for k, v in registry.get_subregions('ES').items())
+        classes = list(classes)
+        for klass in SPAIN_REGION_CLASSES:
+            self.assertIn(klass, classes)
+
     def test_slovenia_code(self):
         # Source: https://github.com/peopledoc/workalendar/pull/291
         self.assertEqual(registry.region_registry['SI'], Slovenia)
diff --git a/calendra/tests/test_spain.py b/calendra/tests/test_spain.py
new file mode 100644
index 00000000..f593e120
--- /dev/null
+++ b/calendra/tests/test_spain.py
@@ -0,0 +1,344 @@
+from datetime import date
+
+from . import GenericCalendarTest
+from ..europe import (
+    Spain, Andalusia, Aragon, Catalonia, CastileAndLeon, CastillaLaMancha,
+    CanaryIslands, Extremadura, Galicia, BalearicIslands, LaRioja,
+    CommunityofMadrid, Murcia, Navarre, Asturias, BasqueCountry, Cantabria,
+    ValencianCommunity
+)
+
+
+class SpainTest(GenericCalendarTest):
+    cal_class = Spain
+
+    def test_common_year_2015(self):
+        holidays = self.cal.holidays_set(2015)
+        self.assertIn(date(2015, 1, 1), holidays)    # New Year
+        self.assertIn(date(2015, 1, 6), holidays)    # Epiphany
+        self.assertIn(date(2015, 4, 3), holidays)    # Good Friday
+        self.assertIn(date(2015, 5, 1), holidays)    # Labour Day
+        self.assertIn(date(2015, 8, 15), holidays)   # Assumption
+        self.assertIn(date(2015, 10, 12), holidays)  # Nation Day
+        self.assertIn(date(2015, 11, 1), holidays)   # All Saints
+        self.assertIn(date(2015, 12, 6), holidays)   # Dia de la Constitución
+        self.assertIn(date(2015, 12, 8), holidays)   # Immaculate conception
+        self.assertIn(date(2015, 12, 25), holidays)  # Christmas
+
+    def test_common_year_2016(self):
+        holidays = self.cal.holidays_set(2016)
+        self.assertIn(date(2016, 1, 1), holidays)    # New Year
+        self.assertIn(date(2016, 1, 6), holidays)    # Epiphany
+        self.assertIn(date(2016, 3, 25), holidays)   # Good Friday
+        self.assertIn(date(2016, 5, 1), holidays)    # Labour Day
+        self.assertIn(date(2016, 8, 15), holidays)   # Assumption
+        self.assertIn(date(2016, 10, 12), holidays)  # Nation Day
+        self.assertIn(date(2016, 11, 1), holidays)   # All Saints
+        self.assertIn(date(2016, 12, 6), holidays)   # Dia de la Constitución
+        self.assertIn(date(2016, 12, 8), holidays)   # Immaculate conception
+        self.assertIn(date(2016, 12, 25), holidays)  # Christmas
+
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "Día del trabajador")
+
+
+class AndalusiaTest(SpainTest):
+    cal_class = Andalusia
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 2, 28), holidays)  # Andalusian National Day
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 2, 28), holidays)  # Andalusian National Day
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertEqual(len(holidays), 12)
+
+
+class AragonTest(SpainTest):
+    cal_class = Aragon
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 4, 23), holidays)  # Regional Day
+        self.assertIn(date(2019, 12, 20), holidays)  # Aragon Ombudsman Day
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 4, 23), holidays)  # Regional Day
+        self.assertIn(date(2020, 12, 20), holidays)  # Aragon Ombudsman Day
+        self.assertEqual(len(holidays), 13)
+
+
+class CastileAndLeonTest(SpainTest):
+    cal_class = CastileAndLeon
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 4, 23), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 4, 23), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+
+class CastillaLaManchaTest(SpainTest):
+    cal_class = CastillaLaMancha
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 5, 31), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 5, 31), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+
+class CanaryIslandsTest(SpainTest):
+    cal_class = CanaryIslands
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 5, 30), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 5, 30), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+
+class CataloniaTest(SpainTest):
+    cal_class = Catalonia
+
+    def test_region_year_2015(self):
+        holidays = self.cal.holidays_set(2015)
+        self.assertIn(date(2015, 4, 6), holidays)  # Easter Monday
+        # Diada nacional de Catalunya
+        self.assertIn(date(2015, 9, 11), holidays)
+        self.assertIn(date(2015, 12, 26), holidays)  # Sant Esteve
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2016(self):
+        holidays = self.cal.holidays_set(2016)
+        self.assertIn(date(2016, 3, 28), holidays)  # Easter Monday
+        # Diada nacional de Catalunya
+        self.assertIn(date(2016, 9, 11), holidays)
+        self.assertIn(date(2016, 12, 26), holidays)  # Sant Esteve
+        self.assertEqual(len(holidays), 13)
+
+    def test_boxing_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(holidays[date(2020, 12, 26)], "Sant Esteve")
+
+
+class ExtremaduraTest(SpainTest):
+    cal_class = Extremadura
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 9, 8), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 9, 8), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 12)
+
+
+class GaliciaTest(SpainTest):
+    cal_class = Galicia
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 5, 17), holidays)  # Galician Literature Day
+        self.assertIn(date(2019, 7, 25), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 5, 17), holidays)  # Galician Literature Day
+        self.assertIn(date(2020, 7, 25), holidays)  # Regional Day
+        self.assertEqual(len(holidays), 13)
+
+
+class BalearicIslandsTest(SpainTest):
+    cal_class = BalearicIslands
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 3, 1), holidays)  # Balearic National Day
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 4, 22), holidays)  # Easter Monday
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 3, 1), holidays)  # Balearic National Day
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertEqual(len(holidays), 13)
+
+
+class LaRiojaTest(SpainTest):
+    cal_class = LaRioja
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 6, 9), holidays)  # Dia de La Rioja
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 6, 9), holidays)  # Dia de La Rioja
+        self.assertEqual(len(holidays), 12)
+
+
+class CommunityofMadridTest(SpainTest):
+    cal_class = CommunityofMadrid
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        # Fiesta de la Comunidad de Madrid
+        self.assertIn(date(2019, 5, 2), holidays)
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        # Fiesta de la Comunidad de Madrid
+        self.assertIn(date(2020, 5, 2), holidays)
+        self.assertEqual(len(holidays), 12)
+
+
+class MurciaTest(SpainTest):
+    cal_class = Murcia
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 3, 19), holidays)  # San José
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 6, 9), holidays)  # Día de la Región de Murcia
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 3, 19), holidays)  # San José
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 6, 9), holidays)  # Día de la Región de Murcia
+        self.assertEqual(len(holidays), 13)
+
+
+class NavarreTest(SpainTest):
+    cal_class = Navarre
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 4, 22), holidays)  # Easter Monday
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertEqual(len(holidays), 12)
+
+
+class AsturiasTest(SpainTest):
+    cal_class = Asturias
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 9, 8), holidays)  # Día de Asturias
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 9, 8), holidays)  # Día de Asturias
+        self.assertEqual(len(holidays), 12)
+
+
+class BasqueCountryTest(SpainTest):
+    cal_class = BasqueCountry
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2019, 4, 22), holidays)  # Easter Monday
+        self.assertIn(date(2019, 10, 25), holidays)  # Euskadi Eguna
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 10, 25), holidays)  # Euskadi Eguna
+        self.assertEqual(len(holidays), 13)
+
+
+class CantabriaTest(SpainTest):
+    cal_class = Cantabria
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 4, 18), holidays)  # Maundy/Holy Thursday
+        # Día de Cantabria o Día de La Montaña
+        self.assertIn(date(2019, 9, 15), holidays)
+        self.assertEqual(len(holidays), 12)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 4, 9), holidays)  # Maundy/Holy Thursday
+        # Día de Cantabria o Día de La Montaña
+        self.assertIn(date(2020, 9, 15), holidays)
+        self.assertEqual(len(holidays), 12)
+
+
+class ValencianCommunityTest(SpainTest):
+    cal_class = ValencianCommunity
+
+    def test_region_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 3, 19), holidays)  # San José
+        self.assertIn(date(2019, 4, 22), holidays)  # Easter Monday
+        # Dia de la Comunitat Valenciana
+        self.assertIn(date(2019, 10, 9), holidays)
+        self.assertEqual(len(holidays), 13)
+
+    def test_region_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 3, 19), holidays)  # San José
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        # Dia de la Comunitat Valenciana
+        self.assertIn(date(2020, 10, 9), holidays)
+        self.assertEqual(len(holidays), 13)
diff --git a/calendra/tests/test_switzerland.py b/calendra/tests/test_switzerland.py
new file mode 100644
index 00000000..29d16c48
--- /dev/null
+++ b/calendra/tests/test_switzerland.py
@@ -0,0 +1,879 @@
+from datetime import date
+
+from . import GenericCalendarTest
+from ..europe import (
+    Aargau, AppenzellInnerrhoden, AppenzellAusserrhoden, Bern, BaselLandschaft,
+    BaselStadt, Fribourg, Geneva, Glarus, Graubunden, Jura, Luzern, Neuchatel,
+    Nidwalden, Obwalden, StGallen, Schaffhausen, Solothurn, Schwyz, Thurgau,
+    Ticino, Uri, Vaud, Valais, Zug, Zurich
+)
+
+
+class AargauTest(GenericCalendarTest):
+    cal_class = Aargau
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class AppenzellInnerrhodenTest(GenericCalendarTest):
+    cal_class = AppenzellInnerrhoden
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class AppenzellAusserrhodenTest(GenericCalendarTest):
+    cal_class = AppenzellAusserrhoden
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class BernTest(GenericCalendarTest):
+    cal_class = Bern
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class BaselLandschaftTest(GenericCalendarTest):
+    cal_class = BaselLandschaft
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class BaselStadtTest(GenericCalendarTest):
+    cal_class = BaselStadt
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class FribourgTest(GenericCalendarTest):
+    cal_class = Fribourg
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class GenevaTest(GenericCalendarTest):
+    cal_class = Geneva
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        # Jeune Genevois, only in Geneva
+        self.assertIn(date(2020, 9, 10), holidays)
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        # Restoration day - Geneva
+        self.assertIn(date(2020, 12, 31), holidays)
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        # Jeune Genevois, only in Geneva
+        self.assertIn(date(2021, 9, 9), holidays)
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        # Restoration day - Geneva
+        self.assertIn(date(2021, 12, 31), holidays)
+
+
+class GlarusTest(GenericCalendarTest):
+    cal_class = Glarus
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        # Only in Glarus/Glaris
+        self.assertIn(date(2020, 4, 3), holidays)   # Näfels Day
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        # Only in Glarus/Glaris
+        self.assertIn(date(2021, 4, 3), holidays)   # Näfels Day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class GraubundenTest(GenericCalendarTest):
+    cal_class = Graubunden
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class JuraTest(GenericCalendarTest):
+    cal_class = Jura
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        # Independance day - Only in Jura
+        self.assertIn(date(2020, 6, 23), holidays)
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        # Independance day - Only in Jura
+        self.assertIn(date(2021, 6, 23), holidays)
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+
+
+class LuzernTest(GenericCalendarTest):
+    cal_class = Luzern
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class NeuchatelTest(GenericCalendarTest):
+    cal_class = Neuchatel
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 3, 1), holidays)  # Republic Day
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 3, 1), holidays)  # Republic Day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+
+
+class NidwaldenTest(GenericCalendarTest):
+    cal_class = Nidwalden
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class ObwaldenTest(GenericCalendarTest):
+    cal_class = Obwalden
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 9, 25), holidays)  # Saint Nicholas of Flüe
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 9, 25), holidays)  # Saint Nicholas of Flüe
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class StGallenTest(GenericCalendarTest):
+    cal_class = StGallen
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class SchaffhausenTest(GenericCalendarTest):
+    cal_class = Schaffhausen
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class SolothurnTest(GenericCalendarTest):
+    cal_class = Solothurn
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class SchwyzTest(GenericCalendarTest):
+    cal_class = Schwyz
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class ThurgauTest(GenericCalendarTest):
+    cal_class = Thurgau
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class TicinoTest(GenericCalendarTest):
+    cal_class = Ticino
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        # St Peter & St Paul - Only in Ticino
+        self.assertIn(date(2020, 6, 29), holidays)
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        # St Peter & St Paul - Only in Ticino
+        self.assertIn(date(2021, 6, 29), holidays)
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class UriTest(GenericCalendarTest):
+    cal_class = Uri
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 6), holidays)  # Epiphany
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class VaudTest(GenericCalendarTest):
+    cal_class = Vaud
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        # Lundi du Jeûne - Only in Vaud
+        self.assertIn(date(2020, 9, 21), holidays)
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        # Lundi du Jeûne - Only in Vaud
+        self.assertIn(date(2021, 9, 20), holidays)
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+
+
+class ValaisTest(GenericCalendarTest):
+    cal_class = Valais
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 3, 19), holidays)  # St Joseph
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+
+
+class ZugTest(GenericCalendarTest):
+    cal_class = Zug
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2020, 4, 10), holidays)  # Good Friday
+        self.assertIn(date(2020, 4, 13), holidays)  # Easter Monday
+        self.assertIn(date(2020, 5, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 6, 11), holidays)  # Corpus Christi
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2020, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2020, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 6, 3), holidays)  # Corpus Christi
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 8, 15), holidays)  # Assumption
+        self.assertIn(date(2021, 11, 1), holidays)  # All Saints
+        self.assertIn(date(2021, 12, 8), holidays)  # Immaculate conception
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
+
+
+class ZurichTest(GenericCalendarTest):
+    cal_class = Zurich
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)  # New years day
+        self.assertIn(date(2020, 1, 2), holidays)  # Berchtolds
+        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, 21), holidays)  # Ascension day
+        self.assertIn(date(2020, 6, 1), holidays)  # Whit Monday
+        self.assertIn(date(2020, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMas
+        self.assertIn(date(2020, 12, 26), holidays)  # St Stephen's day
+
+    def test_year_2021(self):
+        holidays = self.cal.holidays_set(2021)
+        self.assertIn(date(2021, 1, 1), holidays)  # New years day
+        self.assertIn(date(2021, 1, 2), holidays)  # Berchtolds
+        self.assertIn(date(2021, 4, 2), holidays)  # Good Friday
+        self.assertIn(date(2021, 4, 5), holidays)  # Easter Monday
+        self.assertIn(date(2021, 5, 1), holidays)  # Labour Day
+        self.assertIn(date(2021, 5, 13), holidays)  # Ascension day
+        self.assertIn(date(2021, 5, 24), holidays)  # Whit Monday
+        self.assertIn(date(2021, 8, 1), holidays)  # Swiss National Day
+        self.assertIn(date(2021, 12, 25), holidays)  # XMas
+        self.assertIn(date(2021, 12, 26), holidays)  # St Stephen's day
diff --git a/calendra/tests/test_turkey.py b/calendra/tests/test_turkey.py
index e5b05dcd..52921f7a 100644
--- a/calendra/tests/test_turkey.py
+++ b/calendra/tests/test_turkey.py
@@ -79,3 +79,9 @@ def test_year_2019(self):
         self.assertIn(date(2019, 8, 12), holidays)
         self.assertIn(date(2019, 8, 13), holidays)
         self.assertIn(date(2019, 8, 14), holidays)
+
+    def test_labour_day_label(self):
+        holidays = self.cal.holidays(2020)
+        holidays = dict(holidays)
+        self.assertEqual(
+            holidays[date(2020, 5, 1)], "Labor and Solidarity Day")
diff --git a/calendra/tests/test_usa.py b/calendra/tests/test_usa.py
index 05efbe8e..47cc0d31 100644
--- a/calendra/tests/test_usa.py
+++ b/calendra/tests/test_usa.py
@@ -224,7 +224,7 @@ def test_veterans_label(self):
 
     def test_mardi_gras(self):
         year = 2017
-        day, _ = self.cal.get_mardi_gras(year)
+        day = self.cal.get_fat_tuesday(year)
         holidays = self.cal.holidays_set(year)
         self.assertNotIn(day, holidays)
 
@@ -333,9 +333,11 @@ class IncludeMardiGras:
     """
     def test_mardi_gras(self):
         year = 2017
-        day, _ = self.cal.get_mardi_gras(year)
-        holidays = self.cal.holidays_set(year)
-        self.assertIn(day, holidays)
+        day = self.cal.get_fat_tuesday(year)
+        holidays = self.cal.holidays(year)
+        holidays_dict = dict(holidays)
+        self.assertIn(day, holidays_dict)
+        self.assertEqual(holidays_dict[day], "Mardi Gras")
 
 
 class AlabamaTest(UnitedStatesTest):
@@ -473,11 +475,27 @@ def test_state_year_2015(self):
         self.assertIn(date(2015, 3, 31), holidays)  # Cesar Chavez Day
         self.assertIn(date(2015, 11, 27), holidays)  # Thanksgiving Friday
 
+    def test_state_year_2018(self):
+        holidays = self.cal.holidays_set(2018)
+        self.assertIn(date(2018, 3, 31), holidays)  # Cesar Chavez Day
+        # Happens on SAT, but is not shifted
+        self.assertNotIn(date(2018, 3, 30), holidays)
+        self.assertIn(date(2018, 11, 23), holidays)  # Thanksgiving Friday
+
     def test_state_year_2019(self):
         holidays = self.cal.holidays_set(2019)
         self.assertIn(date(2019, 3, 31), holidays)  # Cesar Chavez Day
+        self.assertIn(date(2019, 4, 1), holidays)  # Cesar Chavez Day Shift
         self.assertIn(date(2019, 11, 29), holidays)  # Thanksgiving Friday
 
+    def test_chavez_no_duplicates(self):
+        # See issue #528
+        holidays = self.cal.holidays(2019)
+        days = [item[0] for item in holidays]
+        assert days
+        for day in days:
+            assert days.count(day) == 1, f"{day} is duplicated"
+
 
 class CaliforniaEducationTest(CaliforniaTest):
     cal_class = CaliforniaEducation
@@ -788,44 +806,117 @@ class GeorgiaTest(NoPresidentialDay, UnitedStatesTest):
     def test_state_year_2014(self):
         holidays = self.cal.holidays_set(2014)
         self.assertIn(date(2014, 4, 28), holidays)  # Confederate Memorial
-        # FIXME: this holiday rule is Confusing, probably false
         self.assertIn(date(2014, 12, 26), holidays)  # Washington bday
 
     def test_state_year_2015(self):
         holidays = self.cal.holidays_set(2015)
         self.assertIn(date(2015, 4, 27), holidays)  # Confederate Memorial
-        # FIXME: this holiday rule is Confusing, probably false
         self.assertIn(date(2015, 12, 24), holidays)  # Washington bday
 
-    @skip("Confusing Rule, it's impossible to decide")
     def test_washington_birthday(self):
-        # Source: https://georgia.gov/popular-topic/observing-state-holidays
+        # Sources:
+        # * https://georgia.gov/popular-topic/observing-state-holidays
+        # * https://georgia.gov/popular-topic/state-holidays
+        day, _ = self.cal.get_washington_birthday_december(2020)
+        self.assertEqual(day, date(2020, 12, 24))
+
+        day, _ = self.cal.get_washington_birthday_december(2019)
+        self.assertEqual(day, date(2019, 12, 24))
+
+        day, _ = self.cal.get_washington_birthday_december(2018)
+        self.assertEqual(day, date(2018, 12, 24))
+
         day, _ = self.cal.get_washington_birthday_december(2017)
         self.assertEqual(day, date(2017, 12, 26))
 
         day, _ = self.cal.get_washington_birthday_december(2016)
-        self.assertEqual(
-            day,
-            date(2016, 12, 27),
-        )
+        self.assertEqual(day, date(2016, 12, 27))
 
         day, _ = self.cal.get_washington_birthday_december(2015)
-        self.assertEqual(
-            day,
-            date(2015, 12, 24),
-        )
+        self.assertEqual(day, date(2015, 12, 24))
 
         day, _ = self.cal.get_washington_birthday_december(2014)
-        self.assertEqual(
-            day,
-            date(2014, 12, 26),
-        )
+        self.assertEqual(day, date(2014, 12, 26))
 
         day, _ = self.cal.get_washington_birthday_december(2013)
-        self.assertEqual(
-            day,
-            date(2013, 12, 24),
-        )
+        self.assertEqual(day, date(2013, 12, 24))
+
+        day, _ = self.cal.get_washington_birthday_december(2012)
+        self.assertEqual(day, date(2012, 12, 24))
+
+        # Source:
+        # https://web.archive.org/web/20110927122533/http://www.georgia.gov/00/channel_modifieddate/0,2096,4802_64437763,00.html  # noqa
+        day, _ = self.cal.get_washington_birthday_december(2011)
+        self.assertEqual(day, date(2011, 12, 26))
+
+        # Source:
+        # https://web.archive.org/web/20100304032739/http://www.georgia.gov/00/channel_modifieddate/0,2096,4802_64437763,00.html  # noqa
+        day, _ = self.cal.get_washington_birthday_december(2010)
+        self.assertEqual(day, date(2010, 12, 23))
+
+    def test_year_2019(self):
+        holidays = self.cal.holidays_set(2019)
+        self.assertIn(date(2019, 1, 1), holidays)   # New Year
+        self.assertIn(date(2019, 1, 21), holidays)  # MLK
+        self.assertIn(date(2019, 4, 22), holidays)  # state holiday
+        self.assertIn(date(2019, 5, 27), holidays)  # memorial day
+        self.assertIn(date(2019, 7, 4), holidays)  # Independance day
+        self.assertIn(date(2019, 9, 2), holidays)  # Labor day
+        self.assertIn(date(2019, 10, 14), holidays)  # Columbus
+        self.assertIn(date(2019, 11, 11), holidays)  # Veterans
+        self.assertIn(date(2019, 11, 28), holidays)  # Thanksgiving
+        self.assertIn(date(2019, 11, 29), holidays)  # State Holiday
+        # Washington's Birthday switched to XMAS eve
+        self.assertIn(date(2019, 12, 24), holidays)
+        self.assertIn(date(2019, 12, 25), holidays)  # XMAS
+
+    def test_year_2020(self):
+        holidays = self.cal.holidays_set(2020)
+        self.assertIn(date(2020, 1, 1), holidays)   # New Year
+        self.assertIn(date(2020, 1, 20), holidays)  # MLK
+
+        # State holiday special case
+        # Confederate memorial day has been shifted to April 10th.
+        # Reason is unknown, so we're adding a single exception in the
+        # `get_confederate_day`
+        self.assertNotIn(date(2020, 4, 26), holidays)
+        self.assertIn(date(2020, 4, 10), holidays)
+
+        self.assertIn(date(2020, 5, 25), holidays)  # memorial day
+        observed = set(map(self.cal.get_observed_date, holidays))
+        # Independance day (OBS)
+        self.assertIn(date(2020, 7, 3), observed)
+        self.assertIn(date(2020, 7, 4), holidays)  # Independance day
+        self.assertIn(date(2020, 9, 7), holidays)  # Labor day
+        self.assertIn(date(2020, 10, 12), holidays)  # Columbus
+        self.assertIn(date(2020, 11, 11), holidays)  # Veterans
+        self.assertIn(date(2020, 11, 26), holidays)  # Thanksgiving
+        self.assertIn(date(2020, 11, 27), holidays)  # State Holiday
+        self.assertIn(date(2020, 12, 24), holidays)  # Washington B'day
+        self.assertIn(date(2020, 12, 25), holidays)  # XMAS
+
+    def test_thanksgiving_friday_label(self):
+        # Overwrite UnitedStatesTest.test_thanksgiving_friday_label
+
+        # Until 2016, the 4th FRI of november was labelled
+        # "Robert E. Lee's Birthday (Observed)"
+        for year in (2013, 2014, 2015,):
+            _, label = self.cal.get_robert_lee_birthday(year)
+            self.assertEqual(label, "Robert E. Lee's Birthday (Observed)")
+
+        for year in (2016, 2017, 2018, 2019, 2020):
+            _, label = self.cal.get_robert_lee_birthday(year)
+            self.assertEqual(label, "State Holiday")
+
+    def test_get_confederate_day_label(self):
+        # Until 2016, it was labelled "Confederate Memorial Day"
+        for year in (2013, 2014, 2015,):
+            _, label = self.cal.get_confederate_day(year)
+            self.assertEqual(label, "Confederate Memorial Day")
+
+        for year in (2016, 2017, 2018, 2019, 2020):
+            _, label = self.cal.get_confederate_day(year)
+            self.assertEqual(label, "State Holiday")
 
 
 class HawaiiTest(ElectionDayEvenYears, NoColumbus, UnitedStatesTest):