Skip to content

Commit 78aa0a5

Browse files
Adicionar método is_holiday para verificar feriados nacionais e estaduais (#446)
* Adicitonando is_holiday * Adicitonando is_holiday * Adicionando is_holidays * Atualizando Changelog * Lib Holidays adicionada * Adicionando instalacao de dependencias - run-tests.yml * Mudanca de nome do arquivo date_utils Para evitar conflito com a nomenclatura date de python * Removendo duplicada is_holiday no __init__ * Removendo duplicada is_holiday no __init__ * Movendo import holidays para dentro da funcao * Movendo import holidays para dentro da funcao * Adicionando __init__ na pasta de testes * Mudando o comando de rodar os testes na action * Adicionando working_directory a action run-tests.yml * Adicionando poetry run no generate report * Removendo mudancas desnecessarias Que foram feitas para debug * Atualizando o poetry lock * Arrumando run-tests.yml * Segunda tentativa run-tests.yml * Terceira tentativa * Update .github/workflows/run-tests.yml * Update .github/workflows/run-tests.yml * Update .github/workflows/run-tests.yml * regerando poetry.lock * Update brutils/date_utils.py * Update brutils/date_utils.py --------- Co-authored-by: Camila Maia <cmaiacd@gmail.com>
1 parent 9d8c388 commit 78aa0a5

10 files changed

Lines changed: 276 additions & 22 deletions

File tree

.github/workflows/run-tests.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@ jobs:
1616
uses: actions/setup-python@v5
1717
with:
1818
python-version: ${{ matrix.python-version }}
19+
- name: Poetry Setup
20+
uses: snok/install-poetry@v1
21+
with:
22+
version: 1.8.4
23+
- name: Install Dependencies
24+
run: poetry install
1925
- name: Generate Report
2026
run: |
21-
pip install coverage num2words
22-
coverage run -m unittest discover tests/
27+
poetry run coverage run -m unittest discover -s tests
2328
- name: Upload Coverage to Codecov
2429
uses: codecov/codecov-action@v5
2530
with:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### Added
1010

1111
- Utilitário `convert_code_to_uf` [#397](https://github.com/brazilian-utils/brutils-python/pull/410)
12+
- Utilitário `is_holiday` [#446](https://github.com/brazilian-utils/brutils-python/pull/446)
1213
- Utilitário `convert_date_to_text`[#394](https://github.com/brazilian-utils/brutils-python/pull/415)
1314
- Utilitário `get_municipality_by_code` [412](https://github.com/brazilian-utils/brutils-python/pull/412)
1415
- Utilitário `get_code_by_municipality_name` [#399](https://github.com/brazilian-utils/brutils-python/issues/399)

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ False
9393
- [get_code_by_municipality_name](#get_code_by_municipality_name)
9494
- [convert_code_to_uf](#convert_code_to_uf)
9595
- [get\_municipality\_by\_code](#get_municipality_by_code)
96+
- [Feriados](#feriados)
97+
- [is_holiday](#is_holiday)
9698
- [Monetário](#monetário)
9799
- [format\_currency](#format_currency)
98100

@@ -1193,6 +1195,39 @@ None
11931195
None
11941196
```
11951197

1198+
## Feriados
1199+
1200+
### is_holiday
1201+
1202+
Verifica se uma determinada data é um feriado nacional ou estadual no Brasil.
1203+
1204+
Esta função recebe um objeto `datetime` como a data e uma UF opcional (Unidade Federativa) para especificar feriados estaduais. Retorna `True` se a data for um feriado, `False` se não for, ou `None` se a data ou UF forem inválidas. Nota: a função não abrange feriados municipais.
1205+
1206+
Argumentos:
1207+
1208+
- `date (datetime)`: A data a ser verificada.
1209+
- `uf (str, opcional)`: A abreviação do estado (UF) para verificar feriados estaduais. Se não fornecido, apenas feriados nacionais são considerados.
1210+
1211+
Retorna:
1212+
1213+
- `bool | None`: `True` se a data for um feriado, `False` se não for, ou `None` se a data ou UF forem inválidas.
1214+
1215+
Exemplo:
1216+
1217+
```python
1218+
>>> from datetime import datetime
1219+
>>> from brutils import is_holiday
1220+
1221+
>>> is_holiday(datetime(2024, 1, 1))
1222+
True
1223+
>>> is_holiday(datetime(2024, 1, 2))
1224+
False
1225+
>>> is_holiday(datetime(2024, 3, 2), uf="SP")
1226+
False
1227+
>>> is_holiday(datetime(2024, 12, 25), uf="RJ")
1228+
True
1229+
```
1230+
11961231
## Monetário
11971232

11981233
### format_currency

README_EN.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ False
9393
- [convert_code_to_uf](#convert_code_to_uf)
9494
- [get\_municipality\_by\_code](#get_municipality_by_code)
9595
- [get_code_by_municipality_name](#get_code_by_municipality_name)
96+
- [Holidays](#holidays)
97+
- [is_holiday](#is_holiday)
9698
- [Monetary](#monetary)
9799
- [format_currency](#format_currency)
98100

@@ -1197,6 +1199,38 @@ None
11971199
None
11981200
```
11991201

1202+
## Holidays
1203+
1204+
### is_holiday
1205+
1206+
Checks if a given date is a national or state holiday in Brazil.
1207+
1208+
This function takes a `datetime` object as the date and an optional state abbreviation (UF) to specify state holidays. It returns `True` if the date is a holiday, `False` if it’s not, or `None` if the date or UF are invalid. Note that the function does not cover municipal holidays.
1209+
1210+
Args:
1211+
1212+
- `date (datetime)`: The date to be checked.
1213+
- `uf (str, optional)`: The state abbreviation (UF) to check for state holidays. If not provided, only national holidays are considered.
1214+
1215+
Returns:
1216+
1217+
- `bool | None`: `True` if the date is a holiday, `False` if it’s not, or `None` if the date or UF are invalid.
1218+
1219+
Example:
1220+
1221+
```python
1222+
>>> from datetime import datetime
1223+
>>> from brutils import is_holiday
1224+
>>> is_holiday(datetime(2024, 1, 1))
1225+
True
1226+
>>> is_holiday(datetime(2024, 1, 2))
1227+
False
1228+
>>> is_holiday(datetime(2024, 3, 2), uf="SP")
1229+
False
1230+
>>> is_holiday(datetime(2024, 12, 25), uf="RJ")
1231+
True
1232+
```
1233+
12001234
## Monetary
12011235

12021236
### format_currency

brutils/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
# Date imports
2727
from brutils.date import convert_date_to_text
2828

29+
# Date Utils Import
30+
from brutils.date_utils import is_holiday
31+
2932
# Email Import
3033
from brutils.email import is_valid as is_valid_email
3134

@@ -126,6 +129,8 @@
126129
"convert_code_to_uf",
127130
"get_municipality_by_code",
128131
"get_code_by_municipality_name",
132+
# Date Utils
133+
"is_holiday",
129134
# Currency
130135
"format_currency",
131136
]

brutils/date_utils.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from datetime import datetime
2+
from typing import Union
3+
4+
import holidays
5+
6+
7+
def is_holiday(target_date: datetime, uf: str = None) -> Union[bool, None]:
8+
"""
9+
Checks if the given date is a national or state holiday in Brazil.
10+
11+
This function takes a date as a `datetime` object and an optional UF (Unidade Federativa),
12+
returning a boolean value indicating whether the date is a holiday or `None` if the date or
13+
UF are invalid.
14+
15+
The method does not handle municipal holidays.
16+
17+
Args:
18+
target_date (datetime): The date to be checked.
19+
uf (str, optional): The state abbreviation (UF) to check for state holidays.
20+
If not provided, only national holidays will be considered.
21+
22+
Returns:
23+
bool | None: Returns `True` if the date is a holiday, `False` if it is not,
24+
or `None` if the date or UF are invalid.
25+
26+
Note:
27+
The function logic should be implemented using the `holidays` library.
28+
For more information, refer to the documentation at: https://pypi.org/project/holidays/
29+
30+
Usage Examples:
31+
>>> from datetime import datetime
32+
>>> is_holiday(datetime(2024, 1, 1))
33+
True
34+
35+
>>> is_holiday(datetime(2024, 1, 2))
36+
False
37+
38+
>>> is_holiday(datetime(2024, 3, 2), uf="SP")
39+
False
40+
41+
>>> is_holiday(datetime(2024, 12, 25), uf="RJ")
42+
True
43+
"""
44+
45+
if not isinstance(target_date, datetime):
46+
return None
47+
48+
valid_ufs = holidays.Brazil().subdivisions
49+
if uf is not None and uf not in valid_ufs:
50+
return None
51+
52+
national_holidays = holidays.Brazil(years=target_date.year)
53+
54+
if uf is None:
55+
return target_date in national_holidays
56+
57+
state_holidays = holidays.Brazil(prov=uf, years=target_date.year)
58+
return target_date in state_holidays

poetry.lock

Lines changed: 59 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ classifiers = [
2727

2828
[tool.poetry.dependencies]
2929
python = "^3.8.1"
30+
holidays = "^0.58"
3031
num2words = "0.5.13"
32+
coverage = "^7.2.7"
3133

3234
[tool.poetry.group.test.dependencies]
3335
coverage = "^7.2.7"

tests/__init__.py

Whitespace-only changes.

tests/test_date_utils.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from datetime import datetime
2+
from unittest import TestCase
3+
4+
from brutils.date_utils import is_holiday
5+
6+
7+
class TestIsHoliday(TestCase):
8+
def test_feriados_validos(self):
9+
# Testes com feriados válidos
10+
self.assertTrue(is_holiday(datetime(2024, 1, 1))) # Ano Novo
11+
self.assertTrue(
12+
is_holiday(datetime(2024, 7, 9), uf="SP")
13+
) # Revolução Constitucionalista (SP)
14+
self.assertTrue(
15+
is_holiday(datetime(2024, 9, 7))
16+
) # Independência do Brasil
17+
self.assertTrue(is_holiday(datetime(2025, 1, 1))) # Ano Novo
18+
19+
def test_dias_normais(self):
20+
# Testes com dias normais
21+
self.assertFalse(is_holiday(datetime(2024, 1, 2))) # Dia normal
22+
self.assertFalse(
23+
is_holiday(datetime(2024, 7, 9), uf="RJ")
24+
) # Dia normal no RJ
25+
26+
def test_data_invalida(self):
27+
# Testes com data inválida
28+
self.assertIsNone(is_holiday("2024-01-01")) # Formato incorreto
29+
self.assertIsNone(is_holiday(None)) # Data None
30+
31+
def test_uf_invalida(self):
32+
# Testes com UF inválida
33+
self.assertIsNone(
34+
is_holiday(datetime(2024, 1, 1), uf="XX")
35+
) # UF inválida
36+
self.assertIsNone(
37+
is_holiday(datetime(2024, 1, 1), uf="SS")
38+
) # UF inválida
39+
40+
def test_limite_de_datas(self):
41+
# Testes com limite de datas
42+
self.assertTrue(is_holiday(datetime(2024, 12, 25))) # Natal
43+
self.assertTrue(
44+
is_holiday(datetime(2024, 11, 15))
45+
) # Proclamação da República
46+
47+
def test_datas_depois_de_feriados(self):
48+
# Test data after holidays
49+
self.assertFalse(is_holiday(datetime(2024, 12, 26))) # Não é feriado
50+
self.assertFalse(is_holiday(datetime(2025, 1, 2))) # Não é feriado
51+
52+
def test_ano_bissexto(self):
53+
# Teste ano bissexto
54+
self.assertFalse(
55+
is_holiday(datetime(2024, 2, 29))
56+
) # Não é feriado, mas data válida
57+
# Uncomment to test non-leap year invalid date
58+
# self.assertIsNone(is_holiday(datetime(1900, 2, 29))) # Ano não bissexto, data inválida
59+
60+
def test_data_passada_futura(self):
61+
# Teste de data passada e futura
62+
self.assertTrue(is_holiday(datetime(2023, 1, 1))) # Ano anterior
63+
self.assertTrue(is_holiday(datetime(2150, 12, 25))) # Ano futuro
64+
self.assertFalse(
65+
is_holiday(datetime(2250, 1, 2))
66+
) # Dia normal em ano futuro
67+
68+
def test_data_sem_uf(self):
69+
# Teste feriado nacional sem UF
70+
self.assertTrue(
71+
is_holiday(datetime(2024, 12, 25))
72+
) # Natal, feriado nacional
73+
self.assertFalse(
74+
is_holiday(datetime(2024, 7, 9))
75+
) # Data estadual de SP, sem UF

0 commit comments

Comments
 (0)