Перейти к содержимому
Toova
Все инструменты

Синтаксис cron-выражений: полное объяснение

Toova

Cron-выражения — одна из тех тем, которые разработчики изучают однажды, потом забывают детали и возвращаются к ним каждые полгода. Пятисимвольная строка вроде 30 9 * * 1-5 может кодировать удивительно конкретное расписание — «каждый будний день в 9:30» — но синтаксис содержит достаточно крайних случаев, чтобы вызвать реальную путаницу: поля с нулевой и единичной индексацией, воскресенье как 0 и 7 одновременно, разница между стандартом Unix и шестипольным расширением Quartz, а также неочевидное поведение шаговых значений в сочетании с диапазонами.

В этом руководстве синтаксис cron разбирается с первых принципов, а затем разбирается каждый специальный символ с конкретными примерами. Независимо от того, пишете ли вы задачу cron для демона Linux, настраиваете расписание GitHub Actions или создаёте правило AWS EventBridge, применяются те же базовые концепции — с объяснением нескольких важных платформенных различий.

Пять стандартных полей

Стандартное cron-выражение состоит из пяти полей, разделённых пробелами, читаемых слева направо: минуты, часы, день месяца, месяц и день недели. Каждое поле ограничивает время запуска задачи по соответствующему измерению. Задача выполняется только тогда, когда все пять полей совпадают одновременно.

# Поле           Позиция  Диапазон      Спецсимволы
# ─────────────────────────────────────────────────
# Минуты              1      0-59          * / , -
# Часы                2      0-23          * / , -
# День месяца         3      1-31          * / , - ? L W
# Месяц               4      1-12 или JAN-DEC   * / , -
# День недели         5      0-7 (0=7=Вс) или SUN-SAT  * / , - ? L #

Читаем выражение 30 9 * * 1-5: минута 30, час 9, любой день месяца, любой месяц, день недели от 1 до 5 (с понедельника по пятницу). Результат: 9:30 каждый будний день.

Поле 1: Минуты (0–59)

Поле минут указывает, в какую минуту (или минуты) часа должна выполняться задача. 0 означает начало часа, 30 — половину, 59 — последнюю минуту перед сменой часа. Использование * запускает задачу каждую минуту.

Поле 2: Часы (0–23)

Часы используют 24-часовой формат. 0 — полночь, 12 — полдень, 23 — 23:00. В cron нет AM/PM — задача в 9:00 имеет час 9, а задача в 21:00 — час 21.

Поле 3: День месяца (1–31)

В отличие от других полей, день месяца начинается с 1, а не с 0. Допустимые значения: от 1 до 31. Если указан день, которого нет в данном месяце (например, 31 апреля), задача просто не выполняется в этом месяце. В Quartz специальный символ L означает последний день месяца, вне зависимости от его продолжительности.

Поле 4: Месяц (1–12)

Значения месяца: от 1 (январь) до 12 (декабрь). Многие планировщики также принимают трёхбуквенные сокращения: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC. В большинстве реализаций они нечувствительны к регистру.

Поле 5: День недели (0–7)

Это поле с наибольшим историческим наследием. И 0, и 7 означают воскресенье — это особенность совместимости, унаследованная от ранних версий Unix. Понедельник — 1, вторник — 2, вплоть до субботы — 6. Трёхбуквенные обозначения здесь тоже работают: SUN, MON, TUE, WED, THU, FRI, SAT.

Когда указаны оба поля — день месяца и день недели (ни одно не равно *) — большинство Unix cron-демонов используют логику ИЛИ: задача выполняется, если совпадает хотя бы одно из полей. Это удивляет многих разработчиков. Если нужно «15-е число, но только если это рабочий день», такую проверку придётся реализовывать в самом скрипте задачи, а не в cron-выражении.

Специальные символы

Звёздочка (*) — любое значение

Звёздочка соответствует любому допустимому значению поля. В поле минут * означает минуты от 0 до 59. В поле месяца — все 12 месяцев. Это наиболее распространённый символ в cron-выражениях.

Косая черта (/) — шаговые значения

Косая черта задаёт интервал шага. */5 в поле минут означает «каждые 5 минут». Точнее: «каждое значение в диапазоне, кратное 5, начиная с первого значения диапазона». Таким образом, */5 в минутах даёт: 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55.

Шаги можно комбинировать с диапазонами: 10-30/5 в поле минут означает «каждые 5 минут между минутой 10 и минутой 30», что даёт: 10, 15, 20, 25, 30. Шаг начинается от нижней границы диапазона.

Запятая (,) — список значений

Запятые создают список конкретных значений. 1,3,5 в поле месяца означает январь, март и май. 9,17 в поле часов означает 9:00 и 17:00. Можно перечислять сколько угодно значений и комбинировать их с другими конструкциями.

