I would like to run a job through cron that will be executed every second Tuesday at given time of day. For every Tuesday is easy:
0 6 * * Tue
But how to make it on "every second Tuesday" (or if you prefer - every second week)? I would not like to implement any logic in the script it self, but keep the definition only in cron.
How about this, it does keep it in the
crontab
even if it isn't exactly defined in the first five fields:If you want to do it based on a given start date:
Should fire every other Monday beginning with 2018-03-19
Expression reads: Run at 6am on Mondays if ...
1 - Get today's date, in seconds, divided by the number of seconds in a day to convert to days sice epoch
2 - Do the same for the starting date, converting it to the number of days since epoch
3 - Get the difference between the two
4 - divide by 14 and check the remainder
5- If the remainder is zero you are on the two-week cycle
Why not something like
Is that appropriate?
Answer
Modify your Tuesday cron logic to execute every other week since the epoch.
Knowing that there are 604800 seconds in a week (ignoring DST changes and leap seconds, thank you), and using GNU date:
Aside
Calendar arithmetic is frustrating.
@xahtep's answer is terrific but, as @Doppelganger noted in comments, it will fail on certain year boundaries. None of the
date
utility's "week of year" specifiers can help here. Some Tuesday in early January will inevitably repeat the week parity of the final Tuesday in the preceding year: 2016-01-05 (%V), 2018-01-02 (%U), and 2019-01-01 (%W).I discovered some additional limitations of above approaches that can fail in some edge cases. For instance, consider:
This is because these approaches rely on UTC time (date +%s). Consider a case where we're running a job at 1am and 10pm every 2nd Tuesday.
Suppose GMT-2:
If we are only checking a single time each day, this will not be an issue, but if we are checking multiple times -- or if we are close to UTC time and daylight savings occurs, the script wouldn't consider these to be the same day.
To get around this, we need to calculate an offset from UTC based on our local timezone not UTC. I couldn't find a simple way to do this in BASH, so I developed a solution that uses a quick one liner in Perl to compute the offset from UTC in seconds.
This script takes advantage of date +%z, which outputs the local timezone.
Bash script:
then, to determine whether the day is even or odd: