Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adicionar método is_holiday para verificar feriados nacionais e estaduais #446

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Poetry Setup
uses: snok/install-poetry@v1
- name: Install Dependencies
run: poetry install --no-root --with test
- name: Generate Report
run: |
pip install coverage
coverage run -m unittest discover tests/
poetry run coverage run -m unittest discover -s tests
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v4
with:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Utilitário `convert_code_to_uf` [#397](https://github.com/brazilian-utils/brutils-python/pull/410)
- Utilitário `is_holiday` [#446](https://github.com/brazilian-utils/brutils-python/pull/446)

## [2.2.0] - 2024-09-12

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ emulate bash -c '. .../bin/activate'
Para testar se o ambiente virtual está ativo corretamente, execute o comando e verifique se a resposta é algo parecido com a seguinte:

```sh
$ poetry env inf
$ poetry env info
Virtualenv
Python: 3.x.y
Implementation: CPython
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ False
- [generate_voter_id](#generate_voter_id)
- [IBGE](#ibge)
- [convert_code_to_uf](#convert_code_to_uf)
- [Feriados](#feriados)
- [is_holiday](#is_holiday)

## CPF

Expand Down Expand Up @@ -1109,6 +1111,38 @@ Exemplo:
>>>
```

## Feriados

### is_holiday

Verifica se uma determinada data é um feriado nacional ou estadual no Brasil.

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.

Argumentos:

- `date (datetime)`: A data a ser verificada.
- `uf (str, opcional)`: A abreviação do estado (UF) para verificar feriados estaduais. Se não fornecido, apenas feriados nacionais são considerados.

Retorna:

- `bool | None`: `True` se a data for um feriado, `False` se não for, ou `None` se a data ou UF forem inválidas.

Exemplo:

```python
>>> from datetime import datetime
>>> from brutils import is_holiday

>>> is_holiday(datetime(2024, 1, 1))
True
>>> is_holiday(datetime(2024, 1, 2))
False
>>> is_holiday(datetime(2024, 3, 2), uf="SP")
False
>>> is_holiday(datetime(2024, 12, 25), uf="RJ")
True
```

# Novos Utilitários e Reportar Bugs

Expand Down
34 changes: 34 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ False
- [generate_voter_id](#generate_voter_id)
- [IBGE](#ibge)
- [convert_code_to_uf](#convert_code_to_uf)
- [Holidays](#holidays)
- [is_holiday](#is_holiday)

## CPF

Expand Down Expand Up @@ -1112,6 +1114,38 @@ Exemplo:
>>>
```

## Holidays
### is_holiday

Checks if a given date is a national or state holiday in Brazil.

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.

Args:

- `date (datetime)`: The date to be checked.
- `uf (str, optional)`: The state abbreviation (UF) to check for state holidays. If not provided, only national holidays are considered.

Returns:

- `bool | None`: `True` if the date is a holiday, `False` if it’s not, or `None` if the date or UF are invalid.

Example:

```python
>>> from datetime import datetime
>>> from brutils import is_holiday

>>> is_holiday(datetime(2024, 1, 1))
True
>>> is_holiday(datetime(2024, 1, 2))
False
>>> is_holiday(datetime(2024, 3, 2), uf="SP")
False
>>> is_holiday(datetime(2024, 12, 25), uf="RJ")
True
```

# Feature Request and Bug Report

If you want to suggest new features or report bugs, simply create
Expand Down
5 changes: 5 additions & 0 deletions brutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
remove_symbols as remove_symbols_cpf,
)

# Date Utils Import
from brutils.date_utils import is_holiday

# Email Import
from brutils.email import is_valid as is_valid_email

Expand Down Expand Up @@ -172,4 +175,6 @@
"is_valid_voter_id",
# IBGE
"convert_code_to_uf",
# Date Utils
"is_holiday",
]
61 changes: 61 additions & 0 deletions brutils/date_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from datetime import datetime
from typing import Union

import holidays


def is_holiday(target_date: datetime, uf: str = None) -> Union[bool, None]:
"""
Checks if the given date is a national or state holiday in Brazil.

This function takes a date as a `datetime` object and an optional UF (Unidade Federativa),
returning a boolean value indicating whether the date is a holiday or `None` if the date or
UF are invalid.

The method does not handle municipal holidays.

Args:
target_date (datetime): The date to be checked.
uf (str, optional): The state abbreviation (UF) to check for state holidays.
If not provided, only national holidays will be considered.

Returns:
bool | None: Returns `True` if the date is a holiday, `False` if it is not,
or `None` if the date or UF are invalid.

Note:
The function logic should be implemented using the `holidays` library.
For more information, refer to the documentation at: https://pypi.org/project/holidays/

Usage Examples:
>>> from datetime import datetime
>>> is_holiday(datetime(2024, 1, 1))
True

>>> is_holiday(datetime(2024, 1, 2))
False

>>> is_holiday(datetime(2024, 3, 2), uf="SP")
False

>>> is_holiday(datetime(2024, 12, 25), uf="RJ")
True
"""

if not isinstance(target_date, datetime):
return None

valid_ufs = holidays.Brazil().subdivisions
if uf is not None and uf not in valid_ufs:
return None

national_holidays = holidays.Brazil(years=target_date.year)

if target_date in national_holidays:
return True

if uf is not None:
state_holidays = holidays.Brazil(prov=uf, years=target_date.year)
return target_date in state_holidays

return False
41 changes: 40 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.8.1"
holidays = "^0.58"

[tool.poetry.group.test.dependencies]
coverage = "^7.2.7"
Expand Down
Empty file added tests/__init__.py
Empty file.
75 changes: 75 additions & 0 deletions tests/test_date_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from datetime import datetime
from unittest import TestCase

from brutils.date_utils import is_holiday


class TestIsHoliday(TestCase):
def test_feriados_validos(self):
# Testes com feriados válidos
self.assertTrue(is_holiday(datetime(2024, 1, 1))) # Ano Novo
self.assertTrue(
is_holiday(datetime(2024, 7, 9), uf="SP")
) # Revolução Constitucionalista (SP)
self.assertTrue(
is_holiday(datetime(2024, 9, 7))
) # Independência do Brasil
self.assertTrue(is_holiday(datetime(2025, 1, 1))) # Ano Novo

def test_dias_normais(self):
# Testes com dias normais
self.assertFalse(is_holiday(datetime(2024, 1, 2))) # Dia normal
self.assertFalse(
is_holiday(datetime(2024, 7, 9), uf="RJ")
) # Dia normal no RJ

def test_data_invalida(self):
# Testes com data inválida
self.assertIsNone(is_holiday("2024-01-01")) # Formato incorreto
self.assertIsNone(is_holiday(None)) # Data None

def test_uf_invalida(self):
# Testes com UF inválida
self.assertIsNone(
is_holiday(datetime(2024, 1, 1), uf="XX")
) # UF inválida
self.assertIsNone(
is_holiday(datetime(2024, 1, 1), uf="SS")
) # UF inválida

def test_limite_de_datas(self):
# Testes com limite de datas
self.assertTrue(is_holiday(datetime(2024, 12, 25))) # Natal
self.assertTrue(
is_holiday(datetime(2024, 11, 15))
) # Proclamação da República

def test_datas_depois_de_feriados(self):
# Test data after holidays
self.assertFalse(is_holiday(datetime(2024, 12, 26))) # Não é feriado
self.assertFalse(is_holiday(datetime(2025, 1, 2))) # Não é feriado

def test_ano_bissexto(self):
# Teste ano bissexto
self.assertFalse(
is_holiday(datetime(2024, 2, 29))
) # Não é feriado, mas data válida
# Uncomment to test non-leap year invalid date
# self.assertIsNone(is_holiday(datetime(1900, 2, 29))) # Ano não bissexto, data inválida

def test_data_passada_futura(self):
# Teste de data passada e futura
self.assertTrue(is_holiday(datetime(2023, 1, 1))) # Ano anterior
self.assertTrue(is_holiday(datetime(2150, 12, 25))) # Ano futuro
self.assertFalse(
is_holiday(datetime(2250, 1, 2))
) # Dia normal em ano futuro

def test_data_sem_uf(self):
# Teste feriado nacional sem UF
self.assertTrue(
is_holiday(datetime(2024, 12, 25))
) # Natal, feriado nacional
self.assertFalse(
is_holiday(datetime(2024, 7, 9))
) # Data estadual de SP, sem UF
Loading