diff --git a/Changelog.md b/Changelog.md index d7a04e1..ac207ec 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ ## master (unreleased) - 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. - 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. diff --git a/contributing.md b/contributing.md index 22cda14..920cc5e 100644 --- a/contributing.md +++ b/contributing.md @@ -117,7 +117,7 @@ workon WORKALENDAR 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. +With the `WesternCalendar` base class you have at least one holiday as a bonus: the New Year's day, which is almost a worldwide holiday. #### Add fixed days diff --git a/workalendar/africa/algeria.py b/workalendar/africa/algeria.py index 8a03190..5fd8ead 100644 --- a/workalendar/africa/algeria.py +++ b/workalendar/africa/algeria.py @@ -1,9 +1,9 @@ -from ..core import IslamicCalendar, NewYearsDayMixin +from ..core import IslamicCalendar from ..registry_tools import iso_register @iso_register('DZ') -class Algeria(NewYearsDayMixin, IslamicCalendar): +class Algeria(IslamicCalendar): "Algeria" # Islamic holidays include_prophet_birthday = True @@ -11,8 +11,7 @@ class Algeria(NewYearsDayMixin, IslamicCalendar): include_day_of_sacrifice = True include_islamic_new_year = True - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + \ - IslamicCalendar.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = IslamicCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (7, 5, "Independence Day"), (11, 1, "Anniversary of the revolution"), diff --git a/workalendar/africa/benin.py b/workalendar/africa/benin.py index 42f44da..f697c8f 100644 --- a/workalendar/africa/benin.py +++ b/workalendar/africa/benin.py @@ -1,9 +1,9 @@ -from ..core import NewYearsDayMixin, IslamoWesternCalendar, SAT, SUN +from ..core import IslamoWesternCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('BJ') -class Benin(NewYearsDayMixin, IslamoWesternCalendar): +class Benin(IslamoWesternCalendar): "Benin" # Christian holidays include_easter_monday = True @@ -17,7 +17,7 @@ class Benin(NewYearsDayMixin, IslamoWesternCalendar): include_day_of_sacrifice = True include_day_of_sacrifice_label = "Tabaski" - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = IslamoWesternCalendar.FIXED_HOLIDAYS + ( (1, 10, "Traditional Day"), (5, 1, "Labour Day"), (8, 1, "Independence Day"), @@ -25,5 +25,5 @@ class Benin(NewYearsDayMixin, IslamoWesternCalendar): (11, 30, "National Day"), ) - # Explicitely assign these WE days, Benin has adopted the western workweek + # Explicitly 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 c848d41..f2b6320 100644 --- a/workalendar/africa/ivory_coast.py +++ b/workalendar/africa/ivory_coast.py @@ -1,9 +1,9 @@ -from ..core import NewYearsDayMixin, IslamoWesternCalendar, SAT, SUN +from ..core import IslamoWesternCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('CI') -class IvoryCoast(NewYearsDayMixin, IslamoWesternCalendar): +class IvoryCoast(IslamoWesternCalendar): "Ivory Coast" # Christian holidays include_easter_monday = True @@ -17,7 +17,7 @@ class IvoryCoast(NewYearsDayMixin, IslamoWesternCalendar): include_day_of_sacrifice = True include_day_of_sacrifice_label = "Feast of the Sacrifice" - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = IslamoWesternCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (8, 7, "Independence Day"), (11, 15, "National Peace Day"), diff --git a/workalendar/africa/kenya.py b/workalendar/africa/kenya.py index 12c2834..c21c28a 100644 --- a/workalendar/africa/kenya.py +++ b/workalendar/africa/kenya.py @@ -1,12 +1,12 @@ from copy import copy from datetime import timedelta, date -from ..core import NewYearsDayMixin, IslamoWesternCalendar, SAT, SUN +from ..core import IslamoWesternCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('KE') -class Kenya(NewYearsDayMixin, IslamoWesternCalendar): +class Kenya(IslamoWesternCalendar): "Kenya" # Christian holidays include_good_friday = True @@ -16,10 +16,10 @@ class Kenya(NewYearsDayMixin, IslamoWesternCalendar): include_day_of_sacrifice = True shift_sunday_holidays = True - # Explicitely assign these WE days, Kenya has adopted the western workweek + # Explicitly assign these WE days, Kenya has adopted the western workweek WEEKEND_DAYS = (SAT, SUN) - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = IslamoWesternCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (6, 1, "Madaraka Day"), (10, 20, "Mashujaa Day"), diff --git a/workalendar/asia/china.py b/workalendar/asia/china.py index 351bb44..09b46d7 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, NewYearsDayMixin +from ..core import ChineseNewYearCalendar from ..registry_tools import iso_register from ..exceptions import CalendarError @@ -56,12 +56,12 @@ workdays = { @iso_register('CN') -class China(NewYearsDayMixin, ChineseNewYearCalendar): +class China(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 = NewYearsDayMixin.FIXED_HOLIDAYS + tuple(national_days) + 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 a281a15..48df4cc 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, NewYearsDayMixin, WesternMixin, + ChineseNewYearCalendar, WesternMixin, SUN, SAT ) from ..astronomy import solar_term @@ -9,7 +9,7 @@ from ..registry_tools import iso_register @iso_register('HK') -class HongKong(NewYearsDayMixin, WesternMixin, ChineseNewYearCalendar): +class HongKong(WesternMixin, ChineseNewYearCalendar): "Hong Kong" include_good_friday = True include_easter_saturday = True @@ -18,7 +18,7 @@ class HongKong(NewYearsDayMixin, WesternMixin, ChineseNewYearCalendar): WEEKEND_DAYS = (SUN,) - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (7, 1, "SAR Establishment Day"), (10, 1, "National Day"), diff --git a/workalendar/asia/israel.py b/workalendar/asia/israel.py index 59db5f5..adec293 100644 --- a/workalendar/asia/israel.py +++ b/workalendar/asia/israel.py @@ -7,7 +7,7 @@ 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): diff --git a/workalendar/asia/japan.py b/workalendar/asia/japan.py index c9ac075..04ffc17 100644 --- a/workalendar/asia/japan.py +++ b/workalendar/asia/japan.py @@ -1,18 +1,18 @@ from datetime import date -from ..core import NewYearsDayMixin, Calendar, MON, SAT, SUN +from ..core import Calendar, MON, SAT, SUN from ..astronomy import calculate_equinoxes from ..registry_tools import iso_register @iso_register('JP') -class Japan(NewYearsDayMixin, Calendar): +class Japan(Calendar): "Japan" # Japan uses the "western" workweek WEEKEND_DAYS = (SAT, SUN) - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = Calendar.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 a8f6e64..fa20388 100644 --- a/workalendar/asia/malaysia.py +++ b/workalendar/asia/malaysia.py @@ -1,14 +1,14 @@ from datetime import date from ..core import ( - NewYearsDayMixin, IslamicMixin, ChineseNewYearCalendar, + IslamicMixin, ChineseNewYearCalendar, SAT, SUN ) from ..registry_tools import iso_register @iso_register('MY') -class Malaysia(NewYearsDayMixin, IslamicMixin, ChineseNewYearCalendar): +class Malaysia(IslamicMixin, ChineseNewYearCalendar): "Malaysia" include_nuzul_al_quran = True include_eid_al_fitr = True @@ -23,7 +23,7 @@ class Malaysia(NewYearsDayMixin, IslamicMixin, ChineseNewYearCalendar): WEEKEND_DAYS = (SAT, SUN) # TODO: Add calendar exceptions - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = ChineseNewYearCalendar.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 4199d87..9cf3ace 100644 --- a/workalendar/asia/qatar.py +++ b/workalendar/asia/qatar.py @@ -6,6 +6,8 @@ from ..registry_tools import iso_register class Qatar(IslamicCalendar): "Qatar" + include_new_years_day = False + FIXED_HOLIDAYS = ( (12, 18, "National Day"), ) diff --git a/workalendar/asia/singapore.py b/workalendar/asia/singapore.py index 76ed315..722a816 100644 --- a/workalendar/asia/singapore.py +++ b/workalendar/asia/singapore.py @@ -1,15 +1,14 @@ from datetime import date from ..core import ( - NewYearsDayMixin, WesternMixin, IslamicMixin, ChineseNewYearCalendar, + WesternMixin, IslamicMixin, ChineseNewYearCalendar, SAT, SUN ) from ..registry_tools import iso_register @iso_register('SG') -class Singapore(NewYearsDayMixin, WesternMixin, IslamicMixin, - ChineseNewYearCalendar): +class Singapore(WesternMixin, IslamicMixin, ChineseNewYearCalendar): "Singapore" # Christian holiday include_good_friday = True @@ -20,12 +19,12 @@ class Singapore(NewYearsDayMixin, WesternMixin, IslamicMixin, include_day_of_sacrifice = True day_of_sacrifice_label = "Hari Raya Haji" - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = ChineseNewYearCalendar.FIXED_HOLIDAYS + ( (5, 1, "Labour Day"), (8, 9, "National Day"), ) - # Explicitely assign these WE days, Singapore calendar is too much of a mix + # 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 diff --git a/workalendar/asia/south_korea.py b/workalendar/asia/south_korea.py index 9849481..2e194b6 100644 --- a/workalendar/asia/south_korea.py +++ b/workalendar/asia/south_korea.py @@ -1,11 +1,11 @@ -from ..core import NewYearsDayMixin, ChineseNewYearCalendar +from ..core import ChineseNewYearCalendar from ..registry_tools import iso_register @iso_register('KR') -class SouthKorea(NewYearsDayMixin, ChineseNewYearCalendar): +class SouthKorea(ChineseNewYearCalendar): "South Korea" - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + FIXED_HOLIDAYS = ChineseNewYearCalendar.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 2430d16..f84bef9 100644 --- a/workalendar/asia/taiwan.py +++ b/workalendar/asia/taiwan.py @@ -1,18 +1,15 @@ -from ..core import NewYearsDayMixin, ChineseNewYearCalendar +from ..core import ChineseNewYearCalendar from ..astronomy import solar_term from ..registry_tools import iso_register @iso_register('TW') -class Taiwan(NewYearsDayMixin, ChineseNewYearCalendar): +class Taiwan(ChineseNewYearCalendar): "Taiwan (Republic of China)" - FIXED_HOLIDAYS = ( - NewYearsDayMixin.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/workalendar/core.py b/workalendar/core.py index a38592e..a3c688f 100644 --- a/workalendar/core.py +++ b/workalendar/core.py @@ -42,24 +42,6 @@ def cleaned_date(day, keep_datetime=False): return day -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 @@ -813,16 +795,40 @@ class CoreCalendar: class Calendar(CoreCalendar): - pass + """ + 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 + + 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") + ) + 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 WesternCalendar(NewYearsDayMixin, WesternMixin, Calendar): +class WesternCalendar(WesternMixin, Calendar): """ A Christian calendar using Western definition for Easter. """ -class OrthodoxCalendar(NewYearsDayMixin, OrthodoxMixin, Calendar): +class OrthodoxCalendar(OrthodoxMixin, Calendar): """ A Christian calendar using Orthodox definition for Easter. """ diff --git a/workalendar/europe/turkey.py b/workalendar/europe/turkey.py index 731be95..c8dacc8 100644 --- a/workalendar/europe/turkey.py +++ b/workalendar/europe/turkey.py @@ -1,10 +1,10 @@ from datetime import timedelta -from ..core import NewYearsDayMixin, IslamicCalendar, SAT, SUN +from ..core import IslamicCalendar, SAT, SUN from ..registry_tools import iso_register @iso_register('TR') -class Turkey(NewYearsDayMixin, IslamicCalendar): +class Turkey(IslamicCalendar): 'Turkey' shift_new_years_day = True # Even though they're using an islamic calendar, the work week is MON->FRI @@ -16,7 +16,7 @@ class Turkey(NewYearsDayMixin, IslamicCalendar): include_eid_al_adha = True length_eid_al_adha = 4 - FIXED_HOLIDAYS = NewYearsDayMixin.FIXED_HOLIDAYS + ( + 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"), diff --git a/workalendar/tests/__init__.py b/workalendar/tests/__init__.py index 6657e2e..98c889a 100644 --- a/workalendar/tests/__init__.py +++ b/workalendar/tests/__init__.py @@ -15,6 +15,7 @@ class CoreCalendarTest(TestCase): class GenericCalendarTest(CoreCalendarTest): + test_include_january_1st = True def setUp(self): super().setUp() @@ -28,3 +29,13 @@ class GenericCalendarTest(CoreCalendarTest): 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) diff --git a/workalendar/tests/test_asia.py b/workalendar/tests/test_asia.py index f3acd30..da2da8c 100644 --- a/workalendar/tests/test_asia.py +++ b/workalendar/tests/test_asia.py @@ -440,6 +440,7 @@ class MalaysiaTest(GenericCalendarTest): class QatarTest(GenericCalendarTest): cal_class = Qatar + test_include_january_1st = False def test_year_2013(self): holidays = self.cal.holidays_set(2013) @@ -580,6 +581,7 @@ class TaiwanTest(GenericCalendarTest): class IsraelTest(GenericCalendarTest): cal_class = Israel + test_include_january_1st = False def test_holidays_2017(self): calculated_holidays = self.cal.holidays_set(2017) diff --git a/workalendar/tests/test_core.py b/workalendar/tests/test_core.py index 8914f40..959f47e 100644 --- a/workalendar/tests/test_core.py +++ b/workalendar/tests/test_core.py @@ -348,6 +348,7 @@ class OverwriteGetWeekendDaysCalendarTest(CoreCalendarTest): class NoHolidayCalendar(Calendar): + include_new_years_day = False WEEKEND_DAYS = (SAT, SUN)