Дефис (-) — диапазон

Дефис задаёт включительный диапазон. 1-5 в поле дня недели означает с понедельника по пятницу. 9-17 в поле часов — с 9 до 17 часов. Диапазоны включают обе границы.

Знак вопроса (?) — без конкретного значения (только Quartz)

Стандартный Unix cron не поддерживает ?. В Quartz Scheduler этот символ используется в полях дня месяца или дня недели и означает «это поле не важно». Поскольку Quartz не всегда может разрешить конфликт между этими двумя полями, одно из них должно быть установлено в ?, когда задано другое. 0 0 15 * ? означает «15-е число каждого месяца, любой день недели». 0 0 ? * MON означает «каждый понедельник, любой день месяца».

L — последний (только Quartz)

L в поле дня месяца означает последний день месяца. L в поле дня недели означает субботу, а в сочетании с числом (например, 5L) — последнее вхождение этого дня недели в месяце. 5L — последняя пятница месяца.

W — ближайший рабочий день (только Quartz)

W в поле дня месяца находит ближайший рабочий день (с понедельника по пятницу) к указанному числу. 15W означает ближайший рабочий день к 15-му числу. Если 15-е — суббота, задача выполняется 14-го (в пятницу). Если воскресенье — 16-го (в понедельник). W не переносится на другой месяц.

