Conversor de cron e temporizador systemd | Conversão bidirecional cron ↔ OnCalendar com prévia de 5 disparos e .timer pronto para colar
Conversor bidirecional entre expressões cron POSIX e strings systemd OnCalendar, com prévia dos próximos 5 disparos e modelo .timer ou linha crontab prontos para colar. A ideia é flagrar erros de sintaxe e agendamentos "ah, não era isso" antes de habilitar a unidade.
💡 Sobre esta ferramenta
cron e systemd-timer expressam a mesma ideia — disparar algo em um momento específico — com sintaxes bem distintas. cron lê da esquerda para a direita: minuto hora dia mês dia-semana. OnCalendar, ao contrário, coloca o dia da semana no início, depois um trio de data e por fim HH:MM:SS. A tradução mecânica é direta, mas os detalhes pegam de surpresa: MON-FRI vira Mon..Fri, */15 vira *:0/15, cron de seis campos (com segundos) não tem equivalente POSIX, e RandomizedDelaySec simplesmente não existe no cron.
A ferramenta cuida desses casos. Expande as formas de lista, intervalo, passo e nome (JAN-DEC, MON-FRI) e emite a string OnCalendar com prefixo DOW apenas quando faz diferença. No caminho inverso, interpreta os intervalos .. e o passo 0/N típicos do systemd, separa o RandomizedDelaySec no painel de avisos e escreve uma linha crontab de 5 campos. Os dois sentidos mostram os próximos 5 disparos para 9 fusos comuns, então dá para validar contra o relógio do servidor antes de mexer no /etc/.
O modelo .timer inclui Persistent=true e AccuracySec=1min como ponto de partida razoável para tarefas one-shot. Para disparos mais precisos ou para impedir que a máquina entre em suspensão, ajuste esses valores após copiar. A unidade pressupõe um .service complementar com o mesmo nome de base.
🧐 Perguntas frequentes
Q. cron de 6 campos (com segundos) funciona?
A. Sim — o primeiro campo é tratado como segundos e aparece em HH:MM:SS do OnCalendar. Um aviso lembra que cron POSIX ignora precisão sub-minuto; o ajuste só faz efeito do lado systemd.
Q. Por que @daily ou @reboot não são aceitos?
A. O parser trabalha apenas com a grade explícita * * * * *. @daily equivale a 0 0 * * *, digite assim. @reboot não tem contrapartida em OnCalendar; use OnBootSec= ou OnStartupSec= diretamente na unidade .timer.
Q. O que acontece com RandomizedDelaySec ao virar cron?
A. Ele é removido com um aviso. cron não tem jitter nativo; para dispersar a execução adicione sleep $((RANDOM % 60)) no início do script chamado pelo cron. A ferramenta não faz essa reescrita automaticamente.
Q. A prévia mostra UTC mas o servidor roda em outro fuso
A. O menu de fusos traz UTC, fuso do navegador, JST, ET, PT, GMT, CET (×2) e BRT. Escolha o que combina com o relógio do servidor e a lista de 5 disparos é recalculada. Para fixar o fuso dentro do próprio timer, adicione Timezone= à seção [Timer] após copiar.
Q. O range Mon..Fri é mantido ao converter systemd → cron?
A. Ele é expandido para a lista 1,2,3,4,5 na linha cron. Se preferir a forma 1-5, reescreva à mão; todos os crond modernos aceitam ambas.
Q. Por que minha expressão falha?
A. Os erros aparecem no painel vermelho abaixo do campo de entrada, com o token problemático (Invalid value: ..., Date must be Y-M-D). A saída fica em -- até corrigir a entrada.
📚 Curiosidades
systemd-timer chegou com systemd v183 em 2012, mas crond é bem mais antigo — o cron(8) veio junto do Version 7 Unix em 1979 e há mais de quatro décadas executa lotes pelo mundo. Convivem porque resolvem coisas levemente diferentes: o contrato cron é "rode esta linha neste horário", enquanto um timer systemd é só um gatilho que ativa uma .service definida separadamente. Essa indireção custa esforço para um one-liner, mas compensa quando você quer limites de recursos, arquivos de ambiente, ordenação de dependências ou logs estruturados no journal.
OnCalendar parece ISO 8601 à primeira vista, mas o * curinga e o operador de passo 0/N são extensões próprias do systemd — ISO 8601 não tem nenhum dos dois. O comando systemd-analyze calendar 'Mon..Fri *-*-* 09:30:00' avalia a expressão e imprime os próximos disparos; é exatamente o cálculo que esta página executa dentro do navegador.