Skip to content

Commit

Permalink
Adicionar método is_holiday para verificar feriados nacionais e estad…
Browse files Browse the repository at this point in the history
…uais (#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 <[email protected]>
  • Loading branch information
laistdomiciano and camilamaia authored Jan 17, 2025
1 parent 9d8c388 commit 78aa0a5
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 22 deletions.
9 changes: 7 additions & 2 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Poetry Setup
uses: snok/install-poetry@v1
with:
version: 1.8.4
- name: Install Dependencies
run: poetry install
- name: Generate Report
run: |
pip install coverage num2words
coverage run -m unittest discover tests/
poetry run coverage run -m unittest discover -s tests
- name: Upload Coverage to Codecov
uses: codecov/codecov-action@v5
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)
- Utilitário `convert_date_to_text`[#394](https://github.com/brazilian-utils/brutils-python/pull/415)
- Utilitário `get_municipality_by_code` [412](https://github.com/brazilian-utils/brutils-python/pull/412)
- Utilitário `get_code_by_municipality_name` [#399](https://github.com/brazilian-utils/brutils-python/issues/399)
Expand Down
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ False
- [get_code_by_municipality_name](#get_code_by_municipality_name)
- [convert_code_to_uf](#convert_code_to_uf)
- [get\_municipality\_by\_code](#get_municipality_by_code)
- [Feriados](#feriados)
- [is_holiday](#is_holiday)
- [Monetário](#monetário)
- [format\_currency](#format_currency)

Expand Down Expand Up @@ -1193,6 +1195,39 @@ None
None
```

## 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
```

## Monetário

### format_currency
Expand Down
34 changes: 34 additions & 0 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ False
- [convert_code_to_uf](#convert_code_to_uf)
- [get\_municipality\_by\_code](#get_municipality_by_code)
- [get_code_by_municipality_name](#get_code_by_municipality_name)
- [Holidays](#holidays)
- [is_holiday](#is_holiday)
- [Monetary](#monetary)
- [format_currency](#format_currency)

Expand Down Expand Up @@ -1197,6 +1199,38 @@ None
None
```

## 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
```

## Monetary

### format_currency
Expand Down
5 changes: 5 additions & 0 deletions brutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
# Date imports
from brutils.date import convert_date_to_text

# 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 @@ -126,6 +129,8 @@
"convert_code_to_uf",
"get_municipality_by_code",
"get_code_by_municipality_name",
# Date Utils
"is_holiday",
# Currency
"format_currency",
]
58 changes: 58 additions & 0 deletions brutils/date_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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 uf is None:
return target_date in national_holidays

state_holidays = holidays.Brazil(prov=uf, years=target_date.year)
return target_date in state_holidays
79 changes: 59 additions & 20 deletions poetry.lock

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

2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ classifiers = [

[tool.poetry.dependencies]
python = "^3.8.1"
holidays = "^0.58"
num2words = "0.5.13"
coverage = "^7.2.7"

[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

0 comments on commit 78aa0a5

Please sign in to comment.