Guide
Eventos recorrentes de calendário: RRULE explicado
Uma reunião semanal é um evento no banco de dados e cinquenta e dois na tela. Todo sistema de calendário resolve essa diferença com regras de recorrência — strings RRULE da RFC 5545 — e toda integração acaba esbarrando nas mesmas perguntas: o que a sintaxe da regra significa, quem expande a série em instâncias e o que acontece quando uma ocorrência muda de horário. Este guia responde às três, com as particularidades do Google, do Microsoft Graph e do CalDAV lado a lado.
Written by Hazik Director of Product Management
Referências de comandos usadas neste guia: nylas calendar events list e nylas calendar events show.
O que é um RRULE?
Um RRULE é a gramática de regras de recorrência definida na RFC 5545 (a especificação iCalendar) que descreve uma agenda repetitiva como uma única string. Uma regra substitui centenas de linhas armazenadas: o evento existe uma vez, e o software deriva as ocorrências. Os três principais sistemas de calendário armazenam o RRULE diretamente ou traduzem de e para ele em suas fronteiras.
A gramática tem uma parte obrigatória, FREQ, e um conjunto de modificadores. Os 6 que você realmente vai usar estão abaixo — e uma restrição da RFC importa mais do que qualquer um deles: a especificação afirma que as partes UNTIL e COUNT "MUST NOT occur in the same 'recur'". Uma regra termina de exatamente 3 formas: nunca, após N ocorrências ou em uma data.
| Parte | Significado | Exemplo |
|---|---|---|
FREQ | Cadência base (obrigatória) | FREQ=WEEKLY |
INTERVAL | A cada N períodos (padrão 1) | INTERVAL=2 — quinzenal |
BYDAY | Seletor de dia da semana; aceita ordinais | BYDAY=MO,WE,FR ou BYDAY=-1FR (última sexta-feira) |
BYMONTHDAY | Seletor de dia do mês | BYMONTHDAY=15 |
COUNT | Termina após N ocorrências | COUNT=12 |
UNTIL | Termina em um timestamp UTC | UNTIL=20261231T235959Z |
# Every Monday and Wednesday for 12 occurrences
RRULE:FREQ=WEEKLY;BYDAY=MO,WE;COUNT=12
# Biweekly Friday, forever
RRULE:FREQ=WEEKLY;INTERVAL=2;BYDAY=FR
# Last Friday of each month through 2026
RRULE:FREQ=MONTHLY;BYDAY=-1FR;UNTIL=20261231T235959ZQuem expande a série em instâncias?
A expansão, transformar uma regra em ocorrências datadas, acontece em um lugar diferente em cada API, e essa localização decide quanto código de recorrência você escreve. O Google expande no lado do servidor sob demanda, o Graph armazena um objeto estruturado e expõe uma visão de instâncias, e o CalDAV entrega a regra crua e deixa o cálculo para o cliente. Os 3 modelos em resumo:
- Google Calendar API — passe
singleEvents=trueparaevents.liste a resposta contém instâncias individuais dentro da sua janela de tempo, cada uma carregando umrecurringEventId. O guia de eventos recorrentes do Google cobre o modelo de evento principal vs. instância. - Microsoft Graph — os eventos armazenam um objeto patternedRecurrence com um
pattern(cadência) e umrange(limites) em vez de uma string RRULE crua; a expansão de instâncias vem da visãoinstancesdo evento principal da série, delimitada por parâmetros de início e fim. - CalDAV — o servidor retorna o VEVENT com seu RRULE literal, e o cliente o expande, incluindo o cálculo de fuso horário. Acertar isso sem ajuda é o caminho mais difícil; a seção de recorrência da RFC 5545 ocupa dezenas de páginas por um motivo.
A regra prática: se sua integração só precisa saber "o que acontece nesta semana", prefira uma API (ou ferramenta) que expanda por você. Escrever um expansor correto no lado do cliente é um projeto, não uma função. Para entender como essas 3 APIs diferem além da recorrência (autenticação, disponibilidade, webhooks), veja a comparação de APIs de calendário; esta página fica no problema da recorrência.
O que acontece quando uma ocorrência muda ou é cancelada?
Uma ocorrência única modificada vira uma exceção: um registro independente que sobrescreve a regra para uma data enquanto o resto da série segue o evento principal. Cada sistema codifica isso de um jeito — o Google cria um evento separado com um campo originalStartTime, o Graph o modela como um tipo de ocorrência exception, e o iCalendar adiciona um segundo VEVENT com um RECURRENCE-ID correspondente à instância movida.
Exceções são o ponto onde código de sincronização ingênuo quebra. Dois modos de falha respondem pela maioria dos bugs: tratar uma exceção como um evento novo (a reunião aparece duas vezes: uma da expansão da regra, outra da sobrescrita), e aplicar uma edição da série por cima de uma exceção (a troca pontual de sala feita pelo usuário reverte silenciosamente). A lógica de sincronização precisa verificar o marcador de exceção antes de aplicar qualquer um dos caminhos — uma checagem de 2 ramificações muito mais barata do que o ticket de suporte da reunião duplicada.
Como ler eventos recorrentes pela CLI?
O comando nylas calendar events list retorna instâncias expandidas dentro da janela solicitada, então uma série semanal aparece como ocorrências datadas sem nenhum parsing de RRULE do seu lado. Cada instância de uma série recorrente carrega um master_event_id apontando de volta ao evento principal da série, e o próprio ID da instância embute o timestamp da sua ocorrência — ambos visíveis na saída JSON.
# Expanded instances for the next 30 days
nylas calendar events list --days 30 --json | jq '.[] | {
id: .id,
title: .title,
master: .master_event_id,
start: .when.start_time
}'
# Group instances by series to spot every recurring meeting
nylas calendar events list --days 30 --json | jq '
[.[] | select(.master_event_id != null)]
| group_by(.master_event_id)
| map({series: .[0].title, occurrences: length})'Os mesmos 2 comandos funcionam com contas Google, Microsoft e iCloud, o que elimina as diferenças de expansão por API da seção anterior para os caminhos de leitura. Uma limitação honesta: criar eventos com o comando calendar events create da CLI não aceita uma flag de recorrência — a criação de séries continua na interface ou API do seu provedor, e a CLI cuida da parte de leitura, filtragem e scripts.
Por que eventos recorrentes mudam com o horário de verão?
Uma reunião semanal das 9:00 é às 9:00 em um fuso horário nomeado, não em um offset UTC fixo — então duas vezes por ano, seu horário UTC muda em uma hora. Regras expandidas contra UTC cru derivam após cada transição de horário de verão; a expansão correta resolve cada ocorrência pelo banco de dados de fusos horários (America/Toronto, não UTC-5). Essa é boa parte do motivo pelo qual a expansão CalDAV no lado do cliente é difícil, e também está codificada na exigência da RFC 5545 de que UNTIL use timestamps UTC enquanto os inícios de evento carregam referências de fuso horário.
Ao auditar um bug de sincronização que aparece em março ou novembro, verifique qual relógio o expansor usou antes de qualquer outra coisa. O JSON de instância acima inclui start_timezone em eventos com intervalo de tempo, então um filtro jq de 1 linha para instâncias cujo fuso horário difere do esperado encontra rapidamente a série que derivou.
Próximos passos
- APIs de calendário comparadas: Google, Microsoft, CalDAV — diferenças de autenticação, disponibilidade e recorrência em um só lugar
- Sincronizar calendários entre provedores — onde as exceções de recorrência causam mais dano
- Gerencie o Google Calendar pela CLI — o fluxo de eventos específico do Google
- Gerencie o Yahoo Calendar pela CLI — leituras recorrentes nos calendários do Yahoo baseados em CalDAV
- Referência completa de comandos — todas as flags de eventos de calendário documentadas