Dateutil¶
O dateutil é um pacote que "fornece extensões poderosas para o módulo datetime padrão, disponível em Python". Ele aborda questões muito mais gerais, para lidar com datas no python, e não é apenas um módulo para tratar de feriados.
No site Labix.org, tem vários exemplos interessante sobre o uso de Recurrence Rules, uma função que pode ser usada para feriados.
from datetime import date, datetime, timedelta
from dateutil import parser, rrule
import brazilian_holidays
Recurrence Rules¶
O módulo rrules permite criar uma série de regras de horários recorrentes. Abaixo um exemlo de uma regra.
list(
rrule.rrule(
rrule.DAILY,
byweekday=[rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR],
count=10,
dtstart=parser.parse('19970902T090000'),
)
)
[datetime.datetime(1997, 9, 2, 9, 0), datetime.datetime(1997, 9, 3, 9, 0), datetime.datetime(1997, 9, 4, 9, 0), datetime.datetime(1997, 9, 5, 9, 0), datetime.datetime(1997, 9, 8, 9, 0), datetime.datetime(1997, 9, 9, 9, 0), datetime.datetime(1997, 9, 10, 9, 0), datetime.datetime(1997, 9, 11, 9, 0), datetime.datetime(1997, 9, 12, 9, 0), datetime.datetime(1997, 9, 15, 9, 0)]
Exemplo com regras de minutos.
list(
rrule.rrule(
rrule.MINUTELY,
byhour=range(9, 17),
byminute=(0, 30),
# interval=30,
byweekday=[rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR],
count=20,
dtstart=date.today(),
)
)
[datetime.datetime(2025, 10, 16, 9, 0), datetime.datetime(2025, 10, 16, 9, 30), datetime.datetime(2025, 10, 16, 10, 0), datetime.datetime(2025, 10, 16, 10, 30), datetime.datetime(2025, 10, 16, 11, 0), datetime.datetime(2025, 10, 16, 11, 30), datetime.datetime(2025, 10, 16, 12, 0), datetime.datetime(2025, 10, 16, 12, 30), datetime.datetime(2025, 10, 16, 13, 0), datetime.datetime(2025, 10, 16, 13, 30), datetime.datetime(2025, 10, 16, 14, 0), datetime.datetime(2025, 10, 16, 14, 30), datetime.datetime(2025, 10, 16, 15, 0), datetime.datetime(2025, 10, 16, 15, 30), datetime.datetime(2025, 10, 16, 16, 0), datetime.datetime(2025, 10, 16, 16, 30), datetime.datetime(2025, 10, 17, 9, 0), datetime.datetime(2025, 10, 17, 9, 30), datetime.datetime(2025, 10, 17, 10, 0), datetime.datetime(2025, 10, 17, 10, 30)]
Feriados nos EUA¶
Encontrei no repositório adamJLev/holidays.py, uma função que cria os feriados dos Estados Unidos utilizando rrules.
def get_schedule_holidays_rrules():
return [
# New Years
rrule.rrule(
rrule.YEARLY, dtstart=datetime.now(), bymonth=1, bymonthday=1
),
# Memorial
rrule.rrule(
rrule.YEARLY,
dtstart=datetime.now(),
bymonth=5,
byweekday=rrule.MO(-1),
),
# Independence
rrule.rrule(
rrule.YEARLY, dtstart=datetime.now(), bymonth=7, bymonthday=4
),
# Thanksgiving
rrule.rrule(
rrule.YEARLY,
dtstart=datetime.now(),
bymonth=11,
byweekday=rrule.TH(4),
),
# Christmas
rrule.rrule(
rrule.YEARLY, dtstart=datetime.now(), bymonth=12, bymonthday=25
),
]
get_schedule_holidays_rrules()
[<dateutil.rrule.rrule at 0x1faff9ed8d0>, <dateutil.rrule.rrule at 0x1faff9ee590>, <dateutil.rrule.rrule at 0x1faff9ee710>, <dateutil.rrule.rrule at 0x1faff9ec190>, <dateutil.rrule.rrule at 0x1faff9ee910>]
Pensava que poderia utilizar isso também para feriados brasileiros, porém ao me deparar com feriados de data móvel (páscoa e carnaval, por exemplo), desisti de utilizar o rrules para feriados. De qualquer forma, o pacote pode agregar muito nas análises de séries temporais, pela exclusão de feriados de intervalos sub-diários.
Exclusão de Feriados¶
No artigo StackOverFlow: Datetime Python - Next Business Day descobri que dá pra definir feriados!!! E usar isso na biblioteca dateutil para excluir das regras!!!
Excluindo datas das rrule¶
Defini regras do rrule que eu desejaria obter intervalores de 30 minutos, entre as 9h e as 17h, e desejaria excluir algumas datas (ou "feriados") específicas.
# Feriados
holidays = [
datetime(2024, 3, 26, 11, 0, 0),
datetime(2024, 3, 26, 11, 30, 0),
]
# Create a rule to recur every weekday starting today
r = rrule.rrule(
rrule.MINUTELY,
byhour=range(9, 17),
byminute=(0, 30),
byweekday=[rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR],
count=40,
# dtstart=date.today(),
dtstart=date(2024, 3, 24),
)
# Create a rruleset
rs = rrule.rruleset()
# Attach our rrule to it
rs.rrule(r)
# Add holidays as exclusion days
for holiday in holidays:
rs.exdate(holiday)
# Results
list_intervals = list(rs)
list_intervals
[datetime.datetime(2024, 3, 25, 9, 0), datetime.datetime(2024, 3, 25, 9, 30), datetime.datetime(2024, 3, 25, 10, 0), datetime.datetime(2024, 3, 25, 10, 30), datetime.datetime(2024, 3, 25, 11, 0), datetime.datetime(2024, 3, 25, 11, 30), datetime.datetime(2024, 3, 25, 12, 0), datetime.datetime(2024, 3, 25, 12, 30), datetime.datetime(2024, 3, 25, 13, 0), datetime.datetime(2024, 3, 25, 13, 30), datetime.datetime(2024, 3, 25, 14, 0), datetime.datetime(2024, 3, 25, 14, 30), datetime.datetime(2024, 3, 25, 15, 0), datetime.datetime(2024, 3, 25, 15, 30), datetime.datetime(2024, 3, 25, 16, 0), datetime.datetime(2024, 3, 25, 16, 30), datetime.datetime(2024, 3, 26, 9, 0), datetime.datetime(2024, 3, 26, 9, 30), datetime.datetime(2024, 3, 26, 10, 0), datetime.datetime(2024, 3, 26, 10, 30), datetime.datetime(2024, 3, 26, 12, 0), datetime.datetime(2024, 3, 26, 12, 30), datetime.datetime(2024, 3, 26, 13, 0), datetime.datetime(2024, 3, 26, 13, 30), datetime.datetime(2024, 3, 26, 14, 0), datetime.datetime(2024, 3, 26, 14, 30), datetime.datetime(2024, 3, 26, 15, 0), datetime.datetime(2024, 3, 26, 15, 30), datetime.datetime(2024, 3, 26, 16, 0), datetime.datetime(2024, 3, 26, 16, 30), datetime.datetime(2024, 3, 27, 9, 0), datetime.datetime(2024, 3, 27, 9, 30), datetime.datetime(2024, 3, 27, 10, 0), datetime.datetime(2024, 3, 27, 10, 30), datetime.datetime(2024, 3, 27, 11, 0), datetime.datetime(2024, 3, 27, 11, 30), datetime.datetime(2024, 3, 27, 12, 0), datetime.datetime(2024, 3, 27, 12, 30)]
Logo pensei: posso usar o brazilian-holidays para criar uma lista de feriados!!!
Contudo, infelizmente só dá certo se tiver também o horário definido.
Excluindo Feriados Brasileiros de Séries Temporais¶
Pensava que, caso eu criasse uma lista de feriados, em formato datetime, eu conseguiria excluir os feriados de uma regra definida com o módulo dateutil.rrule.
Para isso criei a lista de feriados, utilizando o brazilian_holidays.
Pensei em outra coisa
holidays = brazilian_holidays.Holidays(year=2024)
holidays.add_all()
holidays = holidays.create_list(tipo='date')
Inicialmente criamos todos os horários possíveis com o intervalo pretendido, para todos os feriados. Essa será a nossa tabela de exclusão.
list_holidays_hours = []
for holiday in holidays:
# Create a rule to recur every weekday starting today
r = rrule.rrule(
rrule.MINUTELY,
byhour=range(9, 17),
byminute=(0, 30),
byweekday=[rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR],
dtstart=holiday,
until=holiday + timedelta(days=1), # Tomorrow,
)
# Create a rruleset
rs = rrule.rruleset()
# Attach our rrule to it
rs.rrule(r)
# Results
list_intervals = list(rs)
list_holidays_hours.extend(list_intervals)
# Results
list_holidays_hours[:5]
[datetime.datetime(2024, 1, 1, 9, 0), datetime.datetime(2024, 1, 1, 9, 30), datetime.datetime(2024, 1, 1, 10, 0), datetime.datetime(2024, 1, 1, 10, 30), datetime.datetime(2024, 1, 1, 11, 0)]
Após isso recriados a regra, com a exclusão dos feridos.
# Create a rule to recur every weekday starting today
r = rrule.rrule(
rrule.MINUTELY,
byhour=range(9, 17),
byminute=(0, 30),
byweekday=[rrule.MO, rrule.TU, rrule.WE, rrule.TH, rrule.FR],
# count=50,
dtstart=date(2024, 3, 27),
until=date(2024, 4, 2),
)
# Create a rruleset
rs = rrule.rruleset()
# Attach our rrule to it
rs.rrule(r)
# Add holidays as exclusion days
for holiday in list_holidays_hours:
rs.exdate(holiday)
# Results
list_intervals = list(rs)
list_intervals
[datetime.datetime(2024, 3, 27, 9, 0), datetime.datetime(2024, 3, 27, 9, 30), datetime.datetime(2024, 3, 27, 10, 0), datetime.datetime(2024, 3, 27, 10, 30), datetime.datetime(2024, 3, 27, 11, 0), datetime.datetime(2024, 3, 27, 11, 30), datetime.datetime(2024, 3, 27, 12, 0), datetime.datetime(2024, 3, 27, 12, 30), datetime.datetime(2024, 3, 27, 13, 0), datetime.datetime(2024, 3, 27, 13, 30), datetime.datetime(2024, 3, 27, 14, 0), datetime.datetime(2024, 3, 27, 14, 30), datetime.datetime(2024, 3, 27, 15, 0), datetime.datetime(2024, 3, 27, 15, 30), datetime.datetime(2024, 3, 27, 16, 0), datetime.datetime(2024, 3, 27, 16, 30), datetime.datetime(2024, 4, 1, 9, 0), datetime.datetime(2024, 4, 1, 9, 30), datetime.datetime(2024, 4, 1, 10, 0), datetime.datetime(2024, 4, 1, 10, 30), datetime.datetime(2024, 4, 1, 11, 0), datetime.datetime(2024, 4, 1, 11, 30), datetime.datetime(2024, 4, 1, 12, 0), datetime.datetime(2024, 4, 1, 12, 30), datetime.datetime(2024, 4, 1, 13, 0), datetime.datetime(2024, 4, 1, 13, 30), datetime.datetime(2024, 4, 1, 14, 0), datetime.datetime(2024, 4, 1, 14, 30), datetime.datetime(2024, 4, 1, 15, 0), datetime.datetime(2024, 4, 1, 15, 30), datetime.datetime(2024, 4, 1, 16, 0), datetime.datetime(2024, 4, 1, 16, 30)]
Note que o dia 28 e 29 de março de 2024 foram removidos, visto que são feriado definidos no brazilian-holidays.