How to Schedule a Cron Job to Run Every 2nd Wednesday of the Month


3 views

When working with cron schedules, specifying a particular weekday in a month requires careful consideration of the cron syntax. The standard cron format consists of five fields:

* * * * *
| | | | |
| | | | +-- Day of week (0 - 6) (Sunday=0)
| | | +---- Month (1 - 12)
| | +------ Day of month (1 - 31)
| +-------- Hour (0 - 23)
+---------- Minute (0 - 59)

While cron supports specifying days of the week (0-6 where 0 is Sunday), it doesn't natively support "nth weekday of the month" patterns. We need to use some creative combinations to achieve this.

To run on the second Wednesday of every month, we can use either of these two approaches:

Option 1: Using Day Range with Day of Week

0 0 8-14 * 3 /path/to/command

This works because:

  • The day of week field (last field) specifies Wednesday (3)
  • The day of month field restricts to days 8-14, which will always include the second Wednesday

Option 2: More Precise Alternative

0 0 8-14 * * [ "$(date '+\%u')" -eq 3 ] && /path/to/command

This version:

  • Runs daily between 8th and 14th
  • Only executes if the current day is Wednesday (3 when using %u format)
  • Is more explicit about the weekday check

To test your cron expression before implementing, you can use:

# For the next 12 months
for i in {0..11}; do
  date -d "$(date +%Y-%m-01) +$i month" +'%B %Y:'
  date -d "$(date +%Y-%m-01) +$i month +1 week +3 days" +'%A, %B %d'
done

For more complex scheduling needs, consider moving the logic to a script:

#!/bin/bash

current_day=$(date +%d)
current_weekday=$(date +%u)

if [[ $current_weekday -eq 3 ]] && [[ $current_day -ge 8 ]] && [[ $current_day -le 14 ]]; then
  /path/to/your/actual/command
fi

Remember that:

  • Cron implementations vary slightly across systems
  • The % character in cron needs escaping as \%
  • Time zones and daylight saving changes might affect execution
  • Always test your cron jobs thoroughly before deployment

Cron is incredibly powerful for scheduling repetitive tasks, but some scheduling patterns can be tricky to implement. Running a job every second Wednesday of the month is one such case where the standard cron syntax doesn't provide a direct solution.

Standard cron syntax allows you to specify:

1. Minutes (0-59)

2. Hours (0-23)

3. Day of month (1-31)

4. Month (1-12)

5. Day of week (0-6, where 0=Sunday)

But there's no built-in way to specify "second occurrence of a particular weekday".

Option 1: Using Shell Logic in the Command

Wrap your command in a shell check that verifies it's the second Wednesday:

0 9 8-14 * * [ "$(date '+\%u')" -eq 3 ] && /path/to/your/command

This breaks down as:

- Runs daily between 8th-14th at 9:00 AM

- The shell condition checks if it's Wednesday (day 3 in cron)

- Only executes if both conditions are true

Option 2: Using Multiple Cron Entries

Create several cron entries covering the possible date ranges:

# For months where the 1st is Sunday-Wednesday
0 9 8-14 * * [ "$(date '+\%u')" -eq 3 ] && /path/to/command

# For months where the 1st is Thursday-Saturday
0 9 15-21 * * [ "$(date '+\%u')" -eq 3 ] && /path/to/command

Option 3: Using a Wrapper Script

Create a shell script that contains the logic and call it from cron:

#!/bin/bash
current_day=$(date '+%d')
current_dow=$(date '+%u')

# Check if it's Wednesday and between 8th-14th
if [ "$current_dow" -eq 3 ] && [ "$current_day" -ge 8 ] && [ "$current_day" -le 14 ]; then
    /path/to/your/command
fi

Then in crontab:

0 9 8-14 * * /path/to/wrapper_script.sh

Always test your cron solutions before putting them into production. You can use this command to simulate cron execution:

for i in {8..14}; do date -d "2023-01-$i" +"%A %d-%m-%Y"; done | grep Wednesday

If you're not constrained to cron, consider these alternatives:

- systemd timers: More flexible calendar-based scheduling

- anacron: For systems that might be off during scheduled times

- CI/CD pipelines: Many modern CI systems have sophisticated scheduling