From b34747233403561c0f27251894ac1a7d47869949 Mon Sep 17 00:00:00 2001 From: Bruno Bord Date: Fri, 12 Jun 2020 18:09:10 +0200 Subject: [PATCH] Refactoring core classes * 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). * Fixed Belarus holidays related to the Orthodox calendar * Fixed Algeria week-end days --- Changelog.md | 6 +- contributing.md | 10 +- workalendar/africa/algeria.py | 17 +- workalendar/africa/angola.py | 5 +- workalendar/africa/benin.py | 10 +- workalendar/africa/ivory_coast.py | 11 +- workalendar/africa/kenya.py | 10 +- workalendar/africa/madagascar.py | 4 +- workalendar/africa/sao_tome.py | 4 +- workalendar/africa/south_africa.py | 6 +- workalendar/america/argentina.py | 7 +- workalendar/america/barbados.py | 5 +- workalendar/america/brazil.py | 5 +- workalendar/america/canada.py | 57 +- workalendar/america/chile.py | 5 +- workalendar/america/colombia.py | 4 +- workalendar/america/mexico.py | 5 +- workalendar/america/panama.py | 4 +- workalendar/america/paraguay.py | 4 +- workalendar/asia/china.py | 6 +- workalendar/asia/hong_kong.py | 6 +- workalendar/asia/japan.py | 10 +- workalendar/asia/malaysia.py | 14 +- workalendar/asia/qatar.py | 6 +- workalendar/asia/singapore.py | 15 +- workalendar/asia/south_korea.py | 6 +- workalendar/asia/taiwan.py | 6 +- workalendar/core.py | 892 ++++++++++---------- workalendar/europe/austria.py | 4 +- workalendar/europe/belarus.py | 17 +- workalendar/europe/belgium.py | 4 +- workalendar/europe/bulgaria.py | 4 +- workalendar/europe/cayman_islands.py | 4 +- workalendar/europe/croatia.py | 4 +- workalendar/europe/cyprus.py | 4 +- workalendar/europe/czech_republic.py | 4 +- workalendar/europe/denmark.py | 4 +- workalendar/europe/estonia.py | 4 +- workalendar/europe/european_central_bank.py | 4 +- workalendar/europe/finland.py | 4 +- workalendar/europe/france.py | 4 +- workalendar/europe/germany.py | 4 +- workalendar/europe/greece.py | 6 +- workalendar/europe/hungary.py | 4 +- workalendar/europe/iceland.py | 4 +- workalendar/europe/ireland.py | 4 +- workalendar/europe/italy.py | 4 +- workalendar/europe/latvia.py | 4 +- workalendar/europe/lithuania.py | 4 +- workalendar/europe/luxembourg.py | 4 +- workalendar/europe/malta.py | 4 +- workalendar/europe/netherlands.py | 4 +- workalendar/europe/norway.py | 4 +- workalendar/europe/poland.py | 4 +- workalendar/europe/portugal.py | 4 +- workalendar/europe/romania.py | 8 +- workalendar/europe/russia.py | 6 +- workalendar/europe/scotland/__init__.py | 4 +- workalendar/europe/serbia.py | 6 +- workalendar/europe/slovakia.py | 4 +- workalendar/europe/slovenia.py | 4 +- workalendar/europe/spain.py | 4 +- workalendar/europe/sweden.py | 4 +- workalendar/europe/switzerland.py | 4 +- workalendar/europe/turkey.py | 9 +- workalendar/europe/ukraine.py | 6 +- workalendar/europe/united_kingdom.py | 4 +- workalendar/oceania/australia.py | 5 +- workalendar/oceania/marshall_islands.py | 5 +- workalendar/oceania/new_zealand.py | 4 +- workalendar/tests/__init__.py | 19 +- workalendar/tests/test_core.py | 36 +- workalendar/tests/test_europe.py | 24 +- workalendar/usa/core.py | 4 +- 74 files changed, 758 insertions(+), 665 deletions(-) diff --git a/Changelog.md b/Changelog.md index 5bc5453..d7a04e1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,11 @@ ## master (unreleased) -Nothing here yet. +- 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). +- Fixed `contributing.md` documentation with the new class/mixin organization. +- Bugfix -- Belarus: removing day after Radonitsa, which is apparently not a holiday. +- Bugfix -- Algeria: assigning the week-end days as FRI+SAT, as it's following a Islamic calendar. + ## v10.2.0 (2020-06-26) diff --git a/contributing.md b/contributing.md index 52d6000..22cda14 100644 --- a/contributing.md +++ b/contributing.md @@ -133,12 +133,12 @@ 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 switching the correct flag. +Since we're using `WesternCalendar` (it inherits from `ChristianMixin`) as a base to our `Zhraa` class, it automatically adds Christmas Day as a holiday. Now we can add Easter monday just by switching the correct flag. ```python -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar -class Zhraa(WesternCalendar, ChristianMixin): +class Zhraa(WesternCalendar): include_easter_monday = True FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), @@ -152,7 +152,7 @@ Almost there, 5 holidays out of 6. 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": ```python -class Zhraa(WesternCalendar, ChristianMixin): +class Zhraa(WesternCalendar): include_easter_monday = True FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), @@ -184,7 +184,7 @@ To register, add the following: from ..registry_tools import iso_register @iso_register('ZK') -class Zhraa(WesternCalendar, ChristianMixin): +class Zhraa(WesternCalendar): # The rest of your code... ``` diff --git a/workalendar/africa/algeria.py b/workalendar/africa/algeria.py index bda5558..8a03190 100644 --- a/workalendar/africa/algeria.py +++ b/workalendar/africa/algeria.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, IslamicMixin +from ..core import IslamicCalendar, NewYearsDayMixin from ..registry_tools import iso_register @iso_register('DZ') -class Algeria(WesternCalendar, IslamicMixin): +class Algeria(NewYearsDayMixin, IslamicCalendar): "Algeria" # Islamic holidays include_prophet_birthday = True @@ -11,12 +11,13 @@ class Algeria(WesternCalendar, IslamicMixin): include_day_of_sacrifice = True include_islamic_new_year = True - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( - (5, 1, "Labour Day"), - (7, 5, "Independence Day"), - (11, 1, "Anniversary of the revolution"), - ) + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + \ + IslamicCalendar.FIXED_HOLIDAYS + ( + (5, 1, "Labour Day"), + (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/workalendar/africa/angola.py b/workalendar/africa/angola.py index 9172c91..db69ed6 100644 --- a/workalendar/africa/angola.py +++ b/workalendar/africa/angola.py @@ -1,11 +1,10 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('AO') -class Angola(WesternCalendar, ChristianMixin): +class Angola(WesternCalendar): "Angola" - include_fat_tuesday = True fat_tuesday_label = "Dia de Carnaval" include_good_friday = True diff --git a/workalendar/africa/benin.py b/workalendar/africa/benin.py index 788f985..42f44da 100644 --- a/workalendar/africa/benin.py +++ b/workalendar/africa/benin.py @@ -1,10 +1,11 @@ -from ..core import WesternCalendar, IslamicMixin, ChristianMixin +from ..core import NewYearsDayMixin, IslamoWesternCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('BJ') -class Benin(WesternCalendar, IslamicMixin, ChristianMixin): +class Benin(NewYearsDayMixin, IslamoWesternCalendar): "Benin" + # Christian holidays include_easter_monday = True include_ascension = True include_whit_monday = True @@ -16,10 +17,13 @@ class Benin(WesternCalendar, IslamicMixin, ChristianMixin): include_day_of_sacrifice = True include_day_of_sacrifice_label = "Tabaski" - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (1, 10, "Traditional Day"), (5, 1, "Labour Day"), (8, 1, "Independence Day"), (10, 26, "Armed Forces Day"), (11, 30, "National Day"), ) + + # Explicitely assign these WE days, Benin has adopted the western workweek + WEEKEND_DAYS = (SAT, SUN) diff --git a/workalendar/africa/ivory_coast.py b/workalendar/africa/ivory_coast.py index 5d87183..c848d41 100644 --- a/workalendar/africa/ivory_coast.py +++ b/workalendar/africa/ivory_coast.py @@ -1,22 +1,27 @@ -from ..core import WesternCalendar, IslamicMixin, ChristianMixin +from ..core import NewYearsDayMixin, IslamoWesternCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('CI') -class IvoryCoast(WesternCalendar, ChristianMixin, IslamicMixin): +class IvoryCoast(NewYearsDayMixin, IslamoWesternCalendar): "Ivory Coast" + # 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 + ( + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (8, 7, "Independence Day"), (11, 15, "National Peace Day"), ) + + # Ivory Coast has adopted the "western" workweek. + WEEKEND_DAYS = (SAT, SUN) diff --git a/workalendar/africa/kenya.py b/workalendar/africa/kenya.py index ac28d90..12c2834 100644 --- a/workalendar/africa/kenya.py +++ b/workalendar/africa/kenya.py @@ -1,13 +1,14 @@ from copy import copy from datetime import timedelta, date -from ..core import WesternCalendar, IslamicMixin, ChristianMixin, SUN +from ..core import NewYearsDayMixin, IslamoWesternCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('KE') -class Kenya(WesternCalendar, IslamicMixin, ChristianMixin): +class Kenya(NewYearsDayMixin, IslamoWesternCalendar): "Kenya" + # Christian holidays include_good_friday = True include_easter_monday = True # Islamic holidays @@ -15,7 +16,10 @@ class Kenya(WesternCalendar, IslamicMixin, ChristianMixin): include_day_of_sacrifice = True shift_sunday_holidays = True - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + # Explicitely assign these WE days, Kenya has adopted the western workweek + WEEKEND_DAYS = (SAT, SUN) + + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (6, 1, "Madaraka Day"), (10, 20, "Mashujaa Day"), diff --git a/workalendar/africa/madagascar.py b/workalendar/africa/madagascar.py index 1f2c37b..5bbd544 100644 --- a/workalendar/africa/madagascar.py +++ b/workalendar/africa/madagascar.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +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"), diff --git a/workalendar/africa/sao_tome.py b/workalendar/africa/sao_tome.py index ad1af64..20bba6f 100644 --- a/workalendar/africa/sao_tome.py +++ b/workalendar/africa/sao_tome.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +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"), diff --git a/workalendar/africa/south_africa.py b/workalendar/africa/south_africa.py index e841db2..75f352b 100644 --- a/workalendar/africa/south_africa.py +++ b/workalendar/africa/south_africa.py @@ -1,14 +1,12 @@ from datetime import timedelta, date -from ..core import WesternCalendar -from ..core import SUN, MON, FRI -from ..core import ChristianMixin +from ..core import WesternCalendar, SUN, MON, FRI from ..exceptions import CalendarError 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/workalendar/america/argentina.py b/workalendar/america/argentina.py index b07d523..a6dbc6f 100644 --- a/workalendar/america/argentina.py +++ b/workalendar/america/argentina.py @@ -1,13 +1,10 @@ from datetime import timedelta, date -from ..core import ( - WesternCalendar, ChristianMixin, - MON, TUE, WED, THU, FRI, SAT -) +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' include_fat_tuesday = True diff --git a/workalendar/america/barbados.py b/workalendar/america/barbados.py index 48b5542..3f3bc04 100644 --- a/workalendar/america/barbados.py +++ b/workalendar/america/barbados.py @@ -1,13 +1,12 @@ from datetime import timedelta from copy import copy -from ..core import WesternCalendar, ChristianMixin -from ..core import SUN, MON +from ..core import WesternCalendar, SUN, MON from ..registry_tools import iso_register @iso_register("BB") -class Barbados(WesternCalendar, ChristianMixin): +class Barbados(WesternCalendar): "Barbados" include_good_friday = True diff --git a/workalendar/america/brazil.py b/workalendar/america/brazil.py index d8aaed8..d1cb4d0 100644 --- a/workalendar/america/brazil.py +++ b/workalendar/america/brazil.py @@ -1,12 +1,11 @@ from datetime import timedelta, date -from ..core import WesternCalendar, ChristianMixin -from ..core import MON, SAT, SUN +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"), diff --git a/workalendar/america/canada.py b/workalendar/america/canada.py index ad6b397..ec3b4b0 100644 --- a/workalendar/america/canada.py +++ b/workalendar/america/canada.py @@ -1,12 +1,11 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin, Calendar -from ..core import SUN, MON, SAT +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,14 +30,14 @@ class Canada(WesternCalendar, ChristianMixin): return days -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): @@ -47,14 +46,14 @@ class VictoriaDayMixin(Calendar): 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): @@ -62,7 +61,7 @@ class ThanksgivingMixin(Calendar): return (thanksgiving, "Thanksgiving") -class BoxingDayMixin(Calendar): +class BoxingDayMixin: "26th of December; shift to next working day" def get_boxing_day(self, year): @@ -78,7 +77,7 @@ class BoxingDayMixin(Calendar): return days -class StJeanBaptisteMixin(Calendar): +class StJeanBaptisteMixin: "24th of June; shift to next working day" def get_st_jean(self, year): @@ -92,7 +91,7 @@ class StJeanBaptisteMixin(Calendar): 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) @@ -106,8 +105,9 @@ class RemembranceDayShiftMixin(Calendar): @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 @@ -125,7 +125,7 @@ class Ontario(Canada, BoxingDayMixin, ThanksgivingMixin, VictoriaDayMixin, @iso_register('CA-QC') -class Quebec(Canada, VictoriaDayMixin, StJeanBaptisteMixin, ThanksgivingMixin): +class Quebec(VictoriaDayMixin, StJeanBaptisteMixin, ThanksgivingMixin, Canada): "Quebec" include_easter_monday = True @@ -140,8 +140,8 @@ class Quebec(Canada, VictoriaDayMixin, StJeanBaptisteMixin, ThanksgivingMixin): @iso_register('CA-BC') -class BritishColumbia(Canada, VictoriaDayMixin, AugustCivicHolidayMixin, - ThanksgivingMixin): +class BritishColumbia(VictoriaDayMixin, AugustCivicHolidayMixin, + ThanksgivingMixin, Canada): "British Columbia" include_good_friday = True @@ -176,7 +176,7 @@ class BritishColumbia(Canada, VictoriaDayMixin, AugustCivicHolidayMixin, @iso_register('CA-AB') -class Alberta(Canada, LateFamilyDayMixin, VictoriaDayMixin, ThanksgivingMixin): +class Alberta(LateFamilyDayMixin, VictoriaDayMixin, ThanksgivingMixin, Canada): "Alberta" include_good_friday = True @@ -195,9 +195,9 @@ class Alberta(Canada, LateFamilyDayMixin, VictoriaDayMixin, ThanksgivingMixin): @iso_register('CA-SK') -class Saskatchewan(Canada, LateFamilyDayMixin, VictoriaDayMixin, +class Saskatchewan(LateFamilyDayMixin, VictoriaDayMixin, RemembranceDayShiftMixin, AugustCivicHolidayMixin, - ThanksgivingMixin): + ThanksgivingMixin, Canada): "Saskatchewan" include_good_friday = True @@ -214,8 +214,9 @@ class Saskatchewan(Canada, LateFamilyDayMixin, VictoriaDayMixin, @iso_register('CA-MB') -class Manitoba(Canada, LateFamilyDayMixin, VictoriaDayMixin, - AugustCivicHolidayMixin, ThanksgivingMixin): +class Manitoba(LateFamilyDayMixin, VictoriaDayMixin, + AugustCivicHolidayMixin, ThanksgivingMixin, + Canada): "Manitoba" include_good_friday = True @@ -231,7 +232,7 @@ class Manitoba(Canada, LateFamilyDayMixin, VictoriaDayMixin, @iso_register('CA-NB') -class NewBrunswick(Canada, AugustCivicHolidayMixin): +class NewBrunswick(AugustCivicHolidayMixin, Canada): "New Brunswick" FIXED_HOLIDAYS = Canada.FIXED_HOLIDAYS + ( @@ -247,7 +248,7 @@ class NewBrunswick(Canada, AugustCivicHolidayMixin): @iso_register('CA-NS') -class NovaScotia(Canada, RemembranceDayShiftMixin, LateFamilyDayMixin): +class NovaScotia(RemembranceDayShiftMixin, LateFamilyDayMixin, Canada): "Nova Scotia" include_good_friday = True @@ -261,7 +262,7 @@ class NovaScotia(Canada, RemembranceDayShiftMixin, LateFamilyDayMixin): @iso_register('CA-PE') -class PrinceEdwardIsland(Canada, LateFamilyDayMixin, RemembranceDayShiftMixin): +class PrinceEdwardIsland(LateFamilyDayMixin, RemembranceDayShiftMixin, Canada): "Prince Edward Island" include_good_friday = True @@ -280,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 + ( @@ -300,8 +301,8 @@ class Yukon(Canada, VictoriaDayMixin, ThanksgivingMixin): @iso_register('CA-NT') -class NorthwestTerritories(Canada, RemembranceDayShiftMixin, VictoriaDayMixin, - ThanksgivingMixin): +class NorthwestTerritories(RemembranceDayShiftMixin, VictoriaDayMixin, + ThanksgivingMixin, Canada): "Northwest Territories" FIXED_HOLIDAYS = Canada.FIXED_HOLIDAYS + ( @@ -321,8 +322,8 @@ class NorthwestTerritories(Canada, RemembranceDayShiftMixin, VictoriaDayMixin, @iso_register('CA-NU') -class Nunavut(Canada, VictoriaDayMixin, ThanksgivingMixin, - RemembranceDayShiftMixin): +class Nunavut(VictoriaDayMixin, ThanksgivingMixin, RemembranceDayShiftMixin, + Canada): "Nunavut" include_good_friday = True diff --git a/workalendar/america/chile.py b/workalendar/america/chile.py index c0103ed..af214c4 100644 --- a/workalendar/america/chile.py +++ b/workalendar/america/chile.py @@ -1,12 +1,11 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin -from ..core import MON, TUE, WED, FRI +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"), diff --git a/workalendar/america/colombia.py b/workalendar/america/colombia.py index 780a086..bd39265 100644 --- a/workalendar/america/colombia.py +++ b/workalendar/america/colombia.py @@ -1,11 +1,11 @@ from datetime import timedelta, date -from ..core import WesternCalendar, ChristianMixin, MON +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"), diff --git a/workalendar/america/mexico.py b/workalendar/america/mexico.py index 0192cb9..3f32bc1 100644 --- a/workalendar/america/mexico.py +++ b/workalendar/america/mexico.py @@ -1,12 +1,11 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin -from ..core import SUN, MON, SAT +from ..core import WesternCalendar, SUN, MON, SAT from ..registry_tools import iso_register @iso_register('MX') -class Mexico(WesternCalendar, ChristianMixin): +class Mexico(WesternCalendar): "Mexico" FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), diff --git a/workalendar/america/panama.py b/workalendar/america/panama.py index b2ed974..5ac465a 100644 --- a/workalendar/america/panama.py +++ b/workalendar/america/panama.py @@ -1,11 +1,11 @@ from datetime import timedelta -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('PA') -class Panama(WesternCalendar, ChristianMixin): +class Panama(WesternCalendar): "Panama" include_good_friday = True include_easter_saturday = True diff --git a/workalendar/america/paraguay.py b/workalendar/america/paraguay.py index 6f707a1..9d69a45 100644 --- a/workalendar/america/paraguay.py +++ b/workalendar/america/paraguay.py @@ -1,11 +1,11 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin +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"), diff --git a/workalendar/asia/china.py b/workalendar/asia/china.py index 8333ad7..351bb44 100644 --- a/workalendar/asia/china.py +++ b/workalendar/asia/china.py @@ -1,7 +1,7 @@ from datetime import date import warnings -from ..core import ChineseNewYearCalendar, WesternCalendar +from ..core import ChineseNewYearCalendar, NewYearsDayMixin from ..registry_tools import iso_register from ..exceptions import CalendarError @@ -56,12 +56,12 @@ workdays = { @iso_register('CN') -class China(ChineseNewYearCalendar, WesternCalendar): +class China(NewYearsDayMixin, 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 = NewYearsDayMixin.FIXED_HOLIDAYS + tuple(national_days) include_chinese_new_year_eve = True diff --git a/workalendar/asia/hong_kong.py b/workalendar/asia/hong_kong.py index 158ddbe..a281a15 100644 --- a/workalendar/asia/hong_kong.py +++ b/workalendar/asia/hong_kong.py @@ -1,7 +1,7 @@ from datetime import date, timedelta from ..core import ( - ChineseNewYearCalendar, WesternCalendar, ChristianMixin, + ChineseNewYearCalendar, NewYearsDayMixin, WesternMixin, SUN, SAT ) from ..astronomy import solar_term @@ -9,7 +9,7 @@ from ..registry_tools import iso_register @iso_register('HK') -class HongKong(WesternCalendar, ChineseNewYearCalendar, ChristianMixin): +class HongKong(NewYearsDayMixin, WesternMixin, ChineseNewYearCalendar): "Hong Kong" include_good_friday = True include_easter_saturday = True @@ -18,7 +18,7 @@ class HongKong(WesternCalendar, ChineseNewYearCalendar, ChristianMixin): WEEKEND_DAYS = (SUN,) - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (7, 1, "SAR Establishment Day"), (10, 1, "National Day"), diff --git a/workalendar/asia/japan.py b/workalendar/asia/japan.py index 8c8a272..c9ac075 100644 --- a/workalendar/asia/japan.py +++ b/workalendar/asia/japan.py @@ -1,16 +1,18 @@ from datetime import date -from ..core import MON -from ..core import WesternCalendar +from ..core import NewYearsDayMixin, Calendar, MON, SAT, SUN from ..astronomy import calculate_equinoxes from ..registry_tools import iso_register @iso_register('JP') -class Japan(WesternCalendar): +class Japan(NewYearsDayMixin, Calendar): "Japan" - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + # Japan uses the "western" workweek + WEEKEND_DAYS = (SAT, SUN) + + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (2, 11, "Foundation Day"), (4, 29, "Showa Day"), (5, 3, "Constitution Memorial Day"), diff --git a/workalendar/asia/malaysia.py b/workalendar/asia/malaysia.py index a0d054c..a8f6e64 100644 --- a/workalendar/asia/malaysia.py +++ b/workalendar/asia/malaysia.py @@ -1,12 +1,14 @@ from datetime import date -from ..core import ChineseNewYearCalendar, WesternCalendar -from ..core import IslamicMixin +from ..core import ( + NewYearsDayMixin, IslamicMixin, ChineseNewYearCalendar, + SAT, SUN +) from ..registry_tools import iso_register @iso_register('MY') -class Malaysia(ChineseNewYearCalendar, WesternCalendar, IslamicMixin): +class Malaysia(NewYearsDayMixin, IslamicMixin, ChineseNewYearCalendar): "Malaysia" include_nuzul_al_quran = True include_eid_al_fitr = True @@ -17,7 +19,11 @@ 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 = NewYearsDayMixin.FIXED_HOLIDAYS + ( (2, 1, "Federal Territory Day"), (5, 1, "Workers' Day"), (8, 31, "National Day"), diff --git a/workalendar/asia/qatar.py b/workalendar/asia/qatar.py index d6a5113..4199d87 100644 --- a/workalendar/asia/qatar.py +++ b/workalendar/asia/qatar.py @@ -1,12 +1,10 @@ -from ..core import Calendar -from ..core import FRI, SAT, IslamicMixin +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) FIXED_HOLIDAYS = ( (12, 18, "National Day"), diff --git a/workalendar/asia/singapore.py b/workalendar/asia/singapore.py index 78007c7..76ed315 100644 --- a/workalendar/asia/singapore.py +++ b/workalendar/asia/singapore.py @@ -1,26 +1,33 @@ from datetime import date from ..core import ( - ChineseNewYearCalendar, WesternCalendar, ChristianMixin, IslamicMixin + NewYearsDayMixin, WesternMixin, IslamicMixin, ChineseNewYearCalendar, + SAT, SUN ) from ..registry_tools import iso_register @iso_register('SG') -class Singapore(WesternCalendar, - ChineseNewYearCalendar, ChristianMixin, IslamicMixin): +class Singapore(NewYearsDayMixin, WesternMixin, IslamicMixin, + ChineseNewYearCalendar): "Singapore" + # 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 + ( + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (8, 9, "National Day"), ) + # Explicitely 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 = { diff --git a/workalendar/asia/south_korea.py b/workalendar/asia/south_korea.py index 511260b..9849481 100644 --- a/workalendar/asia/south_korea.py +++ b/workalendar/asia/south_korea.py @@ -1,11 +1,11 @@ -from ..core import ChineseNewYearCalendar, WesternCalendar +from ..core import NewYearsDayMixin, ChineseNewYearCalendar from ..registry_tools import iso_register @iso_register('KR') -class SouthKorea(WesternCalendar, ChineseNewYearCalendar): +class SouthKorea(NewYearsDayMixin, ChineseNewYearCalendar): "South Korea" - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( (3, 1, "Independence Day"), (5, 5, "Children's Day"), (6, 6, "Memorial Day"), diff --git a/workalendar/asia/taiwan.py b/workalendar/asia/taiwan.py index 83e84bf..2430d16 100644 --- a/workalendar/asia/taiwan.py +++ b/workalendar/asia/taiwan.py @@ -1,13 +1,13 @@ -from ..core import ChineseNewYearCalendar, WesternCalendar +from ..core import NewYearsDayMixin, ChineseNewYearCalendar from ..astronomy import solar_term from ..registry_tools import iso_register @iso_register('TW') -class Taiwan(ChineseNewYearCalendar, WesternCalendar): +class Taiwan(NewYearsDayMixin, ChineseNewYearCalendar): "Taiwan (Republic of China)" FIXED_HOLIDAYS = ( - WesternCalendar.FIXED_HOLIDAYS + + NewYearsDayMixin.FIXED_HOLIDAYS + ( (2, 28, "228 Peace Memorial Day"), (4, 4, "Combination of Women's Day and Children's Day"), diff --git a/workalendar/core.py b/workalendar/core.py index 8b0a2d0..a38592e 100644 --- a/workalendar/core.py +++ b/workalendar/core.py @@ -42,7 +42,451 @@ def cleaned_date(day, keep_datetime=False): return day -class Calendar: +class NewYearsDayMixin: + FIXED_HOLIDAYS = ( + (1, 1, 'New year'), + ) + + shift_new_years_day = False + + def get_variable_days(self, year): + days = super().get_variable_days(year) + new_year = date(year, 1, 1) + if 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 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 + include_holy_thursday = False + include_good_friday = False + good_friday_label = "Good Friday" + include_easter_monday = False + include_easter_saturday = False + include_easter_sunday = False + include_all_saints = False + include_immaculate_conception = False + immaculate_conception_label = "Immaculate Conception" + include_christmas = True + include_christmas_eve = False + include_ascension = False + include_assumption = False + include_whit_sunday = False + whit_sunday_label = 'Whit Sunday' + include_whit_monday = False + whit_monday_label = 'Whit Monday' + include_corpus_christi = False + include_boxing_day = False + 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) + + def get_palm_sunday(self, year): + sunday = self.get_easter_sunday(year) + return sunday - timedelta(days=7) + + def get_holy_thursday(self, year): + "Return the date of the last thursday before easter" + sunday = self.get_easter_sunday(year) + return sunday - timedelta(days=3) + + def get_good_friday(self, year): + "Return the date of the last friday before easter" + sunday = self.get_easter_sunday(year) + return sunday - timedelta(days=2) + + def get_clean_monday(self, year): + "Return the clean monday date" + sunday = self.get_easter_sunday(year) + return sunday - timedelta(days=48) + + def get_easter_saturday(self, year): + "Return the Easter Saturday date" + sunday = self.get_easter_sunday(year) + return sunday - timedelta(days=1) + + def get_easter_sunday(self, year): + "Return the date of the easter (sunday) -- following the easter method" + return easter.easter(year, self.EASTER_METHOD) + + def get_easter_monday(self, year): + "Return the date of the monday after easter" + sunday = self.get_easter_sunday(year) + return sunday + timedelta(days=1) + + def get_ascension_thursday(self, year): + easter = self.get_easter_sunday(year) + return easter + timedelta(days=39) + + def get_whit_monday(self, year): + easter = self.get_easter_sunday(year) + return easter + timedelta(days=50) + + def get_whit_sunday(self, year): + easter = self.get_easter_sunday(year) + return easter + timedelta(days=49) + + def get_corpus_christi(self, year): + return self.get_easter_sunday(year) + timedelta(days=60) + + def shift_christmas_boxing_days(self, year): + """ When Christmas and/or Boxing Day falls on a weekend, it is rolled + forward to the next weekday. + """ + christmas = date(year, 12, 25) + boxing_day = date(year, 12, 26) + boxing_day_label = "{} Shift".format(self.boxing_day_label) + results = [] + if christmas.weekday() in self.get_weekend_days(): + shift = self.find_following_working_day(christmas) + results.append((shift, "Christmas Shift")) + results.append((shift + timedelta(days=1), boxing_day_label)) + elif boxing_day.weekday() in self.get_weekend_days(): + shift = self.find_following_working_day(boxing_day) + results.append((shift, boxing_day_label)) + return results + + def get_variable_days(self, year): # noqa + "Return the christian holidays list according to the mixin" + days = super().get_variable_days(year) + if self.include_epiphany: + days.append((date(year, 1, 6), "Epiphany")) + if self.include_clean_monday: + 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) + ) + if self.include_palm_sunday: + days.append((self.get_palm_sunday(year), "Palm Sunday")) + if self.include_holy_thursday: + days.append((self.get_holy_thursday(year), "Holy Thursday")) + if self.include_good_friday: + days.append((self.get_good_friday(year), self.good_friday_label)) + if self.include_easter_saturday: + days.append((self.get_easter_saturday(year), "Easter Saturday")) + if self.include_easter_sunday: + days.append((self.get_easter_sunday(year), "Easter Sunday")) + if self.include_easter_monday: + days.append((self.get_easter_monday(year), "Easter Monday")) + if self.include_assumption: + days.append((date(year, 8, 15), "Assumption of Mary to Heaven")) + if self.include_all_saints: + days.append((date(year, 11, 1), "All Saints Day")) + if self.include_all_souls: + days.append((date(year, 11, 2), "All Souls Day")) + if self.include_immaculate_conception: + days.append((date(year, 12, 8), self.immaculate_conception_label)) + if self.include_christmas: + days.append((date(year, 12, 25), "Christmas Day")) + if self.include_christmas_eve: + days.append((date(year, 12, 24), "Christmas Eve")) + if self.include_boxing_day: + days.append((date(year, 12, 26), self.boxing_day_label)) + if self.include_ascension: + days.append(( + self.get_ascension_thursday(year), "Ascension Thursday")) + if self.include_whit_monday: + days.append((self.get_whit_monday(year), self.whit_monday_label)) + if self.include_whit_sunday: + days.append((self.get_whit_sunday(year), self.whit_sunday_label)) + if self.include_corpus_christi: + days.append((self.get_corpus_christi(year), "Corpus Christi")) + return days + + +class WesternMixin(ChristianMixin): + """ + General usage calendar for Western countries. + + (chiefly Europe and Northern America) + + """ + EASTER_METHOD = easter.EASTER_WESTERN + WEEKEND_DAYS = (SAT, SUN) + + +class OrthodoxMixin(ChristianMixin): + EASTER_METHOD = easter.EASTER_ORTHODOX + WEEKEND_DAYS = (SAT, SUN) + + +class LunarMixin: + """ + Calendar ready to compute luncar calendar days + """ + @staticmethod + def lunar(year, month, day): + return LunarDate(year, month, day).toSolarDate() + + +class ChineseNewYearMixin(LunarMixin): + """ + Calendar including toolsets to compute the Chinese New Year holidays. + """ + include_chinese_new_year_eve = False + chinese_new_year_eve_label = "Chinese New Year's eve" + # Chinese New Year will be included by default + include_chinese_new_year = True + chinese_new_year_label = 'Chinese New Year' + # Some countries include the 2nd lunar day as a holiday + include_chinese_second_day = False + chinese_second_day_label = "Chinese New Year (2nd day)" + include_chinese_third_day = False + chinese_third_day_label = "Chinese New Year (3rd day)" + shift_sunday_holidays = False + # Some calendars roll a starting Sunday CNY to Sat + shift_start_cny_sunday = False + + def get_chinese_new_year(self, year): + """ + Compute Chinese New Year days. To return a list of holidays. + + By default, it'll at least return the Chinese New Year holidays chosen + using the following options: + + * ``include_chinese_new_year_eve`` + * ``include_chinese_new_year`` (on by default) + * ``include_chinese_second_day`` + + If the ``shift_sunday_holidays`` option is on, the rules are the + following. + + * If the CNY1 falls on MON-FRI, there's not shift. + * If the CNY1 falls on SAT, the CNY2 is shifted to the Monday after. + * If the CNY1 falls on SUN, the CNY1 is shifted to the Monday after, + and CNY2 is shifted to the Tuesday after. + """ + days = [] + + lunar_first_day = ChineseNewYearMixin.lunar(year, 1, 1) + # Chinese new year's eve + if self.include_chinese_new_year_eve: + days.append(( + lunar_first_day - timedelta(days=1), + self.chinese_new_year_eve_label + )) + # Chinese new year (is included by default) + if self.include_chinese_new_year: + days.append((lunar_first_day, self.chinese_new_year_label)) + + if self.include_chinese_second_day: + lunar_second_day = lunar_first_day + timedelta(days=1) + days.append(( + lunar_second_day, + self.chinese_second_day_label + )) + if self.include_chinese_third_day: + lunar_third_day = lunar_first_day + timedelta(days=2) + days.append(( + lunar_third_day, + self.chinese_third_day_label + )) + + if self.shift_sunday_holidays: + if lunar_first_day.weekday() == SUN: + if self.shift_start_cny_sunday: + days.append( + (lunar_first_day - timedelta(days=1), + "Chinese Lunar New Year shift"), + ) + else: + if self.include_chinese_third_day: + shift_day = lunar_third_day + else: + shift_day = lunar_second_day + days.append( + (shift_day + timedelta(days=1), + "Chinese Lunar New Year shift"), + ) + if (lunar_second_day.weekday() == SUN + and self.include_chinese_third_day): + days.append( + (lunar_third_day + timedelta(days=1), + "Chinese Lunar New Year shift"), + ) + return days + + def get_variable_days(self, year): + days = super().get_variable_days(year) + days.extend(self.get_chinese_new_year(year)) + 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. + """ + for holiday, label in dates: + if holiday.weekday() == SUN: + 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 + + +class CalverterMixin: + conversion_method = None + ISLAMIC_HOLIDAYS = () + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.calverter = Calverter() + if self.conversion_method is None: + raise NotImplementedError + + def converted(self, year): + conversion_method = getattr( + self.calverter, 'jd_to_%s' % self.conversion_method) + current = date(year, 1, 1) + days = [] + while current.year == year: + julian_day = self.calverter.gregorian_to_jd( + current.year, + current.month, + current.day) + days.append(conversion_method(julian_day)) + current = current + timedelta(days=1) + return days + + def calverted_years(self, year): + converted = self.converted(year) + generator = (y for y, m, d in converted) + return sorted(list(set(generator))) + + def get_islamic_holidays(self): + return self.ISLAMIC_HOLIDAYS + + def get_delta_islamic_holidays(self, year): + """ + Return the delta to add/substract according to the year or customs. + + By default, to return None or timedelta(days=0) + """ + return None + + def get_variable_days(self, year): + warnings.warn('Please take note that, due to arbitrary decisions, ' + 'this Islamic calendar computation may be wrong.') + days = super().get_variable_days(year) + years = self.calverted_years(year) + conversion_method = getattr( + self.calverter, '%s_to_jd' % self.conversion_method) + for month, day, label in self.get_islamic_holidays(): + for y in years: + jd = conversion_method(y, month, day) + g_year, g_month, g_day = self.calverter.jd_to_gregorian(jd) + holiday = date(g_year, g_month, g_day) + + # Only add a delta if necessary + delta = self.get_delta_islamic_holidays(year) + if delta: + holiday += delta + + if holiday.year == year: + days.append((holiday, label)) + return days + + +class IslamicMixin(CalverterMixin): + + WEEKEND_DAYS = (FRI, SAT) + + conversion_method = 'islamic' + include_prophet_birthday = False + include_day_after_prophet_birthday = False + include_start_ramadan = False + include_eid_al_fitr = False + length_eid_al_fitr = 1 + eid_al_fitr_label = "Eid al-Fitr" + include_eid_al_adha = False + length_eid_al_adha = 1 + include_day_of_sacrifice = False + day_of_sacrifice_label = "Eid al-Adha" + include_islamic_new_year = False + include_laylat_al_qadr = False + include_nuzul_al_quran = False + + def get_islamic_holidays(self): + """Return a list of Islamic (month, day, label) for islamic holidays. + Please take note that these dates must be expressed using the Islamic + Calendar""" + days = list(super().get_islamic_holidays()) + + if self.include_islamic_new_year: + days.append((1, 1, "Islamic New Year")) + if self.include_prophet_birthday: + days.append((3, 12, "Prophet's Birthday")) + if self.include_day_after_prophet_birthday: + days.append((3, 13, "Day after Prophet's Birthday")) + if self.include_start_ramadan: + days.append((9, 1, "Start of ramadan")) + if self.include_nuzul_al_quran: + days.append((9, 17, "Nuzul Al-Qur'an")) + if self.include_eid_al_fitr: + for x in range(self.length_eid_al_fitr): + days.append((10, x + 1, self.eid_al_fitr_label)) + if self.include_eid_al_adha: + for x in range(self.length_eid_al_adha): + days.append((12, x + 10, "Eid al-Adha")) + if self.include_day_of_sacrifice: + days.append((12, 10, self.day_of_sacrifice_label)) + if self.include_laylat_al_qadr: + warnings.warn("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.") + return tuple(days) + + +class JalaliMixin(CalverterMixin): + conversion_method = 'jalali' + + +class CoreCalendar: FIXED_HOLIDAYS = () WEEKEND_DAYS = () @@ -368,438 +812,40 @@ class Calendar: return count -class ChristianMixin(Calendar): - 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 - include_holy_thursday = False - include_good_friday = False - good_friday_label = "Good Friday" - include_easter_monday = False - include_easter_saturday = False - include_easter_sunday = False - include_all_saints = False - include_immaculate_conception = False - immaculate_conception_label = "Immaculate Conception" - include_christmas = True - include_christmas_eve = False - include_ascension = False - include_assumption = False - include_whit_sunday = False - whit_sunday_label = 'Whit Sunday' - include_whit_monday = False - whit_monday_label = 'Whit Monday' - include_corpus_christi = False - include_boxing_day = False - 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) - - def get_palm_sunday(self, year): - sunday = self.get_easter_sunday(year) - return sunday - timedelta(days=7) - - def get_holy_thursday(self, year): - "Return the date of the last thursday before easter" - sunday = self.get_easter_sunday(year) - return sunday - timedelta(days=3) - - def get_good_friday(self, year): - "Return the date of the last friday before easter" - sunday = self.get_easter_sunday(year) - return sunday - timedelta(days=2) - - def get_clean_monday(self, year): - "Return the clean monday date" - sunday = self.get_easter_sunday(year) - return sunday - timedelta(days=48) - - def get_easter_saturday(self, year): - "Return the Easter Saturday date" - sunday = self.get_easter_sunday(year) - return sunday - timedelta(days=1) - - def get_easter_sunday(self, year): - "Return the date of the easter (sunday) -- following the easter method" - return easter.easter(year, self.EASTER_METHOD) - - def get_easter_monday(self, year): - "Return the date of the monday after easter" - sunday = self.get_easter_sunday(year) - return sunday + timedelta(days=1) - - def get_ascension_thursday(self, year): - easter = self.get_easter_sunday(year) - return easter + timedelta(days=39) - - def get_whit_monday(self, year): - easter = self.get_easter_sunday(year) - return easter + timedelta(days=50) - - def get_whit_sunday(self, year): - easter = self.get_easter_sunday(year) - return easter + timedelta(days=49) - - def get_corpus_christi(self, year): - return self.get_easter_sunday(year) + timedelta(days=60) - - def shift_christmas_boxing_days(self, year): - """ When Christmas and/or Boxing Day falls on a weekend, it is rolled - forward to the next weekday. - """ - christmas = date(year, 12, 25) - boxing_day = date(year, 12, 26) - boxing_day_label = "{} Shift".format(self.boxing_day_label) - results = [] - if christmas.weekday() in self.get_weekend_days(): - shift = self.find_following_working_day(christmas) - results.append((shift, "Christmas Shift")) - results.append((shift + timedelta(days=1), boxing_day_label)) - elif boxing_day.weekday() in self.get_weekend_days(): - shift = self.find_following_working_day(boxing_day) - results.append((shift, boxing_day_label)) - return results - - def get_variable_days(self, year): # noqa - "Return the christian holidays list according to the mixin" - days = super().get_variable_days(year) - if self.include_epiphany: - days.append((date(year, 1, 6), "Epiphany")) - if self.include_clean_monday: - 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) - ) - if self.include_palm_sunday: - days.append((self.get_palm_sunday(year), "Palm Sunday")) - if self.include_holy_thursday: - days.append((self.get_holy_thursday(year), "Holy Thursday")) - if self.include_good_friday: - days.append((self.get_good_friday(year), self.good_friday_label)) - if self.include_easter_saturday: - days.append((self.get_easter_saturday(year), "Easter Saturday")) - if self.include_easter_sunday: - days.append((self.get_easter_sunday(year), "Easter Sunday")) - if self.include_easter_monday: - days.append((self.get_easter_monday(year), "Easter Monday")) - if self.include_assumption: - days.append((date(year, 8, 15), "Assumption of Mary to Heaven")) - if self.include_all_saints: - days.append((date(year, 11, 1), "All Saints Day")) - if self.include_all_souls: - days.append((date(year, 11, 2), "All Souls Day")) - if self.include_immaculate_conception: - days.append((date(year, 12, 8), self.immaculate_conception_label)) - if self.include_christmas: - days.append((date(year, 12, 25), "Christmas Day")) - if self.include_christmas_eve: - days.append((date(year, 12, 24), "Christmas Eve")) - if self.include_boxing_day: - days.append((date(year, 12, 26), self.boxing_day_label)) - if self.include_ascension: - days.append(( - self.get_ascension_thursday(year), "Ascension Thursday")) - if self.include_whit_monday: - days.append((self.get_whit_monday(year), self.whit_monday_label)) - if self.include_whit_sunday: - days.append((self.get_whit_sunday(year), self.whit_sunday_label)) - if self.include_corpus_christi: - days.append((self.get_corpus_christi(year), "Corpus Christi")) - return days +class Calendar(CoreCalendar): + pass -class WesternCalendar(Calendar): +class WesternCalendar(NewYearsDayMixin, WesternMixin, Calendar): """ - General usage calendar for Western countries. - - (chiefly Europe and Northern America) - + A Christian calendar using Western definition for Easter. """ - EASTER_METHOD = easter.EASTER_WESTERN + + +class OrthodoxCalendar(NewYearsDayMixin, OrthodoxMixin, Calendar): + """ + A Christian calendar using Orthodox definition for Easter. + """ + + +class ChineseNewYearCalendar(ChineseNewYearMixin, Calendar): + """ + Chinese Calendar, using Chinese New Year computation. + """ + # There are regional exceptions to those week-end days, to define locally. WEEKEND_DAYS = (SAT, SUN) - shift_new_years_day = False - - FIXED_HOLIDAYS = ( - (1, 1, 'New year'), - ) - - def get_variable_days(self, year): - days = super().get_variable_days(year) - new_year = date(year, 1, 1) - if 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 OrthodoxMixin(ChristianMixin): - EASTER_METHOD = easter.EASTER_ORTHODOX - - -class LunarCalendar(Calendar): +class IslamicCalendar(IslamicMixin, Calendar): """ - Calendar ready to compute luncar calendar days + Islamic calendar """ - @staticmethod - def lunar(year, month, day): - return LunarDate(year, month, day).toSolarDate() -class ChineseNewYearCalendar(LunarCalendar): +class IslamoWesternCalendar(IslamicMixin, WesternMixin, Calendar): """ - Calendar including toolsets to compute the Chinese New Year holidays. + Mix of Islamic and Western calendars. + + When countries have both Islamic and Western-Christian holidays. """ - include_chinese_new_year_eve = False - chinese_new_year_eve_label = "Chinese New Year's eve" - # Chinese New Year will be included by default - include_chinese_new_year = True - chinese_new_year_label = 'Chinese New Year' - # Some countries include the 2nd lunar day as a holiday - include_chinese_second_day = False - chinese_second_day_label = "Chinese New Year (2nd day)" - include_chinese_third_day = False - chinese_third_day_label = "Chinese New Year (3rd day)" - shift_sunday_holidays = False - # Some calendars roll a starting Sunday CNY to Sat - shift_start_cny_sunday = False - - def get_chinese_new_year(self, year): - """ - Compute Chinese New Year days. To return a list of holidays. - - By default, it'll at least return the Chinese New Year holidays chosen - using the following options: - - * ``include_chinese_new_year_eve`` - * ``include_chinese_new_year`` (on by default) - * ``include_chinese_second_day`` - - If the ``shift_sunday_holidays`` option is on, the rules are the - following. - - * If the CNY1 falls on MON-FRI, there's not shift. - * If the CNY1 falls on SAT, the CNY2 is shifted to the Monday after. - * If the CNY1 falls on SUN, the CNY1 is shifted to the Monday after, - and CNY2 is shifted to the Tuesday after. - """ - days = [] - - lunar_first_day = ChineseNewYearCalendar.lunar(year, 1, 1) - # Chinese new year's eve - if self.include_chinese_new_year_eve: - days.append(( - lunar_first_day - timedelta(days=1), - self.chinese_new_year_eve_label - )) - # Chinese new year (is included by default) - if self.include_chinese_new_year: - days.append((lunar_first_day, self.chinese_new_year_label)) - - if self.include_chinese_second_day: - lunar_second_day = lunar_first_day + timedelta(days=1) - days.append(( - lunar_second_day, - self.chinese_second_day_label - )) - if self.include_chinese_third_day: - lunar_third_day = lunar_first_day + timedelta(days=2) - days.append(( - lunar_third_day, - self.chinese_third_day_label - )) - - if self.shift_sunday_holidays: - if lunar_first_day.weekday() == SUN: - if self.shift_start_cny_sunday: - days.append( - (lunar_first_day - timedelta(days=1), - "Chinese Lunar New Year shift"), - ) - else: - if self.include_chinese_third_day: - shift_day = lunar_third_day - else: - shift_day = lunar_second_day - days.append( - (shift_day + timedelta(days=1), - "Chinese Lunar New Year shift"), - ) - if (lunar_second_day.weekday() == SUN - and self.include_chinese_third_day): - days.append( - (lunar_third_day + timedelta(days=1), - "Chinese Lunar New Year shift"), - ) - return days - - def get_variable_days(self, year): - days = super().get_variable_days(year) - days.extend(self.get_chinese_new_year(year)) - 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. - """ - for holiday, label in dates: - if holiday.weekday() == SUN: - 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 - - -class CalverterMixin(Calendar): - conversion_method = None - ISLAMIC_HOLIDAYS = () - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.calverter = Calverter() - if self.conversion_method is None: - raise NotImplementedError - - def converted(self, year): - conversion_method = getattr( - self.calverter, 'jd_to_%s' % self.conversion_method) - current = date(year, 1, 1) - days = [] - while current.year == year: - julian_day = self.calverter.gregorian_to_jd( - current.year, - current.month, - current.day) - days.append(conversion_method(julian_day)) - current = current + timedelta(days=1) - return days - - def calverted_years(self, year): - converted = self.converted(year) - generator = (y for y, m, d in converted) - return sorted(list(set(generator))) - - def get_islamic_holidays(self): - return self.ISLAMIC_HOLIDAYS - - def get_delta_islamic_holidays(self, year): - """ - Return the delta to add/substract according to the year or customs. - - By default, to return None or timedelta(days=0) - """ - return None - - def get_variable_days(self, year): - warnings.warn('Please take note that, due to arbitrary decisions, ' - 'this Islamic calendar computation may be wrong.') - days = super().get_variable_days(year) - years = self.calverted_years(year) - conversion_method = getattr( - self.calverter, '%s_to_jd' % self.conversion_method) - for month, day, label in self.get_islamic_holidays(): - for y in years: - jd = conversion_method(y, month, day) - g_year, g_month, g_day = self.calverter.jd_to_gregorian(jd) - holiday = date(g_year, g_month, g_day) - - # Only add a delta if necessary - delta = self.get_delta_islamic_holidays(year) - if delta: - holiday += delta - - if holiday.year == year: - days.append((holiday, label)) - return days - - -class IslamicMixin(CalverterMixin): - conversion_method = 'islamic' - include_prophet_birthday = False - include_day_after_prophet_birthday = False - include_start_ramadan = False - include_eid_al_fitr = False - length_eid_al_fitr = 1 - eid_al_fitr_label = "Eid al-Fitr" - include_eid_al_adha = False - length_eid_al_adha = 1 - include_day_of_sacrifice = False - day_of_sacrifice_label = "Eid al-Adha" - include_islamic_new_year = False - include_laylat_al_qadr = False - include_nuzul_al_quran = False - - def get_islamic_holidays(self): - """Return a list of Islamic (month, day, label) for islamic holidays. - Please take note that these dates must be expressed using the Islamic - Calendar""" - days = list(super().get_islamic_holidays()) - - if self.include_islamic_new_year: - days.append((1, 1, "Islamic New Year")) - if self.include_prophet_birthday: - days.append((3, 12, "Prophet's Birthday")) - if self.include_day_after_prophet_birthday: - days.append((3, 13, "Day after Prophet's Birthday")) - if self.include_start_ramadan: - days.append((9, 1, "Start of ramadan")) - if self.include_nuzul_al_quran: - days.append((9, 17, "Nuzul Al-Qur'an")) - if self.include_eid_al_fitr: - for x in range(self.length_eid_al_fitr): - days.append((10, x + 1, self.eid_al_fitr_label)) - if self.include_eid_al_adha: - for x in range(self.length_eid_al_adha): - days.append((12, x + 10, "Eid al-Adha")) - if self.include_day_of_sacrifice: - days.append((12, 10, self.day_of_sacrifice_label)) - if self.include_laylat_al_qadr: - warnings.warn("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.") - return tuple(days) - - -class JalaliMixin(CalverterMixin): - conversion_method = 'jalali' + FIXED_HOLIDAYS = Calendar.FIXED_HOLIDAYS diff --git a/workalendar/europe/austria.py b/workalendar/europe/austria.py index c07cd50..d7e0f20 100644 --- a/workalendar/europe/austria.py +++ b/workalendar/europe/austria.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +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 + ( diff --git a/workalendar/europe/belarus.py b/workalendar/europe/belarus.py index 37a83cd..e4a9292 100644 --- a/workalendar/europe/belarus.py +++ b/workalendar/europe/belarus.py @@ -1,16 +1,16 @@ from datetime import timedelta -from ..core import WesternCalendar, OrthodoxMixin +from ..core import OrthodoxCalendar from ..registry_tools import iso_register @iso_register('BY') -class Belarus(WesternCalendar, OrthodoxMixin): +class Belarus(OrthodoxCalendar): 'Belarus' "as of http://president.gov.by/en/holidays_en/" include_christmas = False - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + ( (1, 2, "Day After New Year"), (1, 7, "Christmas (Orthodox)"), (3, 8, "International Women's Day"), @@ -24,15 +24,10 @@ class Belarus(WesternCalendar, OrthodoxMixin): # https://en.wikipedia.org/wiki/Radonitsa def get_radonitsa(self, year): - # Second Monday after easter sunday - return self.get_easter_sunday(year) + timedelta(days=15) - - def get_day_after_radonitsa(self, year): - # one day after the Radonsista Monday - return self.get_radonitsa(year) + timedelta(days=1) + # 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), "Radonista")) - days.append((self.get_day_after_radonitsa(year), "Radonista Holiday")) + days.append((self.get_radonitsa(year), "Radonitsa")) return days diff --git a/workalendar/europe/belgium.py b/workalendar/europe/belgium.py index 3afea3f..10f4085 100644 --- a/workalendar/europe/belgium.py +++ b/workalendar/europe/belgium.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +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 + ( diff --git a/workalendar/europe/bulgaria.py b/workalendar/europe/bulgaria.py index 872c562..3d5de62 100644 --- a/workalendar/europe/bulgaria.py +++ b/workalendar/europe/bulgaria.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +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 + ( diff --git a/workalendar/europe/cayman_islands.py b/workalendar/europe/cayman_islands.py index 02be838..b1d1118 100644 --- a/workalendar/europe/cayman_islands.py +++ b/workalendar/europe/cayman_islands.py @@ -1,6 +1,6 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin, MON, SAT +from ..core import WesternCalendar, MON, SAT from ..registry_tools import iso_register @@ -11,7 +11,7 @@ QUEENS_BIRTHDAY_EXCEPTIONS = { @iso_register('KY') -class CaymanIslands(WesternCalendar, ChristianMixin): +class CaymanIslands(WesternCalendar): "Cayman Islands" include_ash_wednesday = True include_good_friday = True diff --git a/workalendar/europe/croatia.py b/workalendar/europe/croatia.py index f2e1cd6..db09438 100644 --- a/workalendar/europe/croatia.py +++ b/workalendar/europe/croatia.py @@ -1,11 +1,11 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin +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 + ( diff --git a/workalendar/europe/cyprus.py b/workalendar/europe/cyprus.py index 3cfa5a5..4f63f9e 100644 --- a/workalendar/europe/cyprus.py +++ b/workalendar/europe/cyprus.py @@ -1,10 +1,10 @@ from datetime import timedelta -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('CY') -class Cyprus(WesternCalendar, ChristianMixin): +class Cyprus(WesternCalendar): 'Cyprus' include_epiphany = True diff --git a/workalendar/europe/czech_republic.py b/workalendar/europe/czech_republic.py index d3a42d1..3746ee2 100644 --- a/workalendar/europe/czech_republic.py +++ b/workalendar/europe/czech_republic.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('CZ') -class CzechRepublic(WesternCalendar, ChristianMixin): +class CzechRepublic(WesternCalendar): 'Czech Republic' include_easter_monday = True diff --git a/workalendar/europe/denmark.py b/workalendar/europe/denmark.py index 8cd01a5..47eb14e 100644 --- a/workalendar/europe/denmark.py +++ b/workalendar/europe/denmark.py @@ -1,10 +1,10 @@ from datetime import timedelta -from ..core import WesternCalendar, ChristianMixin +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/workalendar/europe/estonia.py b/workalendar/europe/estonia.py index 36eca60..ff82203 100644 --- a/workalendar/europe/estonia.py +++ b/workalendar/europe/estonia.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +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/workalendar/europe/european_central_bank.py b/workalendar/europe/european_central_bank.py index 555e6f9..253af33 100644 --- a/workalendar/europe/european_central_bank.py +++ b/workalendar/europe/european_central_bank.py @@ -1,7 +1,7 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar -class EuropeanCentralBank(WesternCalendar, ChristianMixin): +class EuropeanCentralBank(WesternCalendar): "European Central Bank" FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), diff --git a/workalendar/europe/finland.py b/workalendar/europe/finland.py index 0e87b60..561eef3 100644 --- a/workalendar/europe/finland.py +++ b/workalendar/europe/finland.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin, FRI, SAT +from ..core import WesternCalendar, FRI, SAT from ..registry_tools import iso_register @iso_register('FI') -class Finland(WesternCalendar, ChristianMixin): +class Finland(WesternCalendar): 'Finland' include_epiphany = True diff --git a/workalendar/europe/france.py b/workalendar/europe/france.py index 8f3ff64..cd5618e 100644 --- a/workalendar/europe/france.py +++ b/workalendar/europe/france.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('FR') -class France(WesternCalendar, ChristianMixin): +class France(WesternCalendar): 'France' include_easter_monday = True diff --git a/workalendar/europe/germany.py b/workalendar/europe/germany.py index c32daec..94801b6 100644 --- a/workalendar/europe/germany.py +++ b/workalendar/europe/germany.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('DE') -class Germany(WesternCalendar, ChristianMixin): +class Germany(WesternCalendar): 'Germany' FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( diff --git a/workalendar/europe/greece.py b/workalendar/europe/greece.py index 3cdb009..900e4bc 100644 --- a/workalendar/europe/greece.py +++ b/workalendar/europe/greece.py @@ -1,12 +1,12 @@ -from ..core import WesternCalendar, OrthodoxMixin +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 + ( + FIXED_HOLIDAYS = OrthodoxCalendar.FIXED_HOLIDAYS + ( (3, 25, "Independence Day"), (5, 1, "Labour Day"), (10, 28, "Ohi Day"), diff --git a/workalendar/europe/hungary.py b/workalendar/europe/hungary.py index b41c864..a6c255e 100644 --- a/workalendar/europe/hungary.py +++ b/workalendar/europe/hungary.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('HU') -class Hungary(WesternCalendar, ChristianMixin): +class Hungary(WesternCalendar): 'Hungary' include_easter_sunday = True diff --git a/workalendar/europe/iceland.py b/workalendar/europe/iceland.py index 1219961..bd2b4f8 100644 --- a/workalendar/europe/iceland.py +++ b/workalendar/europe/iceland.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin, THU, MON +from ..core import WesternCalendar, THU, MON from ..registry_tools import iso_register @iso_register('IS') -class Iceland(WesternCalendar, ChristianMixin): +class Iceland(WesternCalendar): 'Iceland' include_holy_thursday = True diff --git a/workalendar/europe/ireland.py b/workalendar/europe/ireland.py index 08376b4..e040229 100644 --- a/workalendar/europe/ireland.py +++ b/workalendar/europe/ireland.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin, MON +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/workalendar/europe/italy.py b/workalendar/europe/italy.py index 2dee2eb..93eb7f0 100644 --- a/workalendar/europe/italy.py +++ b/workalendar/europe/italy.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('IT') -class Italy(WesternCalendar, ChristianMixin): +class Italy(WesternCalendar): 'Italy' FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( diff --git a/workalendar/europe/latvia.py b/workalendar/europe/latvia.py index 8c1df65..f6b67b9 100644 --- a/workalendar/europe/latvia.py +++ b/workalendar/europe/latvia.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('LV') -class Latvia(WesternCalendar, ChristianMixin): +class Latvia(WesternCalendar): 'Latvia' FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( diff --git a/workalendar/europe/lithuania.py b/workalendar/europe/lithuania.py index 6832b16..243d124 100644 --- a/workalendar/europe/lithuania.py +++ b/workalendar/europe/lithuania.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin, SUN +from ..core import WesternCalendar, SUN from ..registry_tools import iso_register @iso_register('LT') -class Lithuania(WesternCalendar, ChristianMixin): +class Lithuania(WesternCalendar): 'Lithuania' FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( diff --git a/workalendar/europe/luxembourg.py b/workalendar/europe/luxembourg.py index dbb208f..ae67528 100644 --- a/workalendar/europe/luxembourg.py +++ b/workalendar/europe/luxembourg.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('LU') -class Luxembourg(WesternCalendar, ChristianMixin): +class Luxembourg(WesternCalendar): 'Luxembourg' include_easter_monday = True diff --git a/workalendar/europe/malta.py b/workalendar/europe/malta.py index f76f841..b6ae564 100644 --- a/workalendar/europe/malta.py +++ b/workalendar/europe/malta.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('MT') -class Malta(WesternCalendar, ChristianMixin): +class Malta(WesternCalendar): 'Malta' include_good_friday = True diff --git a/workalendar/europe/netherlands.py b/workalendar/europe/netherlands.py index cbc9e0f..fba5de1 100644 --- a/workalendar/europe/netherlands.py +++ b/workalendar/europe/netherlands.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin, SUN +from ..core import WesternCalendar, SUN from ..registry_tools import iso_register @iso_register('NL') -class Netherlands(WesternCalendar, ChristianMixin): +class Netherlands(WesternCalendar): 'Netherlands' include_good_friday = True diff --git a/workalendar/europe/norway.py b/workalendar/europe/norway.py index d92d13d..936d9e6 100644 --- a/workalendar/europe/norway.py +++ b/workalendar/europe/norway.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('NO') -class Norway(WesternCalendar, ChristianMixin): +class Norway(WesternCalendar): 'Norway' include_holy_thursday = True diff --git a/workalendar/europe/poland.py b/workalendar/europe/poland.py index 7f88142..380467d 100644 --- a/workalendar/europe/poland.py +++ b/workalendar/europe/poland.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('PL') -class Poland(WesternCalendar, ChristianMixin): +class Poland(WesternCalendar): 'Poland' FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( diff --git a/workalendar/europe/portugal.py b/workalendar/europe/portugal.py index d040736..4ea0c2d 100644 --- a/workalendar/europe/portugal.py +++ b/workalendar/europe/portugal.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('PT') -class Portugal(WesternCalendar, ChristianMixin): +class Portugal(WesternCalendar): 'Portugal' include_good_friday = True diff --git a/workalendar/europe/romania.py b/workalendar/europe/romania.py index d0a21e1..51e20d0 100644 --- a/workalendar/europe/romania.py +++ b/workalendar/europe/romania.py @@ -1,14 +1,14 @@ from datetime import date -from ..core import WesternCalendar, OrthodoxMixin +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"), + 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"), diff --git a/workalendar/europe/russia.py b/workalendar/europe/russia.py index e2a6f9b..0eb2ccf 100644 --- a/workalendar/europe/russia.py +++ b/workalendar/europe/russia.py @@ -1,14 +1,14 @@ -from ..core import WesternCalendar +from ..core import OrthodoxCalendar from ..registry_tools import iso_register @iso_register('RU') -class Russia(WesternCalendar): +class Russia(OrthodoxCalendar): 'Russia' 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"), diff --git a/workalendar/europe/scotland/__init__.py b/workalendar/europe/scotland/__init__.py index 03a7d9d..a7a8388 100644 --- a/workalendar/europe/scotland/__init__.py +++ b/workalendar/europe/scotland/__init__.py @@ -24,7 +24,7 @@ FIXME: # 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 @@ from .mixins import ( ) -class Scotland(WesternCalendar, ChristianMixin): +class Scotland(WesternCalendar): "Scotland" FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( (1, 2, "New Year Holiday"), diff --git a/workalendar/europe/serbia.py b/workalendar/europe/serbia.py index ffd9d91..2d21d9d 100644 --- a/workalendar/europe/serbia.py +++ b/workalendar/europe/serbia.py @@ -1,12 +1,12 @@ -from ..core import WesternCalendar, OrthodoxMixin +from ..core import OrthodoxCalendar from ..registry_tools import iso_register @iso_register('RS') -class Serbia(OrthodoxMixin, WesternCalendar): +class Serbia(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"), diff --git a/workalendar/europe/slovakia.py b/workalendar/europe/slovakia.py index 71e0906..3a5d8ef 100644 --- a/workalendar/europe/slovakia.py +++ b/workalendar/europe/slovakia.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('SK') -class Slovakia(WesternCalendar, ChristianMixin): +class Slovakia(WesternCalendar): 'Slovakia' include_epiphany = True diff --git a/workalendar/europe/slovenia.py b/workalendar/europe/slovenia.py index 2b6210f..899a443 100644 --- a/workalendar/europe/slovenia.py +++ b/workalendar/europe/slovenia.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('SI') -class Slovenia(WesternCalendar, ChristianMixin): +class Slovenia(WesternCalendar): 'Slovenia' include_easter_sunday = True diff --git a/workalendar/europe/spain.py b/workalendar/europe/spain.py index 7d0aa54..f10700e 100644 --- a/workalendar/europe/spain.py +++ b/workalendar/europe/spain.py @@ -1,9 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..registry_tools import iso_register @iso_register('ES') -class Spain(WesternCalendar, ChristianMixin): +class Spain(WesternCalendar): 'Spain' include_epiphany = True diff --git a/workalendar/europe/sweden.py b/workalendar/europe/sweden.py index 114391b..0821484 100644 --- a/workalendar/europe/sweden.py +++ b/workalendar/europe/sweden.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin, FRI, SAT +from ..core import WesternCalendar, FRI, SAT from ..registry_tools import iso_register @iso_register('SE') -class Sweden(WesternCalendar, ChristianMixin): +class Sweden(WesternCalendar): 'Sweden' include_epiphany = True diff --git a/workalendar/europe/switzerland.py b/workalendar/europe/switzerland.py index cbbdaf2..d21fd05 100644 --- a/workalendar/europe/switzerland.py +++ b/workalendar/europe/switzerland.py @@ -1,10 +1,10 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin, SUN +from ..core import WesternCalendar, SUN from ..registry_tools import iso_register @iso_register('CH') -class Switzerland(WesternCalendar, ChristianMixin): +class Switzerland(WesternCalendar): 'Switzerland' # ChristianMixin entries common to (most) cantons - opt out diff --git a/workalendar/europe/turkey.py b/workalendar/europe/turkey.py index dd18d29..731be95 100644 --- a/workalendar/europe/turkey.py +++ b/workalendar/europe/turkey.py @@ -1,13 +1,14 @@ from datetime import timedelta -from ..core import WesternCalendar, IslamicMixin +from ..core import NewYearsDayMixin, IslamicCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('TR') -class Turkey(WesternCalendar, IslamicMixin): +class Turkey(NewYearsDayMixin, IslamicCalendar): 'Turkey' - shift_new_years_day = True + # 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,7 +16,7 @@ class Turkey(WesternCalendar, IslamicMixin): include_eid_al_adha = True length_eid_al_adha = 4 - FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = NewYearsDayMixin.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"), diff --git a/workalendar/europe/ukraine.py b/workalendar/europe/ukraine.py index 3ac7f91..000edbd 100644 --- a/workalendar/europe/ukraine.py +++ b/workalendar/europe/ukraine.py @@ -1,16 +1,16 @@ from datetime import date -from ..core import WesternCalendar, OrthodoxMixin +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"), diff --git a/workalendar/europe/united_kingdom.py b/workalendar/europe/united_kingdom.py index f2a0bd9..6f86688 100644 --- a/workalendar/europe/united_kingdom.py +++ b/workalendar/europe/united_kingdom.py @@ -1,10 +1,10 @@ from datetime import date -from ..core import WesternCalendar, ChristianMixin, MON +from ..core import WesternCalendar, MON 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/workalendar/oceania/australia.py b/workalendar/oceania/australia.py index 2a216c2..99cdc40 100644 --- a/workalendar/oceania/australia.py +++ b/workalendar/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 ..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/workalendar/oceania/marshall_islands.py b/workalendar/oceania/marshall_islands.py index 4410eee..f769826 100644 --- a/workalendar/oceania/marshall_islands.py +++ b/workalendar/oceania/marshall_islands.py @@ -1,10 +1,9 @@ -from ..core import WesternCalendar, ChristianMixin -from ..core import FRI +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/workalendar/oceania/new_zealand.py b/workalendar/oceania/new_zealand.py index 4967a79..6b08ebb 100644 --- a/workalendar/oceania/new_zealand.py +++ b/workalendar/oceania/new_zealand.py @@ -1,11 +1,11 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin, MON, SAT, SUN +from ..core import WesternCalendar, MON, SAT, SUN from ..registry_tools import iso_register @iso_register("NZ") -class NewZealand(WesternCalendar, ChristianMixin): +class NewZealand(WesternCalendar): "New Zealand" include_good_friday = True include_easter_monday = True diff --git a/workalendar/tests/__init__.py b/workalendar/tests/__init__.py index fa37265..6657e2e 100644 --- a/workalendar/tests/__init__.py +++ b/workalendar/tests/__init__.py @@ -5,11 +5,26 @@ from unittest import TestCase from ..core import Calendar -class GenericCalendarTest(TestCase): +class CoreCalendarTest(TestCase): cal_class = Calendar def setUp(self): super().setUp() - warnings.simplefilter('ignore') self.year = date.today().year self.cal = self.cal_class() + + +class GenericCalendarTest(CoreCalendarTest): + + 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) diff --git a/workalendar/tests/test_core.py b/workalendar/tests/test_core.py index 488c711..8914f40 100644 --- a/workalendar/tests/test_core.py +++ b/workalendar/tests/test_core.py @@ -4,16 +4,16 @@ from unittest import TestCase import pandas -from . import GenericCalendarTest +from . import CoreCalendarTest from ..core import ( MON, TUE, THU, FRI, WED, SAT, SUN, - Calendar, LunarCalendar, WesternCalendar, - IslamicMixin, JalaliMixin, ChristianMixin + Calendar, LunarMixin, WesternCalendar, + IslamicMixin, JalaliMixin ) from ..exceptions import UnsupportedDateType -class CalendarTest(GenericCalendarTest): +class CalendarTest(CoreCalendarTest): def test_private_variables(self): self.assertTrue(hasattr(self.cal, '_holidays')) @@ -104,12 +104,11 @@ class CalendarTest(GenericCalendarTest): ) -class LunarCalendarTest(GenericCalendarTest): - cal_class = LunarCalendar +class LunarCalendarTest(TestCase): - def test_new_year(self): + def test_lunar_new_year(self): self.assertEquals( - self.cal.lunar(2014, 1, 1), + LunarMixin.lunar(2014, 1, 1), date(2014, 1, 31) ) @@ -126,7 +125,7 @@ class MockCalendar(Calendar): return [] # no week-end, yes, it's sad -class MockCalendarTest(GenericCalendarTest): +class MockCalendarTest(CoreCalendarTest): cal_class = MockCalendar def test_holidays_set(self): @@ -239,7 +238,7 @@ class MockCalendarTest(GenericCalendarTest): ) -class IslamicMixinTest(GenericCalendarTest): +class IslamicMixinTest(CoreCalendarTest): cal_class = IslamicMixin def test_year_conversion(self): @@ -247,7 +246,7 @@ class IslamicMixinTest(GenericCalendarTest): self.assertEquals(len(days), 365) -class JalaliMixinTest(GenericCalendarTest): +class JalaliMixinTest(CoreCalendarTest): cal_class = JalaliMixin def test_year_conversion(self): @@ -255,11 +254,12 @@ class JalaliMixinTest(GenericCalendarTest): self.assertEquals(len(days), 365) -class MockChristianCalendar(WesternCalendar, ChristianMixin): +class MockChristianCalendar(WesternCalendar): + # WesternCalendar inherits from ChristianMixin pass -class MockChristianCalendarTest(GenericCalendarTest): +class MockChristianCalendarTest(CoreCalendarTest): cal_class = MockChristianCalendar def test_year_2014(self): @@ -297,7 +297,7 @@ class NoWeekendCalendar(Calendar): """ -class NoWeekendCalendarTest(GenericCalendarTest): +class NoWeekendCalendarTest(CoreCalendarTest): cal_class = NoWeekendCalendar def test_weekend(self): @@ -317,7 +317,7 @@ class WeekendOnWednesdayCalendar(Calendar): WEEKEND_DAYS = (WED,) -class WeekendOnWednesdayCalendarTest(GenericCalendarTest): +class WeekendOnWednesdayCalendarTest(CoreCalendarTest): cal_class = WeekendOnWednesdayCalendar def test_weekend(self): @@ -337,7 +337,7 @@ class OverwriteGetWeekendDaysCalendar(Calendar): return (WED,) -class OverwriteGetWeekendDaysCalendarTest(GenericCalendarTest): +class OverwriteGetWeekendDaysCalendarTest(CoreCalendarTest): cal_class = OverwriteGetWeekendDaysCalendar def test_weekend(self): @@ -487,7 +487,7 @@ class CalendarClassName(TestCase): ) -class TestAcceptableDateTypes(GenericCalendarTest): +class TestAcceptableDateTypes(CoreCalendarTest): """ Test cases about accepted date and datetime types. """ @@ -642,7 +642,7 @@ class TestAcceptableDateTypes(GenericCalendarTest): 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): diff --git a/workalendar/tests/test_europe.py b/workalendar/tests/test_europe.py index e8e65cc..8e5a3f9 100644 --- a/workalendar/tests/test_europe.py +++ b/workalendar/tests/test_europe.py @@ -72,8 +72,7 @@ class BelarusTest(GenericCalendarTest): 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, 6), holidays) # Radonista - self.assertIn(date(2019, 5, 7), holidays) # Radonista Holiday + 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 @@ -85,8 +84,7 @@ class BelarusTest(GenericCalendarTest): 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, 27), holidays) # Radonista - self.assertIn(date(2020, 4, 28), holidays) # Radonista Holiday + 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 @@ -569,6 +567,24 @@ class GreeceTest(GenericCalendarTest): 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 + class HungaryTest(GenericCalendarTest): cal_class = Hungary diff --git a/workalendar/usa/core.py b/workalendar/usa/core.py index 2968ab9..03b97a3 100644 --- a/workalendar/usa/core.py +++ b/workalendar/usa/core.py @@ -1,12 +1,12 @@ from datetime import date, timedelta -from ..core import WesternCalendar, ChristianMixin +from ..core import WesternCalendar from ..core import SUN, MON, TUE, WED, THU, FRI, SAT 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 + (