Expressão cron: como ler, escrever e acertar o fuso horário

Uma expressão cron tem cinco campos: minuto, hora, dia do mês, mês e dia da semana. Este guia explica cada campo, traz os agendamentos mais usados, mostra a regra que faz o cron rodar mais vezes do que o esperado e explica o erro mais comum no Brasil: agendar no horário de Brasília um serviço que roda em UTC.

Por Leonardo Gaertner · Publicado em

Os cinco campos

  • Minuto: 0 a 59.
  • Hora: 0 a 23.
  • Dia do mês: 1 a 31.
  • Mês: 1 a 12 (ou JAN a DEC).
  • Dia da semana: 0 a 6, com 0 no domingo (a maioria das implementações também aceita 7 como domingo, e SUN a SAT).

Em cada campo, * significa "todos", 1,15 é uma lista, 1-5 é um intervalo e */10 é "a cada 10". Para ler uma expressão em português e ver as próximas execuções, cole no interpretador de cron.

Exemplos prontos

  • */5 * * * *: a cada 5 minutos.
  • 0 * * * *: a cada hora, no minuto zero.
  • 0 3 * * *: todo dia às 3h.
  • 30 8 * * 1-5: de segunda a sexta, às 8h30.
  • 0 0 1 * *: à meia-noite do dia 1 de cada mês.
  • 0 */6 * * *: a cada 6 horas (0h, 6h, 12h e 18h).

A pegadinha do dia do mês com o dia da semana

Quando os dois campos de dia estão restritos, o cron padrão roda quando qualquer um deles bate, e não quando os dois batem. 0 9 13 * 5 roda todo dia 13 e também toda sexta-feira, não só na sexta-feira 13. Se um dos dois for *, vale só o outro.

Para "a primeira segunda-feira do mês", o cron sozinho não resolve: agende 0 9 1-7 * * e confira o dia da semana dentro do script.

O fuso horário: Brasília ou UTC?

O cron do Linux usa o fuso do servidor, mas muitos agendadores usam UTC: o GitHub Actions, a maioria dos serviços de nuvem e servidores configurados em UTC. Brasília é UTC-3 o ano todo desde 2019, sem horário de verão, então 0 3 * * * em UTC roda à meia-noite de Brasília.

Para converter, some 3 horas. Das 21h em diante, em UTC já é o dia seguinte, e se a expressão tiver dias da semana eles também andam um dia. A função abaixo faz a conversão para agendamentos diários, com ou sem lista de dias:

JavaScript
// Brasília é UTC-3 o ano todo (não há horário de verão desde 2019).
// diasDaSemana: '*' ou uma lista como '1,3,5' (0 = domingo).
function cronDeBrasilia(minuto, hora, diasDaSemana = '*') {
  const horaUtc = hora + 3;
  let dias = diasDaSemana;
  // Das 21h em diante, em UTC já é o dia seguinte: o dia da semana anda junto
  if (horaUtc >= 24 && dias !== '*') {
    dias = dias.split(',').map((d) => (Number(d) + 1) % 7).join(',');
  }
  return `${minuto} ${horaUtc % 24} * * ${dias}`;
}

Serve para minuto e hora fixos. Faixas como 1-5 precisam virar lista (1,2,3,4,5) antes.

Python
# Brasília é UTC-3 o ano todo (não há horário de verão desde 2019).
# dias_da_semana: "*" ou uma lista como "1,3,5" (0 = domingo).
def cron_de_brasilia(minuto: int, hora: int, dias_da_semana: str = "*") -> str:
    hora_utc = hora + 3
    dias = dias_da_semana
    # Das 21h em diante, em UTC já é o dia seguinte: o dia da semana anda junto
    if hora_utc >= 24 and dias != "*":
        dias = ",".join(str((int(d) + 1) % 7) for d in dias.split(","))
    return f"{minuto} {hora_utc % 24} * * {dias}"
PHP
<?php
// Brasília é UTC-3 o ano todo (não há horário de verão desde 2019).
// $diasDaSemana: '*' ou uma lista como '1,3,5' (0 = domingo).
function cronDeBrasilia(int $minuto, int $hora, string $diasDaSemana = '*'): string
{
    $horaUtc = $hora + 3;
    $dias = $diasDaSemana;
    // Das 21h em diante, em UTC já é o dia seguinte: o dia da semana anda junto
    if ($horaUtc >= 24 && $dias !== '*') {
        $dias = implode(',', array_map(fn($d) => ((int) $d + 1) % 7, explode(',', $dias)));
    }
    return sprintf('%d %d * * %s', $minuto, $horaUtc % 24, $dias);
}

Cuidados

  • Há variações com seis ou sete campos, com segundos no começo (Quartz, Spring) ou ano no fim. Confira quantos campos o seu agendador espera.
  • No GitHub Actions, o menor intervalo é de 5 minutos, e a execução pode atrasar em horários de muita carga. Não conte com precisão de minuto.
  • Evite agendar tudo em 0 * * * * ou à meia-noite: muita gente faz isso, e os serviços ficam sobrecarregados nesses horários. Um minuto quebrado, como 17, ajuda.
  • Garanta que a tarefa aguente rodar duas vezes ou atrasar. Reinícios e trocas de servidor fazem isso acontecer.

Achou um erro neste guia? Avise por e-mail.

Perguntas frequentes

Como fazer um cron a cada 5 minutos?

Use */5 * * * *. O */5 no campo de minuto significa a cada 5 minutos, a partir do minuto zero: 0, 5, 10 e assim por diante.

O cron usa o horário de Brasília?

Depende de onde roda. O cron do Linux usa o fuso configurado no servidor. O GitHub Actions e a maioria dos serviços de nuvem usam UTC, que está 3 horas à frente de Brasília.

Domingo é 0 ou 7 no cron?

O padrão é 0. A maioria das implementações aceita 7 também como domingo, mas usar 0 funciona em todas.

Por que meu cron rodou em dias a mais?

Provavelmente os campos de dia do mês e de dia da semana estão restritos ao mesmo tempo. Nesse caso o cron roda quando qualquer um dos dois bate, não quando os dois batem.