Skip to content

Commit

Permalink
Merge pull request #238 from fvlima/brazil-bank-calendar
Browse files Browse the repository at this point in the history
Brazil bank calendar
  • Loading branch information
brunobord committed Jul 27, 2017
2 parents 88570bb + 1efab32 commit 36427bf
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Expand Up @@ -7,6 +7,7 @@ master (unreleased)
- Added Hong Kong, by @nedlowe (#235).
- Splitted `africa.py` file into an `africa/` module (#236).
- Added Alabama Counties - Baldwin County, Mobile County, Perry County. Refactored UnitedStates classes to have a parameter to include the "Mardi Gras" day (#214).
- Added brazilian calendar to consider working days for bank transactions, by @fvlima (#238).

2.0.0 (2017-06-23)
------------------
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Expand Up @@ -111,7 +111,7 @@ Europe
America
-------

* Brazil (all states and cities, except the city of Viana)
* Brazil (all states, cities and for bank transactions, except the city of Viana)
* Chile
* Colombia
* Mexico
Expand Down
3 changes: 2 additions & 1 deletion workalendar/america/__init__.py
Expand Up @@ -11,7 +11,7 @@
BrazilRondonia, BrazilRoraima, BrazilSantaCatarina, BrazilSaoPauloState,
BrazilSaoPauloCity, BrazilSergipe, BrazilTocantins, BrazilVitoriaCity,
BrazilVilaVelhaCity, BrazilCariacicaCity, BrazilGuarapariCity,
BrazilSerraCity,
BrazilSerraCity, BrazilBankCalendar
)
from .chile import Chile
from .colombia import Colombia
Expand Down Expand Up @@ -52,6 +52,7 @@
BrazilCariacicaCity,
BrazilGuarapariCity,
BrazilSerraCity,
BrazilBankCalendar,
Chile,
Colombia,
Mexico,
Expand Down
63 changes: 62 additions & 1 deletion workalendar/america/brazil.py
Expand Up @@ -5,7 +5,7 @@
from datetime import timedelta, date

from workalendar.core import WesternCalendar, ChristianMixin
from workalendar.core import MON
from workalendar.core import MON, SAT, SUN


class Brazil(WesternCalendar, ChristianMixin):
Expand Down Expand Up @@ -349,3 +349,64 @@ def get_variable_days(self, year):
days.append((carnaval_tuesday - timedelta(days=1), "Carnaval Monday"))
days.append((carnaval_tuesday, "Carnaval"))
return days


class BrazilBankCalendar(Brazil):
"""
Calendar that considers only working days for bank transactions
for companies and the general public
"""
FIXED_HOLIDAYS = Brazil.FIXED_HOLIDAYS + (
(12, 25, "Christmas Day"),
)

def get_last_day_of_year_for_only_internal_bank_trans(self, year):
"""
The last day of year isn't a working day for public bank
transactions in Brazil. More details can be read in
http://www.bcb.gov.br/pre/bc_atende/port/servicos4.asp
"""
last_day = date(year, 12, 31)

if last_day.weekday() == SAT:
return last_day - timedelta(days=1)
elif last_day.weekday() == SUN:
return last_day - timedelta(days=2)

return last_day

def get_variable_days(self, year):
"""
Define the brazilian variable holidays and the last
day for only internal bank transactions
"""
tuesday_carnaval = self.get_carnaval(year)
monday_carnaval = tuesday_carnaval - timedelta(days=1)
good_friday = self.get_good_friday(year)
corpus_christi = self.get_corpus_christi(year)

non_fixed_holidays = [
(monday_carnaval, "Monday carnaval"),
(tuesday_carnaval, "Tuesday carnaval"),
(good_friday, "Good friday"),
(corpus_christi, "Corpus Christi")
]

non_working_days = [
(
self.get_last_day_of_year_for_only_internal_bank_trans(year),
"Last day of year for only internal bank transactions"
)
]

return non_fixed_holidays + non_working_days

def find_following_working_day(self, day):
"""
Find for the next working day by ignoring weekends,
fixed and non fixed holidays and the last working
day for only internal bank transactions in Brazil
"""
while not self.is_working_day(day):
day = day + timedelta(days=1)
return day
92 changes: 92 additions & 0 deletions workalendar/tests/test_brazil.py
Expand Up @@ -15,6 +15,8 @@
# Cities
BrazilSaoPauloCity, BrazilVitoriaCity, BrazilVilaVelhaCity,
BrazilCariacicaCity, BrazilGuarapariCity, BrazilSerraCity,
# Banks
BrazilBankCalendar
)


Expand Down Expand Up @@ -440,3 +442,93 @@ def test_year_2017_city(self):
self.cal.get_holiday_label(good_friday),
"Paixão do Cristo",
)


class BrazilBankCalendarTest(BrazilTest):
cal_class = BrazilBankCalendar

def test_year_2017_holidays(self):
holidays = self.cal.holidays_set(2017)
self.assertIn(date(2017, 1, 1), holidays) # New year
self.assertIn(date(2017, 2, 27), holidays) # Monday carnaval
self.assertIn(date(2017, 2, 28), holidays) # Tuesday carnaval
self.assertIn(date(2017, 4, 14), holidays) # Good friday
self.assertIn(date(2017, 4, 21), holidays) # Tiradentes
self.assertIn(date(2017, 5, 1), holidays) # Labour day
self.assertIn(date(2017, 6, 15), holidays) # Corpus Christi
self.assertIn(date(2017, 9, 7), holidays) # Independence day
self.assertIn(date(2017, 10, 12), holidays) # Our Lady Aparecida
self.assertIn(date(2017, 11, 2), holidays) # All Souls' Day
self.assertIn(date(2017, 11, 15), holidays) # Republic day
self.assertIn(date(2017, 12, 25), holidays) # Christmas Day

def test_year_2017_find_next_working_day_for_new_year(self):
new_year = date(2017, 1, 1)
working_day = self.cal.find_following_working_day(new_year)
self.assertEquals(working_day, date(2017, 1, 2))

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))

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))

