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

- Endereço oficial: https://pradev.com.br/guias/expressao-cron-como-ler-e-escrever
- Tipo: Guia
- Publicado em: 2026-09-30
- Atualizado em: 2026-09-30
- Autor: Leonardo Gaertner (https://pradev.com.br/sobre)
- Ferramentas: [Interpretador de Cron](https://pradev.com.br/interpretador-de-cron)

## 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](https://pradev.com.br/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}`;
}
```

_JavaScript. 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}"
```

_Python_

```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);
}
```

_PHP_

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

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

## Fontes

- [crontab(5), manual do Linux](https://man7.org/linux/man-pages/man5/crontab.5.html)
- [Kubernetes: CronJob](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)

## Guias relacionados

- [Timestamp Unix: como converter data e hora em código](https://pradev.com.br/guias/timestamp-unix-como-converter): Entenda o timestamp Unix, a diferença entre segundos e milissegundos e como converter de e para data em JavaScript, Python e PHP, com código testado.
