Is POSIX wrong about crontab with non-asterisk month and day-of-week

cronposix

Background

It is widely known that the UNIX cron runs jobs when either day-of-month or day-of-week matches, if both are specified with explicit values rather than an asterisk wildcard:

0 0 * * 1     # every Monday
0 0 1 * *     # every month's first day
0 0 1 * 1     # every Monday *and also* every month's first day
0 0 1 * 1-7   # every day
0 0 1-31 * 1  # every day

Note how these commonly seen examples all have * in the month field.

POSIX on non-asterisk month

The POSIX specification actually says this for crontab (emphasis mine):

The specification of days can be made by two fields (day of the month and day of the week). If month, day of month, and day of week are all <asterisk> characters, every day shall be matched. If either the month or day of month is specified as an element or list, but the day of week is an <asterisk>, the month and day of month fields shall specify the days that match. If both month and day of month are specified as an <asterisk>, but day of week is an element or list, then only the specified days of the week match. Finally, if either the month or day of month is specified as an element or list, and the day of week is also specified as an element or list, then any day matching either the month and day of month, or the day of week, shall be matched.

This seems to declare that the following schedules will match either month or day-of-week:

0 0 * 1 1     # every month's every Monday *and also* every day in January
0 0 * 1-12 1  # every day
0 0 1 1 1     # every month's every Monday *and also* 1 January

Non-asterisk month in practice

As far as I can tell, no common modern or historical cron implementation actually does this, including SVR4 cron (which must have been the first to match day-of-month or day-of-week) and its descendants in OpenSolaris and illumos as well as Vixie cron and its descendants Cronie and FreeBSD, OpenBSD, NetBSD, DragonFly BSD and macOS crons, as well as BusyBox crond.

Instead, all of these implementations always require a match of the month field regardless of what form any field takes, and only change their day-of-month matching logic based on whether the two day-of-month and day-of-week fields contain * or explicit value lists:

0 0 * 1 1     # every Monday in January
0 0 * 1-12 1  # every Monday
0 0 1 1 1     # every Monday in January *and also* 1 January

(or in the case of BusyBox, based on whether the fields contain full value ranges or strict subsets, regardless of the syntax used)

Specification bug?

Is the behaviour described in the POSIX specification actually implemented anywhere? Is the description a bug in the specification that might be corrected if a defect report is submitted? Is there a story behind why it is currently phrased this way?

Best Answer

Digital Unix v4 (aka OSF1) implemented this (archived man page here):

You can specify the days on which the command is to execute in two fields (day of the month and day of the week). You can specify both fields, or you can specify only one field. To use only one field to specify the days, the other field should contain an asterisk (*). If both methods are used, the command is executed whenever either of the specifications is met.

And so does Solaris proper, though the text of the man page lacks clarity (and has done since forever), Example 3 is clear on the intended behaviour:

Example 3 Specifying Days of the Month and Week

This example runs a command on the first and fifteenth of each month, as well as on every Monday:

0 0 1,15 * 1

Dillon crond interprets * * 2 * mon to mean "the 2nd Monday of the month", which while useful, is also non-standard. The answers in the question linked by @thanasisp are useful to read at this point. The "or" behaviour is from System V R2 (ca. 1985) at least.

I have found that when reading the Open Group documentation one needs to be aware of

  • pedantic meaning of certain words in certain contexts, just like RFCs, but more so
  • sections of text bounded by ⌦ ... ⌫ symbols (an unusual convention as these symbols are forward- and backward-delete)

There is text within the crontab reference marked [UP] ⌦ ... ⌫ which indicates optional behaviour (for crontab -e) on the part of User Portable Utilies, the "Input Files" section does not have any such marks.

Finally, if either the month or day of month is specified as an element or list, and the day of week is also specified as an element or list, then any day matching either the month and day of month, or the day of week, shall be matched.

I have highlighted the word "shall", which leaves no doubt:

shall

For an implementation that conforms to POSIX.1-2017, describes a feature or behavior that is mandatory. An application can rely on the existence of the feature or behavior.

For an application or user, describes a behavior that is mandatory.

But, as a system administrator I would tend to consider the specification abstract, it cannot be wrong (unless it is so wrong it cannot be implemented). The fault, if any, is within an implementation if it does not conform to a claimed specification (possibly with the help of POSIXLY_CORRECT which is the usual get-out-of-jail-free for implementors who bend the specification in the interest of sanity or user expectations).