Решётка (#) — N-й день недели месяца (только Quartz)

# задаёт N-е вхождение дня недели в месяце. 5#2 означает вторую пятницу месяца. 2#1 — первый вторник. Если указанное вхождение отсутствует в данном месяце, задача не выполняется в этом месяце.

Распространённые шаблоны cron

# Каждую минуту
* * * * *

# Каждый день в полночь (00:00)
0 0 * * *

# Каждый день в 9:30
30 9 * * *

# Каждый понедельник в 8:00
0 8 * * 1

# Первый день каждого месяца в полдень
0 12 1 * *

# Каждые 15 минут
*/15 * * * *

# Будние дни в 18:00
0 18 * * 1-5

Проверить и подтвердить любой из этих шаблонов можно с помощью инструмента Cron Parser, который показывает пять ближайших времён запуска для любого выражения и подсвечивает недопустимый синтаксис. Используйте Конвертер временны́х меток, если нужно узнать, какому времени в вашем часовом поясе соответствует конкретная метка Unix.

6-польное расширение: Quartz Scheduler

Quartz Scheduler — широко используемый в экосистеме Java и многими корпоративными планировщиками — добавляет обязательное поле секунд в начало выражения. Формат принимает вид: секунды минуты часы день-месяца месяц день-недели.

# Quartz 6 полей: секунды минуты часы день месяц деньНедели
# Запуск ровно в 00 секунд, 30 минут каждого часа
0 30 * * * ?

# Запуск в полночь каждый день
0 0 0 * * ?

# Запуск каждые 5 секунд
0/5 * * * * ?

# Последний день месяца в 10:00
0 0 10 L * ?

# Последняя пятница месяца в 15:00
0 0 15 ? * 6L

Ключевые отличия от стандартного cron: поле секунд (0–59) является первым и обязательным; день недели использует значения от 1 (воскресенье) до 7 (суббота), а не от 0 до 6; ? обязателен либо в поле дня месяца, либо в поле дня недели, когда задано другое; L, W и # являются допустимыми специальными символами.

Никогда не вставляйте выражение Quartz в Unix crontab без удаления поля секунд и корректировки нумерации дней недели. Парсеры несовместимы.

Cron в различных системах

Linux/Unix crontab (Vixie cron)

Исходная и наиболее распространённая форма. Пять полей, разделённых пробелами, за которыми следует команда. Переменные окружения CRON_TZ или TZ устанавливают часовой пояс. Пользовательские crontab редактируются с помощью crontab -e; системные crontab находятся в /etc/cron.d/. Сокращения @reboot, @daily, @hourly, @weekly и @monthly широко поддерживаются как псевдонимы распространённых шаблонов.

GitHub Actions

GitHub Actions использует стандартный 5-польный синтаксис cron, однако часовой пояс всегда UTC — в самом выражении нельзя указать локальный часовой пояс. Выражения вычисляются на уровне репозитория, а не на уровне задания. Обратите внимание, что запланированные рабочие процессы могут задерживаться на несколько минут в периоды высокой нагрузки на инфраструктуру GitHub.

# Синтаксис schedule для GitHub Actions (5 полей, только UTC)
on:
  schedule:
    # Запуск каждый день в 02:30 UTC
    - cron: '30 2 * * *'
    # Запуск каждый понедельник в 09:00 UTC
    - cron: '0 9 * * 1'

В официальной документации GitHub Actions по cron указано, что минимальный интервал — 5 минут; выражения с более частым запуском молча игнорируются.

AWS EventBridge (CloudWatch Events)

AWS EventBridge поддерживает как выражения скорости (rate(5 minutes)), так и cron-выражения. Их вариант cron 6-польный, но отличается от Quartz: день недели использует обозначения Sun–Sat или числа от 1 до 7, где 1 — воскресенье; всё время указывается в UTC; хотя бы одно из полей — день месяца или день недели — должно быть ?. AWS не поддерживает символы W и #.

# AWS EventBridge (cron в UTC, 6-польный вариант)
# Запуск каждый день в 10:00 UTC
cron(0 10 * * ? *)

# Запуск в 18:00 в последний рабочий день каждого месяца
cron(0 18 L-1 * ? *)

Kubernetes CronJob

Kubernetes CronJob использует стандартный 5-польный синтаксис cron. По умолчанию часовой пояс соответствует часовому поясу процесса kube-controller-manager (обычно UTC на управляемых кластерах). В Kubernetes 1.25 в спецификацию CronJob добавлено поле timeZone, позволяющее настраивать часовой пояс для каждого задания без опоры на системный часовой пояс.

Node.js (node-cron / cron npm)

Популярный пакет node-cron поддерживает стандартные 5-польные выражения с необязательным 6-м полем секунд в начале. Пакет cron совместим с Quartz. Оба поддерживают строки часового пояса (например, America/New_York) в качестве параметра конструктора. Проверяйте документацию конкретного пакета для подтверждения ожидаемого формата.

Типичные ошибки

Неправильные предположения о часовом поясе

Cron выполняется в часовом поясе сервера или контейнера, который в облачных средах часто соответствует UTC. Задача, предназначенная для запуска «в 9 утра рабочего времени» и настроенная как 0 9 * * *, выполнится в 9:00 UTC — что может быть 4:00, 5:00 или 11:00 по местному времени в зависимости от смещения и перехода на летнее время. Всегда устанавливайте CRON_TZ, используйте поле часового пояса планировщика или явно конвертируйте целевое время в UTC. Конвертер часовых поясов поможет найти UTC-эквивалент для любого местного времени в любом часовом поясе.

Логика ИЛИ для полей день месяца / день недели

В Unix cron, когда оба поля — день месяца и день недели — отличны от *, демон запускает задачу при совпадении любого из них. Таким образом, 0 9 15 * 1 выполняется и 15-го числа каждого месяца, и каждый понедельник — а не только в понедельники, приходящиеся на 15-е. Для реализации логики И нужно добавить соответствующую проверку в сам скрипт задачи.

Путаница шаговых значений с диапазонами

*/5 означает «каждые 5 минут, начиная с 0» (0, 5, 10...). 5/5 означает «каждые 5 минут, начиная с 5» (5, 10, 15...). 5-30/5 означает «каждые 5 минут между минутой 5 и минутой 30» (5, 10, 15, 20, 25, 30). Комбинирование с диапазонами может дать неожиданные результаты, если начальное значение задано неточно.

Ловушка «каждую секунду»

Минимальное разрешение cron в стандартном 5-польном формате — одна минута. С помощью crontab невозможно запланировать задачу на каждую секунду или на несколько секунд. Для суперминутного планирования используйте таймеры на уровне языка (setInterval в Node.js, APScheduler в Python) или специализированную систему очередей, а не cron. Если нужно каждые 30 секунд, запустите два задания cron: одно на * * * * * и одно, которое ждёт 30 секунд перед выполнением.

Путаница с нумерацией месяцев

В стандартном cron месяцы нумеруются от 1 (январь) до 12 (декабрь). В некоторых API дат языков программирования месяцы индексируются с нуля (0 = январь, 11 = декабрь). При программной генерации cron-выражений из объектов дат убедитесь, что в поле месяца нет смещения на единицу.

Валидация выражений

Инструмент Cron Parser на Toova показывает пять ближайших запланированных времён для любого выражения, поддерживает 5-польный и 6-польный синтаксис Quartz и подсвечивает недопустимые значения прямо в интерфейсе. Для быстрой проверки crontab.guru — широко известный интерактивный инструмент, описывающий выражения простым языком. Оба полезно держать в закладках.

При работе с метками времени и запланированными временами Конвертер временны́х меток переводит между метками времени Unix и читаемыми датами в любом часовом поясе. Для задач, связанных с часовыми поясами, объедините его с Конвертером часовых поясов, чтобы проверить смещения, переходы на летнее время и UTC-эквиваленты перед написанием финального выражения.