Repository: peopledoc/workalendar
Pull request #415 (2019-11-08 16:39:37)

More brazilian holidays ibge
superseedes and potentially closes #409 

Important items:

* Added 27 Brazil calendars (thanks a lot to @luismalta & @mileo),
* Fixes and additions to some calendars (again, thanks to @luismalta & @mileo),
* Added an IGBE_REGISTER to reference IGBE (brazilian) calendars with related tests.


- [x] Tests with a significant number of years to be tested for your calendar.
- [ ] Changelog amended with a mention describing your changes.


Patch:
diff --git a/Changelog.md b/Changelog.md
index f268eb7..4eeeca8 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -5,6 +5,9 @@
 - Increase Malaysia coverage by adding tests for missing Deepavali & Thaipusam.
 - Increase China coverage by adding tests for special extra-holidays & extra-working days cases.
 - Added compatibility with Python 3.8 (#406).
+- Added 27 Brazil calendars -- thanks a lot to @luismalta & @mileo, (#409 & #415),
+- Fixes and additions to some calendars -- again, thanks to @luismalta & @mileo, (#409 & #415)
+- Added an IBGE_REGISTER to reference IBGE (brazilian) calendars with related tests (#415).
 
 ## v7.0.0 (2019-09-20)
 
diff --git a/workalendar/america/__init__.py b/workalendar/america/__init__.py
index 7d8117b..20d92c1 100644
--- a/workalendar/america/__init__.py
+++ b/workalendar/america/__init__.py
@@ -3,13 +3,21 @@ from .barbados import Barbados
 from .brazil import (
     Brazil, BrazilAcre, BrazilAlagoas, BrazilAmapa, BrazilAmazonas,
     BrazilBahia, BrazilCeara, BrazilDistritoFederal, BrazilEspiritoSanto,
-    BrazilGoias, BrazilMaranhao, BrazilMatoGrosso, BrazilMatoGrossoDoSul,
-    BrazilPara, BrazilParaiba, BrazilPernambuco, BrazilPiaui,
-    BrazilRioDeJaneiro, BrazilRioGrandeDoNorte, BrazilRioGrandeDoSul,
-    BrazilRondonia, BrazilRoraima, BrazilSantaCatarina, BrazilSaoPauloState,
-    BrazilSaoPauloCity, BrazilSergipe, BrazilTocantins, BrazilVitoriaCity,
-    BrazilVilaVelhaCity, BrazilCariacicaCity, BrazilGuarapariCity,
-    BrazilSerraCity, BrazilBankCalendar
+    BrazilGoias, BrazilMaranhao, BrazilMinasGerais, BrazilMatoGrosso,
+    BrazilMatoGrossoDoSul, BrazilPara, BrazilParaiba, BrazilPernambuco,
+    BrazilPiaui, BrazilParana, BrazilRioDeJaneiro, BrazilRioGrandeDoNorte,
+    BrazilRioGrandeDoSul, BrazilRondonia, BrazilRoraima, BrazilSantaCatarina,
+    BrazilSaoPauloState, BrazilSaoPauloCity, BrazilSergipe, BrazilTocantins,
+    BrazilVitoriaCity, BrazilVilaVelhaCity, BrazilCariacicaCity,
+    BrazilGuarapariCity, BrazilSerraCity, BrazilRioBrancoCity,
+    BrazilMaceioCity, BrazilManausCity, BrazilMacapaCity, BrazilSalvadorCity,
+    BrazilFortalezaCity, BrazilGoianiaCity, BrazilBeloHorizonteCity,
+    BrazilCampoGrandeCity, BrazilCuiabaCity, BrazilBelemCity,
+    BrazilJoaoPessoaCity, BrazilRecifeCity, BrazilTeresinaCity,
+    BrazilCuritibaCity, BrazilNatalCity, BrazilPortoVelhoCity,
+    BrazilBoaVistaCity, BrazilPortoAlegreCity, BrazilChapecoCity,
+    BrazilFlorianopolisCity, BrazilJoinvilleCity, BrazilAracajuCity,
+    BrazilSorocabaCity, BrazilPalmasCity, BrazilBankCalendar
 )
 from .canada import (
     Canada, Ontario, Quebec, BritishColumbia, Alberta, Saskatchewan, Manitoba,
@@ -36,12 +44,14 @@ __all__ = (
     'BrazilEspiritoSanto',
     'BrazilGoias',
     'BrazilMaranhao',
+    'BrazilMinasGerais',
     'BrazilMatoGrosso',
     'BrazilMatoGrossoDoSul',
     'BrazilPara',
     'BrazilParaiba',
     'BrazilPernambuco',
     'BrazilPiaui',
+    'BrazilParana',
     'BrazilRioDeJaneiro',
     'BrazilRioGrandeDoNorte',
     'BrazilRioGrandeDoSul',
@@ -58,6 +68,31 @@ __all__ = (
     'BrazilGuarapariCity',
     'BrazilSerraCity',
     'BrazilBankCalendar',
+    'BrazilRioBrancoCity',
+    'BrazilMaceioCity',
+    'BrazilManausCity',
+    'BrazilMacapaCity',
+    'BrazilSalvadorCity',
+    'BrazilFortalezaCity',
+    'BrazilGoianiaCity',
+    'BrazilBeloHorizonteCity',
+    'BrazilCampoGrandeCity',
+    'BrazilCuiabaCity',
+    'BrazilBelemCity',
+    'BrazilJoaoPessoaCity',
+    'BrazilRecifeCity',
+    'BrazilTeresinaCity',
+    'BrazilCuritibaCity',
+    'BrazilNatalCity',
+    'BrazilPortoVelhoCity',
+    'BrazilBoaVistaCity',
+    'BrazilPortoAlegreCity',
+    'BrazilChapecoCity',
+    'BrazilFlorianopolisCity',
+    'BrazilJoinvilleCity',
+    'BrazilAracajuCity',
+    'BrazilSorocabaCity',
+    'BrazilPalmasCity',
     # Canada
     'Canada',
     'Ontario',
diff --git a/workalendar/america/brazil.py b/workalendar/america/brazil.py
index 6f63387..e178380 100644
--- a/workalendar/america/brazil.py
+++ b/workalendar/america/brazil.py
@@ -29,12 +29,13 @@ class Brazil(WesternCalendar, ChristianMixin):
     include_servidor_publico = False
     servidor_publico_label = "Dia do Servidor Público"
     # Consciência Negra day
-    include_consciencia_negra = False
+    include_consciencia_negra = True
     # There are two dates for the Consciência Negra day
     # The most common is November, 20th
     consciencia_negra_day = (11, 20)
     consciencia_negra_label = "Consciência Negra"
     include_nossa_senhora_conceicao = False
+    include_easter_sunday = True
 
     def get_carnaval(self, year):
         """
@@ -74,6 +75,7 @@ class BrazilAcre(Brazil):
         (6, 15, "Aniversário do Acre"),
         (9, 5, "Dia da Amazônia"),
         (11, 17, "Assinatura do Tratado de Petrópolis"),
+        (8, 6, "Início da Revolução Acreana"),
     )
 
 
@@ -94,6 +96,7 @@ class BrazilAmapa(Brazil):
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (7, 25, "São Tiago"),
         (10, 5, "Criação do estado"),
+        (9, 13, "Aniversário da Amapá"),
     )
     include_sao_jose = True
     sao_jose_label = "Dia de São José"
@@ -123,6 +126,7 @@ class BrazilCeara(Brazil):
     "Brazil Ceará State"
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (3, 23, "Data Manga do Ceará"),
+        (3, 25, "Aniversário do Ceará"),
     )
     include_sao_jose = True
 
@@ -157,6 +161,14 @@ class BrazilMaranhao(Brazil):
     include_nossa_senhora_conceicao = True
 
 
+@iso_register('BR-MG')
+class BrazilMinasGerais(Brazil):
+    "Brasil Minas Gerais State"
+    FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
+        (4, 21, "Aniversário de Minas Gerais"),
+    )
+
+
 @iso_register('BR-MT')
 class BrazilMatoGrosso(Brazil):
     "Brazil Mato Grosso State"
@@ -178,6 +190,7 @@ class BrazilPara(Brazil):
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (8, 15, "Adesão do Grão-Pará á independência do Brasil"),
     )
+    include_nossa_senhora_conceicao = True
 
 
 @iso_register('BR-PB')
@@ -185,12 +198,16 @@ class BrazilParaiba(Brazil):
     "Brazil Paraíba State"
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (8, 5, "Fundação do Estado"),
+        (7, 26, "Homenagem à memória do ex-presidente João Pessoa"),
     )
 
 
 @iso_register('BR-PE')
 class BrazilPernambuco(Brazil):
     "Brazil Pernambuco State"
+    FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
+        (6, 3, "Revolução Pernambucana de 1817"),
+    )
     include_sao_joao = True
 
 
@@ -203,11 +220,20 @@ class BrazilPiaui(Brazil):
     )
 
 
+@iso_register('BR-PR')
+class BrazilParana(Brazil):
+    "Brazil Paraná State"
+    FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
+        (12, 19, "Aniversário do Paraná"),
+    )
+
+
 @iso_register('BR-RJ')
 class BrazilRioDeJaneiro(Brazil):
     "Brazil Rio de Janeiro State"
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (4, 23, "Dia de São Jorge"),
+        (3, 1, "Aniversário da Cidade do Rio de Janeiro"),
     )
     include_servidor_publico = True
     servidor_publico_label = "Dia do Funcionário Público"
@@ -270,6 +296,7 @@ class BrazilSantaCatarina(Brazil):
     "Brazil Santa Catarina State"
     FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
         (8, 11, "Criação da capitania, separando-se de SP"),
+        (11, 25, "Dia de Santa Catarina de Alexandria"),
     )
 
 
@@ -314,6 +341,7 @@ class BrazilTocantins(Brazil):
         (1, 1, "Instalação de Tocantins"),
         (9, 8, "Nossa Senhora da Natividade"),
         (10, 5, "Criação de Tocantins"),
+        (3, 18, "Autonomia do estado de Tocantins"),
     )
 
 
@@ -322,6 +350,7 @@ class BrazilVitoriaCity(BrazilEspiritoSanto):
     FIXED_HOLIDAYS = BrazilEspiritoSanto.FIXED_HOLIDAYS + (
         (4, 24, "Nossa Senhora da Penha"),  # Our Lady of Pain?
         (9, 8, "Nossa Senhora da Vitória"),  # Our Lady of Vitória
+        (9, 8, "Aniversário de Vitória"),
     )
     include_corpus_christi = True
     include_good_friday = True
@@ -378,6 +407,181 @@ class BrazilSerraCity(BrazilEspiritoSanto):
         return days
 
 
+class BrazilRioBrancoCity(BrazilAcre):
+    "Brazil Rio Branco City"
+    FIXED_HOLIDAYS = BrazilAcre.FIXED_HOLIDAYS + (
+        (12, 28, "Aniversário de Rio Branco"),
+    )
+
+
+class BrazilMaceioCity(BrazilAlagoas):
+    "Brazil Maceió City"
+    FIXED_HOLIDAYS = BrazilAlagoas.FIXED_HOLIDAYS + (
+        (12, 5, "Aniversário de Maceió"),
+    )
+
+
+class BrazilManausCity(BrazilAmazonas):
+    "Brazil Manaus City"
+    FIXED_HOLIDAYS = BrazilAmazonas.FIXED_HOLIDAYS + (
+        (10, 24, "Aniversário de Manaus"),
+    )
+
+
+class BrazilMacapaCity(BrazilAmapa):
+    "Brazil Macapá City"
+    FIXED_HOLIDAYS = BrazilAmapa.FIXED_HOLIDAYS + (
+        (2, 4, "Aniversário de Macapá"),
+    )
+
+
+class BrazilSalvadorCity(BrazilBahia):
+    "Brazil Salvador City"
+    FIXED_HOLIDAYS = BrazilBahia.FIXED_HOLIDAYS + (
+        (3, 29, "Aniversário de Salvador"),
+    )
+
+
+class BrazilFortalezaCity(BrazilCeara):
+    "Brazil Fortaleza City"
+    FIXED_HOLIDAYS = BrazilCeara.FIXED_HOLIDAYS + (
+        (4, 13, "Aniversário de Fortaleza"),
+    )
+
+
+class BrazilGoianiaCity(BrazilGoias):
+    "Brazil Goiânia City"
+    FIXED_HOLIDAYS = BrazilGoias.FIXED_HOLIDAYS + (
+        (10, 24, "Aniversário de Goiânia"),
+    )
+
+
+class BrazilBeloHorizonteCity(BrazilMinasGerais):
+    "Brazil Belo Horizonte City"
+    FIXED_HOLIDAYS = BrazilMinasGerais.FIXED_HOLIDAYS + (
+        (12, 12, "Aniversário de Belo Horizonte"),
+    )
+
+
+class BrazilCampoGrandeCity(BrazilMatoGrossoDoSul):
+    "Brazil Campo Grande City"
+    FIXED_HOLIDAYS = BrazilMatoGrossoDoSul.FIXED_HOLIDAYS + (
+        (8, 26, "Aniversário de Campo Grande"),
+    )
+
+
+class BrazilCuiabaCity(BrazilMatoGrosso):
+    "Brazil Cuiabá City"
+    FIXED_HOLIDAYS = BrazilMatoGrosso.FIXED_HOLIDAYS + (
+        (4, 8, "Aniversário de Cuiabá"),
+    )
+
+
+class BrazilBelemCity(BrazilPara):
+    "Brazil Belém City"
+    FIXED_HOLIDAYS = BrazilPara.FIXED_HOLIDAYS + (
+        (1, 12, "Aniversário de Belém"),
+    )
+
+
+class BrazilJoaoPessoaCity(BrazilParaiba):
+    "Brazil João Pessoa City"
+    FIXED_HOLIDAYS = BrazilParaiba.FIXED_HOLIDAYS + (
+        (8, 5, "Aniversário de João Pessoa"),
+    )
+
+
+class BrazilRecifeCity(BrazilPernambuco):
+    "Brazil Recife City"
+    FIXED_HOLIDAYS = BrazilPernambuco.FIXED_HOLIDAYS + (
+        (3, 12, "Aniversário de Recife"),
+    )
+
+
+class BrazilTeresinaCity(BrazilPiaui):
+    "Brazil Teresina City"
+    FIXED_HOLIDAYS = BrazilPiaui.FIXED_HOLIDAYS + (
+        (8, 16, "Aniversário de Teresina"),
+    )
+
+
+class BrazilCuritibaCity(BrazilParana):
+    "Brazil Curitiba City"
+    FIXED_HOLIDAYS = BrazilParana.FIXED_HOLIDAYS + (
+        (3, 29, "Aniversário de Curitiba"),
+    )
+
+
+class BrazilNatalCity(BrazilRioGrandeDoNorte):
+    "Brazil Natal City"
+    FIXED_HOLIDAYS = BrazilRioGrandeDoNorte.FIXED_HOLIDAYS + (
+        (12, 25, "Aniversário de Natal"),
+    )
+
+
+class BrazilPortoVelhoCity(BrazilRondonia):
+    "Brazil Porto Velho City"
+    FIXED_HOLIDAYS = BrazilRondonia.FIXED_HOLIDAYS + (
+        (10, 2, "Aniversário de Porto Velho"),
+    )
+
+
+class BrazilBoaVistaCity(BrazilRoraima):
+    "Brazil Boa Vista City"
+    FIXED_HOLIDAYS = BrazilRoraima.FIXED_HOLIDAYS + (
+        (6, 9, "Aniversário de Boa Vista"),
+    )
+
+
+class BrazilPortoAlegreCity(BrazilRioGrandeDoSul):
+    "Brazil Porto Alegre City"
+    FIXED_HOLIDAYS = BrazilRioGrandeDoSul.FIXED_HOLIDAYS + (
+        (3, 26, "Aniversário de Porto Alegre"),
+    )
+
+
+class BrazilChapecoCity(BrazilSantaCatarina):
+    "Brazil Chapecó City"
+    FIXED_HOLIDAYS = BrazilSantaCatarina.FIXED_HOLIDAYS + (
+        (8, 25, "Aniversário de Chapecó"),
+    )
+
+
+class BrazilFlorianopolisCity(BrazilSantaCatarina):
+    "Brazil Florianópolis City"
+    FIXED_HOLIDAYS = BrazilSantaCatarina.FIXED_HOLIDAYS + (
+        (3, 23, "Aniversário de Florianópolis"),
+    )
+
+
+class BrazilJoinvilleCity(BrazilSantaCatarina):
+    "Brazil Joinville City"
+    FIXED_HOLIDAYS = BrazilSantaCatarina.FIXED_HOLIDAYS + (
+        (3, 9, "Aniversário de Joinville"),
+    )
+
+
+class BrazilAracajuCity(BrazilSergipe):
+    "Brazil Aracaju City"
+    FIXED_HOLIDAYS = BrazilSergipe.FIXED_HOLIDAYS + (
+        (3, 17, "Aniversário de Aracaju"),
+    )
+
+
+class BrazilSorocabaCity(BrazilSaoPauloState):
+    "Brazil Sorocaba City"
+    FIXED_HOLIDAYS = BrazilSaoPauloState.FIXED_HOLIDAYS + (
+        (8, 15, "Aniversário de Sorocaba"),
+    )
+
+
+class BrazilPalmasCity(BrazilTocantins):
+    "Brazil Palmas City"
+    FIXED_HOLIDAYS = BrazilTocantins.FIXED_HOLIDAYS + (
+        (5, 20, "Aniversário de Palmas"),
+    )
+
+
 class BrazilBankCalendar(Brazil):
     """
     Calendar that considers only working days for bank transactions
@@ -411,12 +615,14 @@ class BrazilBankCalendar(Brazil):
         monday_carnaval = tuesday_carnaval - timedelta(days=1)
         good_friday = self.get_good_friday(year)
         corpus_christi = self.get_corpus_christi(year)
+        ash_wednesday = self.get_ash_wednesday(year)
 
         non_fixed_holidays = [
             (monday_carnaval, "Monday carnaval"),
             (tuesday_carnaval, "Tuesday carnaval"),
             (good_friday, "Good friday"),
-            (corpus_christi, "Corpus Christi")
+            (corpus_christi, "Corpus Christi"),
+            (ash_wednesday, "Ash Wednesday"),
         ]
 
         non_working_days = [
@@ -437,3 +643,66 @@ class BrazilBankCalendar(Brazil):
         while not self.is_working_day(day):
             day = day + timedelta(days=1)
         return day
+
+
+IBGE_TUPLE = (
+    ('BR-IBGE-12', BrazilAcre),
+    ('BR-IBGE-27', BrazilAlagoas),
+    ('BR-IBGE-16', BrazilAmapa),
+    ('BR-IBGE-13', BrazilAmazonas),
+    ('BR-IBGE-29', BrazilBahia),
+    ('BR-IBGE-23', BrazilCeara),
+    ('BR-IBGE-53', BrazilDistritoFederal),
+    ('BR-IBGE-32', BrazilEspiritoSanto),
+    ('BR-IBGE-52', BrazilGoias),
+    ('BR-IBGE-21', BrazilMaranhao),
+    ('BR-IBGE-31', BrazilMinasGerais),
+    ('BR-IBGE-51', BrazilMatoGrosso),
+    ('BR-IBGE-50', BrazilMatoGrossoDoSul),
+    ('BR-IBGE-15', BrazilPara),
+    ('BR-IBGE-25', BrazilParaiba),
+    ('BR-IBGE-26', BrazilPernambuco),
+    ('BR-IBGE-22', BrazilPiaui),
+    ('BR-IBGE-41', BrazilParana),
+    ('BR-IBGE-33', BrazilRioDeJaneiro),
+    ('BR-IBGE-24', BrazilRioGrandeDoNorte),
+    ('BR-IBGE-43', BrazilRioGrandeDoSul),
+    ('BR-IBGE-11', BrazilRondonia),
+    ('BR-IBGE-14', BrazilRoraima),
+    ('BR-IBGE-42', BrazilSantaCatarina),
+    ('BR-IBGE-35', BrazilSaoPauloState),
+    ('BR-IBGE-3550308', BrazilSaoPauloCity),
+    ('BR-IBGE-28', BrazilSergipe),
+    ('BR-IBGE-17', BrazilTocantins),
+    ('BR-IBGE-3205309', BrazilVitoriaCity),
+    ('BR-IBGE-3205200', BrazilVilaVelhaCity),
+    ('BR-IBGE-3201308', BrazilCariacicaCity),
+    ('BR-IBGE-3202405', BrazilGuarapariCity),
+    ('BR-IBGE-3205002', BrazilSerraCity),
+    ('BR-IBGE-1200401', BrazilRioBrancoCity),
+    ('BR-IBGE-2704302', BrazilMaceioCity),
+    ('BR-IBGE-1302603', BrazilManausCity),
+    ('BR-IBGE-1600303', BrazilMacapaCity),
+    ('BR-IBGE-2927408', BrazilSalvadorCity),
+    ('BR-IBGE-2304400', BrazilFortalezaCity),
+    ('BR-IBGE-5208707', BrazilGoianiaCity),
+    ('BR-IBGE-3106200', BrazilBeloHorizonteCity),
+    ('BR-IBGE-5002704', BrazilCampoGrandeCity),
+    ('BR-IBGE-5103403', BrazilCuiabaCity),
+    ('BR-IBGE-1501402', BrazilBelemCity),
+    ('BR-IBGE-2507507', BrazilJoaoPessoaCity),
+    ('BR-IBGE-2611606', BrazilRecifeCity),
+    ('BR-IBGE-2211001', BrazilTeresinaCity),
+    ('BR-IBGE-4106902', BrazilCuritibaCity),
+    ('BR-IBGE-2408102', BrazilNatalCity),
+    ('BR-IBGE-1100205', BrazilPortoVelhoCity),
+    ('BR-IBGE-1400100', BrazilBoaVistaCity),
+    ('BR-IBGE-4314902', BrazilPortoAlegreCity),
+    ('BR-IBGE-4204202', BrazilChapecoCity),
+    ('BR-IBGE-4205407', BrazilFlorianopolisCity),
+    ('BR-IBGE-4209102', BrazilJoinvilleCity),
+    ('BR-IBGE-2800308', BrazilAracajuCity),
+    ('BR-IBGE-3552205', BrazilSorocabaCity),
+    ('BR-IBGE-1721000', BrazilPalmasCity),
+)
+IBGE_REGISTER = dict(IBGE_TUPLE)


Test patch:
diff --git a/workalendar/tests/test_brazil.py b/workalendar/tests/test_brazil.py
index 49879c6..ed6b20f 100644
--- a/workalendar/tests/test_brazil.py
+++ b/workalendar/tests/test_brazil.py
@@ -3,21 +3,33 @@ from __future__ import (absolute_import, division, print_function,
                         unicode_literals)
 
 from datetime import date
+from unittest import TestCase
+
 from workalendar.tests import GenericCalendarTest
 from workalendar.america import (
     Brazil, BrazilSaoPauloState,
     BrazilAcre, BrazilAlagoas, BrazilAmapa, BrazilAmazonas, BrazilBahia,
     BrazilCeara, BrazilDistritoFederal, BrazilEspiritoSanto, BrazilGoias,
-    BrazilMaranhao, BrazilMatoGrosso, BrazilMatoGrossoDoSul, BrazilPara,
-    BrazilParaiba, BrazilPernambuco, BrazilPiaui, BrazilRioDeJaneiro,
-    BrazilRioGrandeDoNorte, BrazilRioGrandeDoSul, BrazilRondonia,
-    BrazilRoraima, BrazilSantaCatarina, BrazilSergipe, BrazilTocantins,
+    BrazilMaranhao, BrazilMinasGerais, BrazilMatoGrosso, BrazilMatoGrossoDoSul,
+    BrazilPara, BrazilParaiba, BrazilPernambuco, BrazilPiaui, BrazilParana,
+    BrazilRioDeJaneiro, BrazilRioGrandeDoNorte, BrazilRioGrandeDoSul,
+    BrazilRondonia, BrazilRoraima, BrazilSantaCatarina, BrazilSergipe,
+    BrazilTocantins,
     # Cities
     BrazilSaoPauloCity, BrazilVitoriaCity, BrazilVilaVelhaCity,
     BrazilCariacicaCity, BrazilGuarapariCity, BrazilSerraCity,
+    BrazilRioBrancoCity, BrazilMaceioCity, BrazilManausCity, BrazilMacapaCity,
+    BrazilSalvadorCity, BrazilFortalezaCity, BrazilGoianiaCity,
+    BrazilBeloHorizonteCity, BrazilCampoGrandeCity, BrazilCuiabaCity,
+    BrazilBelemCity, BrazilJoaoPessoaCity, BrazilRecifeCity,
+    BrazilTeresinaCity, BrazilCuritibaCity, BrazilNatalCity,
+    BrazilPortoVelhoCity, BrazilBoaVistaCity, BrazilPortoAlegreCity,
+    BrazilChapecoCity, BrazilFlorianopolisCity, BrazilJoinvilleCity,
+    BrazilAracajuCity, BrazilSorocabaCity, BrazilPalmasCity,
     # Banks
-    BrazilBankCalendar
+    BrazilBankCalendar,
 )
+from workalendar.america.brazil import IBGE_REGISTER, IBGE_TUPLE
 
 
 class BrazilTest(GenericCalendarTest):
@@ -45,6 +57,8 @@ class BrazilAcreTest(BrazilTest):
         self.assertIn(date(2017, 9, 5), holidays)  # Dia da Amazônia
         # Assinatura do Tratado de Petrópolis
         self.assertIn(date(2017, 11, 17), holidays)
+        # Início da Revolução Acreana
+        self.assertIn(date(2017, 8, 6), holidays)
 
 
 class BrazilAlagoasTest(BrazilTest):
@@ -74,6 +88,7 @@ class BrazilAmapaTest(BrazilTest):
         self.assertIn(date(2017, 7, 25), holidays)  # São Tiago
         self.assertIn(date(2017, 10, 5), holidays)  # Criação do estado
         self.assertIn(date(2017, 11, 20), holidays)  # Consciência Negra
+        self.assertIn(date(2017, 9, 13), holidays)  # Aniversário da Amapá
 
 
 class BrazilAmazonasTest(BrazilTest):
@@ -103,6 +118,7 @@ class BrazilCearaTest(BrazilTest):
         holidays = self.cal.holidays_set(2017)
         self.assertIn(date(2017, 3, 19), holidays)  # São José
         self.assertIn(date(2017, 3, 23), holidays)  # Data Manga do Ceará
+        self.assertIn(date(2017, 3, 25), holidays)  # Aniversário do Ceará
 
 
 class BrazilDistritoFederalTest(BrazilTest):
@@ -141,6 +157,15 @@ class BrazilMaranhaoTest(BrazilTest):
         self.assertIn(date(2017, 12, 8), holidays)
 
 
+class BrazilMinasGeraisTest(BrazilTest):
+    cal_class = BrazilMinasGerais
+
+    def test_year_2017_state(self):
+        holidays = self.cal.holidays_set(2017)
+        # Aniversário de Minas Geraisl
+        self.assertIn(date(2017, 4, 21), holidays)
+
+
 class BrazilMatoGrossoTest(BrazilTest):
     cal_class = BrazilMatoGrosso
 
@@ -172,6 +197,8 @@ class BrazilParaibaTest(BrazilTest):
     def test_year_2017_state(self):
         holidays = self.cal.holidays_set(2017)
         self.assertIn(date(2017, 8, 5), holidays)  # Fundação do Estado
+        # Homenagem à memória do ex-presidente João Pessoa
+        self.assertIn(date(2017, 7, 26), holidays)
 
 
 class BrazilPernambucoTest(BrazilTest):
@@ -192,6 +219,15 @@ class BrazilPiauiTest(BrazilTest):
         self.assertIn(date(2017, 10, 19), holidays)  # Dia do Piauí
 
 
+class BrazilParanaTest(BrazilTest):
+    cal_class = BrazilParana
+
+    def test_year_2017_state(self):
+        holidays = self.cal.holidays_set(2017)
+        # Aniversário do Paraná
+        self.assertIn(date(2017, 12, 19), holidays)
+
+
 class BrazilRioDeJaneiroTest(BrazilTest):
     cal_class = BrazilRioDeJaneiro
 
@@ -263,6 +299,8 @@ class BrazilSantaCatarinaTest(BrazilTest):
         holidays = self.cal.holidays_set(2017)
         # Criação da capitania, separando-se de SP
         self.assertIn(date(2017, 8, 11), holidays)
+        # Dia de Santa Catarina de Alexandria
+        self.assertIn(date(2017, 11, 25), holidays)
 
 
 class SaoPauloStateTest(BrazilTest):
@@ -272,6 +310,8 @@ class SaoPauloStateTest(BrazilTest):
         holidays = self.cal.holidays_set(2013)
         # Revolução Constitucionalista de 1932
         self.assertIn(date(2013, 7, 9), holidays)
+        # Revolução Constitucionalista de 1932
+        self.assertIn(date(2013, 7, 9), holidays)
 
 
 class SaoPauloCityTest(SaoPauloStateTest):
@@ -321,6 +361,8 @@ class BrazilTocantinsTest(BrazilTest):
         # Nossa Senhora da Natividade
         self.assertIn(date(2017, 9, 8), holidays)
         self.assertIn(date(2017, 10, 5), holidays)  # Criação de Tocantins
+        # Autonomia do estado de Tocantins
+        self.assertIn(date(2017, 3, 18), holidays)
 
 
 class BrazilVitoriaCityTest(BrazilEspiritoSantoTest):
@@ -444,6 +486,331 @@ class BrazilSerraCityTest(BrazilEspiritoSantoTest):
         )
 
 
+class BrazilRioBrancoCityTest(BrazilAcreTest):
+    """
+    Rio Branco is in the Acre state
+    """
+    cal_class = BrazilRioBrancoCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Rio Branco
+        self.assertIn(date(2017, 12, 28), holidays)
+
+
+class BrazilMaceioCityTest(BrazilAlagoasTest):
+    """
+    Maceió is in the Alagoas state
+    """
+    cal_class = BrazilMaceioCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Maceió
+        self.assertIn(date(2017, 12, 5), holidays)
+
+
+class BrazilManausCityTest(BrazilAmazonasTest):
+    """
+    Manaus is in the Amazonas state
+    """
+    cal_class = BrazilManausCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Manaus
+        self.assertIn(date(2017, 10, 24), holidays)
+
+
+class BrazilMacapaCityTest(BrazilAmapaTest):
+    """
+    Macapá is in the Amapá state
+    """
+    cal_class = BrazilMacapaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Macapá
+        self.assertIn(date(2017, 2, 4), holidays)
+
+
+class BrazilSalvadorCityTest(BrazilBahiaTest):
+    """
+    Salvador is in the Bahia state
+    """
+    cal_class = BrazilSalvadorCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Salvador
+        self.assertIn(date(2017, 3, 29), holidays)
+
+
+class BrazilFortalezaCityTest(BrazilCearaTest):
+    """
+    Fortaleza is in the Ceará state
+    """
+    cal_class = BrazilFortalezaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Fortaleza
+        self.assertIn(date(2017, 4, 13), holidays)
+
+
+class BrazilGoianiaCityTest(BrazilGoiasTest):
+    """
+    Goiânia is in the Goiás state
+    """
+    cal_class = BrazilGoianiaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Goiânia
+        self.assertIn(date(2017, 10, 24), holidays)
+
+
+class BrazilBeloHorizonteCityTest(BrazilMinasGeraisTest):
+    """
+    Belo Horizonte is in the Minas Gerais state
+    """
+    cal_class = BrazilBeloHorizonteCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Belo Horizonte
+        self.assertIn(date(2017, 12, 12), holidays)
+
+
+class BrazilCampoGrandeCityTest(BrazilMatoGrossoDoSulTest):
+    """
+    Campo Grande is in the Mato Grosso do Sul state
+    """
+    cal_class = BrazilCampoGrandeCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Campo Grande
+        self.assertIn(date(2017, 8, 26), holidays)
+
+
+class BrazilCuiabaCityTest(BrazilMatoGrossoTest):
+    """
+    Cuiabá is in the Mato Grosso state
+    """
+    cal_class = BrazilCuiabaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Cuiabá
+        self.assertIn(date(2017, 4, 8), holidays)
+
+
+class BrazilBelemCityTest(BrazilParaTest):
+    """
+    Belém is in the Pará state
+    """
+    cal_class = BrazilBelemCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Belém
+        self.assertIn(date(2017, 1, 12), holidays)
+
+
+class BrazilJoaoPessoaCityTest(BrazilParaibaTest):
+    """
+    João Pessoa is in the Paraíba state
+    """
+    cal_class = BrazilJoaoPessoaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de João Pessoa
+        self.assertIn(date(2017, 8, 5), holidays)
+
+
+class BrazilRecifeCityTest(BrazilPernambucoTest):
+    """
+    Recife is in the Pernambuco state
+    """
+    cal_class = BrazilRecifeCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Recife
+        self.assertIn(date(2017, 3, 12), holidays)
+
+
+class BrazilTeresinaCityTest(BrazilPiauiTest):
+    """
+    Teresina is in the Piauí state
+    """
+    cal_class = BrazilTeresinaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Teresina
+        self.assertIn(date(2017, 8, 16), holidays)
+
+
+class BrazilCuritibaCityTest(BrazilParanaTest):
+    """
+    Curitiba is in the Paraná state
+    """
+    cal_class = BrazilCuritibaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Curitiba
+        self.assertIn(date(2017, 3, 29), holidays)
+
+
+class BrazilNatalCityTest(BrazilRioGrandeDoNorteTest):
+    """
+    Natal is in the Rio Grande do Norte state
+    """
+    cal_class = BrazilNatalCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Natal
+        self.assertIn(date(2017, 12, 25), holidays)
+
+
+class BrazilPortoVelhoCityTest(BrazilRondoniaTest):
+    """
+    Porto Velho is in the Rio Grande do Norte state
+    """
+    cal_class = BrazilPortoVelhoCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Porto Velho
+        self.assertIn(date(2017, 10, 2), holidays)
+
+
+class BrazilBoaVistaCityTest(BrazilRoraimaTest):
+    """
+    Boa Vista is in the Roraima state
+    """
+    cal_class = BrazilBoaVistaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Boa Vista
+        self.assertIn(date(2017, 6, 9), holidays)
+
+
+class BrazilPortoAlegreCityTest(BrazilRioGrandeDoSulTest):
+    """
+    Porto Alegre is in the Rio Grande do Sul state
+    """
+    cal_class = BrazilPortoAlegreCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Porto Alegre
+        self.assertIn(date(2017, 3, 26), holidays)
+
+
+class BrazilChapecoCityTest(BrazilSantaCatarinaTest):
+    """
+    Chapecó is in the Santa Catarina state
+    """
+    cal_class = BrazilChapecoCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Chapecó
+        self.assertIn(date(2017, 8, 25), holidays)
+
+
+class BrazilFlorianopolisCityTest(BrazilSantaCatarinaTest):
+    """
+    Florianópolis is in the Santa Catarina state
+    """
+    cal_class = BrazilFlorianopolisCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Florianópolis
+        self.assertIn(date(2017, 3, 23), holidays)
+
+
+class BrazilJoinvilleCityTest(BrazilSantaCatarinaTest):
+    """
+    Joinville is in the Santa Catarina state
+    """
+    cal_class = BrazilJoinvilleCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Joinville
+        self.assertIn(date(2017, 3, 9), holidays)
+
+
+class BrazilAracajuCityTest(BrazilSergipeTest):
+    """
+    Aracajú is in the Sergipe state
+    """
+    cal_class = BrazilAracajuCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Aracajú
+        self.assertIn(date(2017, 3, 17), holidays)
+
+
+class BrazilSorocabaCityTest(SaoPauloStateTest):
+    """
+    Sorocaba is in the São Paulo state
+    """
+    cal_class = BrazilSorocabaCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Sorocaba
+        self.assertIn(date(2017, 8, 15), holidays)
+
+
+class BrazilPalmasCityTest(BrazilTocantinsTest):
+    """
+    Palmas is in the Tocantins state
+    """
+    cal_class = BrazilPalmasCity
+
+    def test_year_2017_city(self):
+        holidays = self.cal.holidays_set(2017)
+        # Fixed days
+        # Aniversário de Palmas
+        self.assertIn(date(2017, 5, 20), holidays)
+
+
 class BrazilBankCalendarTest(BrazilTest):
     cal_class = BrazilBankCalendar
 
@@ -470,12 +837,12 @@ class BrazilBankCalendarTest(BrazilTest):
     def test_year_2017_find_next_working_day_for_monday_carnaval(self):
         monday_carnaval = date(2017, 2, 27)
         working_day = self.cal.find_following_working_day(monday_carnaval)
-        self.assertEquals(working_day, date(2017, 3, 1))
+        self.assertEquals(working_day, date(2017, 3, 2))
 
     def test_year_2017_find_next_working_day_for_tuesday_carnaval(self):
         tuesday_carnaval = date(2017, 2, 28)
         working_day = self.cal.find_following_working_day(tuesday_carnaval)
-        self.assertEquals(working_day, date(2017, 3, 1))
+        self.assertEquals(working_day, date(2017, 3, 2))
 
     def test_year_2017_find_next_working_day_for_good_friday(self):
         good_friday = date(2017, 4, 14)
@@ -532,3 +899,20 @@ class BrazilBankCalendarTest(BrazilTest):
         already_working_day = date(2017, 7, 25)
         working_day = self.cal.find_following_working_day(already_working_day)
         self.assertEquals(working_day, date(2017, 7, 25))
+
+
+class TestIBGERegister(TestCase):
+
+    def test_register_length(self):
+        # Each time another calendar will be added, this length should increase
+        # This also fails when a key appears twice (typo mistake?)
+        self.assertEqual(len(IBGE_REGISTER), len(IBGE_TUPLE))
+
+    def test_no_duplicate(self):
+        # Check if a class doesn't appear twice with different keys
+        values = set(IBGE_REGISTER.values())
+        self.assertEqual(len(IBGE_REGISTER.values()), len(values))
+
+    def test_all_are_brazilian_classes(self):
+        for key, value in IBGE_TUPLE:
+            self.assertTrue(issubclass(value, Brazil))