def test_year_2017_find_next_working_day_for_good_friday(self):
good_friday = date(2017, 4, 14)
working_day = self.cal.find_following_working_day(good_friday)
self.assertEquals(working_day, date(2017, 4, 17))

def test_year_2017_find_next_working_day_for_tiradentes(self):
tiradentes = date(2017, 4, 21)
working_day = self.cal.find_following_working_day(tiradentes)
self.assertEquals(working_day, date(2017, 4, 24))

def test_year_2017_find_next_working_day_for_labour_day(self):
labour_day = date(2017, 5, 1)
working_day = self.cal.find_following_working_day(labour_day)
self.assertEquals(working_day, date(2017, 5, 2))

def test_year_2017_find_next_working_day_for_corpus_christi(self):
corpus_christi = date(2017, 6, 15)
working_day = self.cal.find_following_working_day(corpus_christi)
self.assertEquals(working_day, date(2017, 6, 16))

def test_year_2017_find_next_working_day_for_independence_day(self):
independence_day = date(2017, 9, 7)
working_day = self.cal.find_following_working_day(independence_day)
self.assertEquals(working_day, date(2017, 9, 8))

def test_year_2017_find_next_working_day_for_our_lady_aparecida(self):
our_lady_aparecida = date(2017, 10, 12)
working_day = self.cal.find_following_working_day(our_lady_aparecida)
self.assertEquals(working_day, date(2017, 10, 13))

def test_year_2017_find_next_working_day_for_our_all_souls(self):
all_souls = date(2017, 11, 2)
working_day = self.cal.find_following_working_day(all_souls)
self.assertEquals(working_day, date(2017, 11, 3))

def test_year_2017_find_next_working_day_for_our_republic_day(self):
republic_day = date(2017, 11, 15)
working_day = self.cal.find_following_working_day(republic_day)
self.assertEquals(working_day, date(2017, 11, 16))

def test_year_2017_find_next_working_day_for_our_christmas_day(self):
christmas_day = date(2017, 12, 25)
working_day = self.cal.find_following_working_day(christmas_day)
self.assertEquals(working_day, date(2017, 12, 26))

def test_year_2017_find_next_working_day_for_last_day(self):
# last day of year for only internal bank transactions
last_day = date(2017, 12, 29)
working_day = self.cal.find_following_working_day(last_day)
self.assertEquals(working_day, date(2018, 1, 2))

def test_year_2017_find_next_working_day_for_already_working_day(self):
already_working_day = date(2017, 7, 25)
working_day = self.cal.find_following_working_day(already_working_day)
self.assertEquals(working_day, date(2017, 7, 25))

0 comments on commit 36427bf

Please sign in to comment.