diff --git a/Changelog.md b/Changelog.md index 9069c09..fbca79c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -3,6 +3,7 @@ ## master (unreleased) - Small fixes (docstrings, use of extends, etc) on Cayman Islands calendar (#507). +- Moving Carnaval / Mardi Gras / Fat Tuesday calculation into the `workalendar.core` module, because it's used in at least 3 countries and some States / Counties in the USA. ## v10.0.0 (2020-06-05) diff --git a/workalendar/africa/angola.py b/workalendar/africa/angola.py index c58db3f..9172c91 100644 --- a/workalendar/africa/angola.py +++ b/workalendar/africa/angola.py @@ -1,12 +1,13 @@ -from datetime import timedelta from ..core import WesternCalendar, ChristianMixin from ..registry_tools import iso_register @iso_register('AO') class Angola(WesternCalendar, ChristianMixin): - """Angola""" + "Angola" + include_fat_tuesday = True + fat_tuesday_label = "Dia de Carnaval" include_good_friday = True include_easter_sunday = True include_christmas = True @@ -20,12 +21,3 @@ class Angola(WesternCalendar, ChristianMixin): (9, 17, "Dia do Fundador da Nação e do Herói Nacional"), (11, 11, "Dia da Independência Nacional"), ) - - def get_variable_entrudo(self, year): - easter_sunday = self.get_easter_sunday(year) - return easter_sunday - timedelta(days=47) - - def get_variable_days(self, year): - days = super().get_variable_days(year) - days.append((self.get_variable_entrudo(year), "Dia de Carnaval")) - return days diff --git a/workalendar/america/argentina.py b/workalendar/america/argentina.py index b684b3c..b07d523 100644 --- a/workalendar/america/argentina.py +++ b/workalendar/america/argentina.py @@ -10,6 +10,8 @@ from ..registry_tools import iso_register class Argentina(WesternCalendar, ChristianMixin): 'Argentina' + include_fat_tuesday = True + fat_tuesday_label = "Carnival" include_good_friday = True include_easter_saturday = True include_easter_sunday = True @@ -130,10 +132,6 @@ class Argentina(WesternCalendar, ChristianMixin): (self.get_easter_sunday(year) - timedelta(days=48), "Carnival Lunes")) - days.append( - (self.get_easter_sunday(year) - timedelta(days=47), - "Carnival")) - days.append( self.get_malvinas_day(year)) diff --git a/workalendar/america/brazil.py b/workalendar/america/brazil.py index dd1d728..2fb57c7 100644 --- a/workalendar/america/brazil.py +++ b/workalendar/america/brazil.py @@ -33,14 +33,6 @@ class Brazil(WesternCalendar, ChristianMixin): include_nossa_senhora_conceicao = False include_easter_sunday = True - def get_carnaval(self, year): - """ - Return the third day of Carnaval (Tuesday) - - This day is shared holidays by several Brazil states. - """ - return self.get_easter_sunday(year) - timedelta(days=47) - def get_variable_days(self, year): days = super().get_variable_days(year) if self.include_sao_jose: @@ -231,6 +223,8 @@ class BrazilRioDeJaneiro(Brazil): (4, 23, "Dia de São Jorge"), (3, 1, "Aniversário da Cidade do Rio de Janeiro"), ) + include_fat_tuesday = True + fat_tuesday_label = "Carnaval" include_servidor_publico = True servidor_publico_label = "Dia do Funcionário Público" include_consciencia_negra = True @@ -247,7 +241,6 @@ class BrazilRioDeJaneiro(Brazil): def get_variable_days(self, year): days = super().get_variable_days(year) - days.append((self.get_carnaval(year), "Carnaval")) days.append((self.get_dia_do_comercio(year), "Dia do Comércio")) return days @@ -309,6 +302,8 @@ class BrazilSaoPauloCity(BrazilSaoPauloState): FIXED_HOLIDAYS = BrazilSaoPauloState.FIXED_HOLIDAYS + ( (1, 25, "Anniversary of the city of São Paulo"), ) + include_fat_tuesday = True + fat_tuesday_label = "Carnaval" include_easter_sunday = True include_corpus_christi = True include_good_friday = True @@ -316,11 +311,6 @@ class BrazilSaoPauloCity(BrazilSaoPauloState): include_consciencia_negra = True consciencia_negra_label = "Dia da Consciência Negra" - def get_variable_days(self, year): - days = super().get_variable_days(year) - days.append((self.get_carnaval(year), "Carnaval")) - return days - @iso_register('BR-SE') class BrazilSergipe(Brazil): @@ -388,6 +378,8 @@ class BrazilSerraCity(BrazilEspiritoSanto): FIXED_HOLIDAYS = BrazilEspiritoSanto.FIXED_HOLIDAYS + ( (12, 26, "Dia do Serrano"), ) + include_fat_tuesday = True + fat_tuesday_label = "Carnaval" include_ash_wednesday = True ash_wednesday_label = "Quarta-feira de cinzas" include_good_friday = True @@ -397,9 +389,8 @@ class BrazilSerraCity(BrazilEspiritoSanto): def get_variable_days(self, year): days = super().get_variable_days(year) - carnaval_tuesday = self.get_carnaval(year) + carnaval_tuesday = self.get_fat_tuesday(year) days.append((carnaval_tuesday - timedelta(days=1), "Carnaval Monday")) - days.append((carnaval_tuesday, "Carnaval")) return days @@ -583,6 +574,8 @@ class BrazilBankCalendar(Brazil): Calendar that considers only working days for bank transactions for companies and the general public """ + include_fat_tuesday = True + fat_tuesday_label = "Tuesday carnaval" include_good_friday = True include_ash_wednesday = True include_corpus_christi = True @@ -610,22 +603,17 @@ class BrazilBankCalendar(Brazil): day for only internal bank transactions """ days = super().get_variable_days(year) - tuesday_carnaval = self.get_carnaval(year) + tuesday_carnaval = self.get_fat_tuesday(year) monday_carnaval = tuesday_carnaval - timedelta(days=1) - carnaval_days = [ - (monday_carnaval, "Monday carnaval"), - (tuesday_carnaval, "Tuesday carnaval"), - ] + days.append((monday_carnaval, "Monday carnaval")) - non_working_days = [ - ( - self.get_last_day_of_year_for_only_internal_bank_trans(year), - "Last day of year for only internal bank transactions" - ) - ] + days.append(( + self.get_last_day_of_year_for_only_internal_bank_trans(year), + "Last day of year for only internal bank transactions" + )) - return days + carnaval_days + non_working_days + return days def find_following_working_day(self, day): """ diff --git a/workalendar/core.py b/workalendar/core.py index c701b66..8b0a2d0 100644 --- a/workalendar/core.py +++ b/workalendar/core.py @@ -10,7 +10,7 @@ from calverter import Calverter from dateutil import easter from lunardate import LunarDate -from .exceptions import UnsupportedDateType +from .exceptions import UnsupportedDateType, CalendarError MON, TUE, WED, THU, FRI, SAT, SUN = range(7) @@ -373,6 +373,10 @@ class ChristianMixin(Calendar): 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 @@ -398,6 +402,14 @@ class ChristianMixin(Calendar): boxing_day_label = "Boxing Day" include_all_souls = False + def get_fat_tuesday(self, year): + if not self.fat_tuesday_label: + raise CalendarError( + "Improperly configured: please provide a " + "`fat_tuesday_label` value") + sunday = self.get_easter_sunday(year) + return sunday - timedelta(days=47) + def get_ash_wednesday(self, year): sunday = self.get_easter_sunday(year) return sunday - timedelta(days=46) @@ -476,6 +488,10 @@ class ChristianMixin(Calendar): 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) diff --git a/workalendar/tests/test_africa.py b/workalendar/tests/test_africa.py index 990dabc..c00ac70 100644 --- a/workalendar/tests/test_africa.py +++ b/workalendar/tests/test_africa.py @@ -549,6 +549,12 @@ class AngolaTest(GenericCalendarTest): # Dia do Natal – 25 de Dezembro self.assertIn(date(2018, 12, 25), holidays) # Natal + def test_carnaval_label(self): + holidays = self.cal.holidays(2018) + holidays_dict = dict(holidays) + label_carnaval = holidays_dict[date(2018, 2, 13)] + self.assertEqual(label_carnaval, "Dia de Carnaval") + class KenyaTest(GenericCalendarTest): cal_class = Kenya diff --git a/workalendar/tests/test_america.py b/workalendar/tests/test_america.py index 2beaf89..bbea135 100644 --- a/workalendar/tests/test_america.py +++ b/workalendar/tests/test_america.py @@ -118,6 +118,12 @@ class ArgentinaTest(GenericCalendarTest): "Día Nacional de la Memoria por la Verdad y la Justicia" ) + def test_carnival_label(self): + holidays = self.cal.holidays(2020) + holidays = dict(holidays) + label_carnival = holidays[date(2020, 2, 25)] + self.assertEqual(label_carnival, "Carnival") + class ChileTest(GenericCalendarTest): cal_class = Chile diff --git a/workalendar/tests/test_brazil.py b/workalendar/tests/test_brazil.py index eb1d50a..94297c0 100644 --- a/workalendar/tests/test_brazil.py +++ b/workalendar/tests/test_brazil.py @@ -246,6 +246,12 @@ class BrazilRioDeJaneiroTest(BrazilTest): # Dia de Nossa Senhora da Conceição self.assertIn(date(2017, 12, 8), holidays) + def test_carnaval_label(self): + holidays = self.cal.holidays(2017) + holidays_dict = dict(holidays) + label_carnaval = holidays_dict[date(2017, 2, 28)] + self.assertEqual(label_carnaval, "Carnaval") + class BrazilRioGrandeDoNorteTest(BrazilTest): cal_class = BrazilRioGrandeDoNorte @@ -338,6 +344,12 @@ class SaoPauloCityTest(SaoPauloStateTest): "Sexta-feira da Paixão", ) + def test_carnaval_label(self): + holidays = self.cal.holidays(2013) + holidays_dict = dict(holidays) + label_carnaval = holidays_dict[date(2013, 2, 12)] + self.assertEqual(label_carnaval, "Carnaval") + class BrazilSergipeTest(BrazilTest): cal_class = BrazilSergipe @@ -481,6 +493,12 @@ class BrazilSerraCityTest(BrazilEspiritoSantoTest): "Paixão do Cristo", ) + def test_carnaval_label(self): + holidays = self.cal.holidays(2017) + holidays_dict = dict(holidays) + label_carnaval = holidays_dict[date(2017, 2, 28)] + self.assertEqual(label_carnaval, "Carnaval") + class BrazilRioBrancoCityTest(BrazilAcreTest): """ @@ -899,6 +917,12 @@ class BrazilBankCalendarTest(BrazilTest): working_day = self.cal.find_following_working_day(already_working_day) self.assertEquals(working_day, date(2017, 7, 25)) + def test_carnaval_label(self): + holidays = self.cal.holidays(2017) + holidays_dict = dict(holidays) + label_carnaval = holidays_dict[date(2017, 2, 28)] + self.assertEqual(label_carnaval, "Tuesday carnaval") + class TestIBGERegister(TestCase): diff --git a/workalendar/tests/test_usa.py b/workalendar/tests/test_usa.py index fdc616f..c5aa23f 100644 --- a/workalendar/tests/test_usa.py +++ b/workalendar/tests/test_usa.py @@ -207,7 +207,7 @@ class UnitedStatesTest(GenericCalendarTest): def test_mardi_gras(self): year = 2017 - day, _ = self.cal.get_mardi_gras(year) + day = self.cal.get_fat_tuesday(year) holidays = self.cal.holidays_set(year) self.assertNotIn(day, holidays) @@ -316,9 +316,11 @@ class IncludeMardiGras: """ def test_mardi_gras(self): year = 2017 - day, _ = self.cal.get_mardi_gras(year) - holidays = self.cal.holidays_set(year) - self.assertIn(day, holidays) + day = self.cal.get_fat_tuesday(year) + holidays = self.cal.holidays(year) + holidays_dict = dict(holidays) + self.assertIn(day, holidays_dict) + self.assertEqual(holidays_dict[day], "Mardi Gras") class AlabamaTest(UnitedStatesTest): diff --git a/workalendar/usa/alabama.py b/workalendar/usa/alabama.py index d0d6f48..785fe64 100644 --- a/workalendar/usa/alabama.py +++ b/workalendar/usa/alabama.py @@ -16,12 +16,12 @@ class Alabama(UnitedStates): class AlabamaBaldwinCounty(Alabama): "Baldwin County, Alabama" - include_mardi_gras = True + include_fat_tuesday = True class AlabamaMobileCounty(Alabama): "Mobile County, Alabama" - include_mardi_gras = True + include_fat_tuesday = True class AlabamaPerryCounty(Alabama): diff --git a/workalendar/usa/core.py b/workalendar/usa/core.py index 2ad8e0f..2968ab9 100644 --- a/workalendar/usa/core.py +++ b/workalendar/usa/core.py @@ -59,7 +59,8 @@ class UnitedStates(WesternCalendar, ChristianMixin): national_memorial_day_label = "Memorial Day" # Some regional variants - include_mardi_gras = False + include_fat_tuesday = False + fat_tuesday_label = "Mardi Gras" # Shift day mechanism # These days won't be shifted to next MON or previous FRI @@ -272,13 +273,6 @@ class UnitedStates(WesternCalendar, ChristianMixin): self.national_memorial_day_label ) - def get_mardi_gras(self, year): - """ - Mardi Gras is the Tuesday happening 47 days before Easter - """ - sunday = self.get_easter_sunday(year) - return (sunday - timedelta(days=47), "Mardi Gras") - def get_variable_days(self, year): # usual variable days days = super().get_variable_days(year) @@ -295,9 +289,6 @@ class UnitedStates(WesternCalendar, ChristianMixin): "Thanksgiving Day"), ]) - if self.include_mardi_gras: - days.append(self.get_mardi_gras(year)) - if self.include_federal_presidents_day: days.append(self.get_presidents_day(year)) diff --git a/workalendar/usa/florida.py b/workalendar/usa/florida.py index 06f88d6..bec0770 100644 --- a/workalendar/usa/florida.py +++ b/workalendar/usa/florida.py @@ -78,7 +78,7 @@ class FloridaLegal(Florida): (4, 2, 'Pascua Florida Day'), (6, 14, 'Flag Day'), ) - include_mardi_gras = True + include_fat_tuesday = True include_lincoln_birthday = True include_federal_presidents_day = True include_good_friday = True diff --git a/workalendar/usa/louisiana.py b/workalendar/usa/louisiana.py index 6eda673..0f5c349 100644 --- a/workalendar/usa/louisiana.py +++ b/workalendar/usa/louisiana.py @@ -8,4 +8,4 @@ class Louisiana(UnitedStates): include_good_friday = True include_election_day_even = True include_columbus_day = False - include_mardi_gras = True + include_fat_tuesday = True