DEV Community

Cover image for 0 9 1 * 5 is Friday or the 1st, not Friday the 1st
Ameen Usman
Ameen Usman

Posted on

0 9 1 * 5 is Friday or the 1st, not Friday the 1st

I meant “09:00 only when the 1st is a Friday.” The line I wrote was 0 9 1 * 5. On Vixie cron and cronie that is 09:00 on the 1st of every month, and also 09:00 every Friday. Both day fields are restricted, so the daemon runs the job when either field matches. It does not wait until both match.

A star turns the rule off

A bare * is not a restriction. 0 9 1 * * is 09:00 on the 1st only. 0 9 * * 5 is 09:00 every Friday. The either-day rule starts only when both day fields carry a number, list, range, or a step that does not start with *. On these daemons a field that starts with *, including */2, counts as unrestricted and combines with the other day field using AND.

30 4 1,15 * 5
Enter fullscreen mode Exit fullscreen mode

That crontab(5) example is 04:30 on the 1st, 04:30 on the 15th, and 04:30 every Friday. On a Friday that is also the 1st, one line still runs once that morning.

0 9 * * 1-5 is not this rule. The day-of-month is a star, so the line is simply weekdays at 09:00.

Confirm the daemon, then test

This is the documented rule for Vixie cron and cronie. FreeBSD’s crontab(5) describes the same either-field rule. Quartz, Spring, and EventBridge use six fields and a ? in one day field. systemd timers use OnCalendar, which ANDs weekday and day-of-month: Mon *-*-01 09:00:00 is 09:00 only when the 1st is a Monday. Confirm that with systemd-analyze calendar on that machine. This post does not claim the either-day rule for GitHub Actions or BusyBox crond.

Pick a weekday that is not the 31st. Suppose today is Wednesday and date +%w prints 3:

* * 31 * 3 date >> /tmp/cron-or-test.log
Enter fullscreen mode Exit fullscreen mode

The day-of-month is wrong for today. The weekday matches. If a new line appears within a minute, this host is using the either-field rule. Then try * * 31 * *. That should stay quiet today. Remove both lines when you are done. A * in the minute field means every minute.

How to require both days

You cannot say “Friday the 1st only” with one standard five-field line on Vixie cron. Schedule one field, and test the other in the command. On GNU date and in POSIX, %u is 1 for Monday through 7 for Sunday, so Friday is 5. In the crontab write \%, because a bare % is a newline.

0 9 1 * * [ "$(date +\%u)" -eq 5 ] && /usr/local/bin/job
Enter fullscreen mode Exit fullscreen mode

Cron runs the command with sh. Use the absolute path of the job. Two separate lines, one for the 1st and one for Friday, match the same set of days as 0 9 1 * 5, except that when the 1st is a Friday both lines fire and the job can run twice. One line runs once.

Build the fields in the cron expression generator, then test the line on the host that will run it. The generator cannot know which daemon you have.

The full walkthrough is in the guide: https://kepttools.com/guides/cron-both-day-fields

Top comments (0)