Understanding Time Settings
A job carries one time record. Everything in it is optional, and with none of it set the job runs as soon as the load level allows.
- No time at all
- The job is eligible immediately and runs when the load level allows. This is what you get if you give none of the time options.
- A next run time
- The earliest moment the job may start. The scheduler will start a job up to three seconds early rather than wait for the exact second.
- A repeat specification
- After each run that ends inside the job's normal exit-code range, the next run time is recalculated and the job stays on the queue.
- Days to avoid
- Days of the week, and optionally holidays, that a recalculated run time must not fall on.
- An action for a missed slot
- What to do if the scheduler reaches the job after its run time has already gone by.
Two further settings are about the end of the job rather than the start: a maximum elapsed run time, and a time after which a finished job is removed from the queue.
Which Option Sets Which
Several of these letters are easy to confuse, and three of the wrong ones are accepted silently rather than refused. Check this table before writing a command line.
| Option | Long form | Sets |
|---|---|---|
| -T | --time | The next run time |
| -r | --repeat | The repeat specification |
| -A | --avoiding-days | Days of the week and holidays to avoid |
| -S | --skip-if-held | Missed slot: skip it |
| -H | --hold-current | Missed slot: run late, keep the schedule (the default) |
| -R | --reschedule-all | Missed slot: run late, move the schedule |
| -9 | --catch-up | Missed slot: one run, keep the schedule |
| -j | --advance-time-error | Advance the time after a failed run (the default) |
| -J | --no-advance-time-error | Leave the time alone after a failed run |
| -d | --delete-at-end | Run once, then delete from the queue (the default) |
| -o | --no-repeat | Run once, then retain on the queue marked Done |
| -U | --no-time | Remove the time setting entirely |
| -t | --delete-time | Hours after a job ends before it is deleted |
| -Y | --run-time | Maximum elapsed run time |
| -W | --which-signal | Signal to send when the run time is exceeded |
| -2 | --grace-time | Grace period before SIGKILL follows that signal |
The four that are commonly written by mistake:
- -t is the delete time in hours, not the run time. btr -t "03:00" job.sh is accepted, sets a three-hour auto-delete, and leaves the job with no run time at all - the argument is read with atoi, so everything after the first number is discarded without a message.
- -a is --argument. btr -a "Sat,Sun" job.sh passes the string Sat,Sun to the job as a command-line argument.
- -E is --local-environment, which applies only to rbtr and is ignored by btr.
- -D is --directory. btr -D 24 job.sh sets the job's working directory to a directory called 24.
Setting the Run Time
The formats -T accepts:
hh:mm Time today, or tomorrow if it has passed
dd/mm Date this year, midnight
dd/mm,hh:mm Date and time
yy/mm/dd Two-digit year
yy/mm/dd,hh:mm
yyyy/mm/dd,hh:mm Four-digit year
The date and the time are separated by a comma. Nothing else is accepted: there are no words such as tomorrow or Monday, no ISO dates such as 2026-02-15, no space between the date and the time, and no seconds field. Anything else is refused with Bad time specification and exit status 2, and a stray character after an otherwise valid time gives Bad char `c' in time spec.
# Run at 03:00 - today if 03:00 is still to come, otherwise tomorrow
btr -T "03:00" script.sh
# Run at 14:30 on 15 February 2026
btr -T "2026/2/15,14:30" script.sh
# Run at 18:00 on 5 January, next occurrence
btr -T "5/1,18:00" script.sh
Two-digit dates depend on the time zone. In zones four or more hours behind UTC a bare 3/4 is read as month/day; everywhere else as day/month. Four-digit years and the yy/mm/dd form are unambiguous, and are the safer choice for a site that spans time zones.
A bare time rolls forward. If the hour and minute you give are earlier than now, or the same minute, the date is advanced by one day. So -T "03:00" never produces a time in the past.
A date in the past is treated differently for the two kinds of job. For a job that runs once, btr refuses it - Time given is not in future, exit status 2. For a repeating job the time is advanced by whole repeat intervals until it lands in the future; add -v and the command prints the result as a warning.
Seconds are always zero. The value stored is built from whole minutes.
In btq:
- Move to the job on the job list
- Press t
- Answer the prompts in turn: whether a time applies, the date and time, the repeat, the days to avoid, and the missed-slot action
- On the day and avoid rows, t s y set a value and f u n unset it; + and - step a number; h and l (or the arrow keys) move
Repeat Specifications
The argument to -r is a unit name, a colon and a count, and for the two monthly units a second colon and a target day. The unit names are matched without regard to case, and a bare number with no unit is read as hours.
| Unit | Meaning | Largest count accepted |
|---|---|---|
| Minutes | Every n minutes | 527040 |
| Hours | Every n hours (the unit assumed if none is given) | 8784 |
| Days | Every n days | 1000 |
| Weeks | Every n weeks | 520 |
| Monthsb | Every n months, on a target day counted from the start of the month | 50 |
| Monthse | Every n months, on a target day counted back from the end of the month | 50 |
| Years | Every n years | 10 |
A count above the limit is refused with Bad repeat statement, as is an unknown unit name or a missing colon.
Simple Repeat Examples
Every 10 minutes:
btr -r Minutes:10 script.sh
Every 2 hours:
btr -r Hours:2 script.sh
Every day at the same time:
btr -T "03:00" -r Days:1 backup.sh
Every 2 weeks, starting on a particular Monday:
btr -T "2026/2/2,09:00" -r Weeks:2 report.sh
A repeat on its own is enough to give the job a time: -r sets the "time applies" marker, and if you give no -T the first run time is the moment the job was submitted, rounded up to the next minute.
Monthly Repeats
Months relative to the beginning. The target day is the day of the month to aim for, and it is used unchanged every month. If a month is shorter than the target day, the last day of that month is used.
# The 5th of every month at 10:00
btr -T "2026/2/5,10:00" -r Monthsb:1:5 invoice.sh
# The 15th of every third month at 14:00
btr -T "2026/2/15,14:00" -r Monthsb:3:15 quarterly.sh
Months relative to the end. The target day is interpreted once, against the length of the month named in -T, and is then stored as a number of days back from the end of the month. From then on it applies to every month whatever its length, so there is nothing to change for February.
# Last day of every month at 23:00 - March has 31 days, so 31
btr -T "2026/3/31,23:00" -r Monthse:1:31 month-end.sh
# The same result, set against February - 28 days, so 28
btr -T "2026/2/28,23:00" -r Monthse:1:28 month-end.sh
# Second to last day of every month
btr -T "2026/3/30,23:00" -r Monthse:1:30 pre-month-end.sh
A target day at or beyond the length of the -T month is taken as the last day. A target day that would put the run more than twenty days before the end of the month is refused with The effective "target day" of your months-relative-to-end parameter could take you back past the beginning of the month on the next repeat, so Monthse is for the last third of the month only. Use Monthsb for anything earlier.
How the Next Run Time Is Calculated
The new time is calculated from the scheduled time, not from the time the job actually ran. A job that starts late because of load or a condition returns to its original grid.
- Minutes, Hours, Days and Weeks
- The interval in seconds is added to the scheduled time.
- Monthsb
- The day of the month is moved to the target day, then the length of each intervening month is added.
- Monthse
- The stored number of days back from the end of the month is applied to the month reached.
- Years
- The year is incremented and the time rebuilt from the calendar, so the local clock time is preserved.
Example - every 2 hours:
Scheduled: 10:00 ran at 10:00
Next: 12:00 ran at 12:47 (held up by load)
Next: 14:00 not 14:47
The one exception is -R, described under When a Run Time Is Missed below, which deliberately rebases the schedule on the time the job actually started.
Avoiding Days
The argument to -A is a comma-separated list of day names, matched without regard to case. The names are the short ones only.
- Sun
- Mon
- Tue
- Wed
- Thu
- Fri
- Sat
- Hday - holidays, from the holiday calendar
As shipped, Sat and Sun are already avoided. The defaults come from the help file, and a job submitted with no -A at all inherits them. This matters twice over: adding -A "Sat,Sun" changes nothing, and a job that really must run every day needs the defaults cleared.
# Replaces the list - this job now avoids ONLY holidays, not weekends
btr -T "09:00" -r Days:1 -A "Hday" job.sh
# Adds to the list - a leading comma keeps the defaults
btr -T "09:00" -r Days:1 -A ",Hday" job.sh
# Clears the list - runs every day including weekends
btr -T "09:00" -r Days:1 -A "-" job.sh
# Clears the list and then sets one day
btr -T "09:00" -r Days:1 -A "-,Wed" job.sh
An unrecognised name is refused with a message that restates the syntax, and a list naming every day of the week is refused separately. Confirm what a job actually carries with btjlist -N -F "%a" <job_number>.
In btq:
- Move to the job on the job list
- Press t
- Step past the time and repeat prompts to the Avoiding row
- Move along it with h and l or the arrow keys; t s y set a day, f u n unset it, ! toggles
Avoid Days Behaviour
Avoidance is applied when a run time is calculated - at the end of a run, and when a past time given to btr is advanced into the future. A time you set explicitly with -T is used exactly as given, so the first run of a job can fall on an avoided day even though every later one will not.
Every unit except Monthse moves forward:
Schedule: Daily 09:00, avoiding Sat and Sun
Friday: 09:00 - runs
Saturday: 09:00 - avoided
Sunday: 09:00 - avoided
Monday: 09:00 - runs
Monthse moves backward:
Schedule: last day of month, avoiding Sat, Sun and Hday
Month ends Saturday: Friday is used
Month ends Sunday: Friday is used
Month ends a holiday: the previous acceptable day is used
This is why "last working day of the month" is a Monthse job and cannot be built out of any other unit. A weekly job that lands on a holiday moves forward to the next acceptable day, so a Friday payroll job avoiding weekends and holidays runs on the following Monday rather than on the Thursday before.
When a Run Time Is Missed
The scheduler compares each job's next run time with the clock on every pass. If the time has already gone by - because the scheduler was stopped, because the previous run overran, because the load level or a condition held the job, or simply because the machine was busy - what happens next depends only on the job's missed-slot setting. Conditions and load levels are tested afterwards, and they do not change this decision.
- -H, hold current - the default
- Run now. The next run time then advances by one interval from the slot that was missed, so if several slots went by the job runs once for each of them, one after another, until it has caught up.
- -R, reschedule all
- Run now, and reset the next run time to the moment the job actually started. The whole schedule moves by the amount of the delay.
- -9, catch up
- Discard the intervening slots, run once, and stay on the original grid.
- -S, skip if held
- Run anyway if the job is only slightly late; otherwise do not run at all - move the next run time forward past the present and wait for it.
Example - an hourly job whose 10:00 and 11:00 slots both go by, unblocked at 11:30:
-H (default): runs at 11:30, runs again immediately, next slot 12:00
-R: runs at 11:30, next slot 12:30
-9: runs once at 11:30, next slot 12:00
-S: does not run, next slot 12:00
A job that runs once always runs, however late. The missed-slot setting applies to repeating jobs; a -d or -o job whose time has passed is started at the first opportunity.
How late is "slightly late". Only -S uses a tolerance. It is a tenth of the repeat interval for Minutes and Hours, and a tenth of half the nominal interval for everything above.
| Repeat | Still runs if less than this late |
|---|---|
| Minutes:n | n × 6 seconds |
| Hours:n | n × 6 minutes |
| Days:n | n × 72 minutes |
| Weeks:n | n × 8 hours 24 minutes |
| Monthsb:n, Monthse:n | n × 1 day 10 hours |
| Years:n | n × 16 days 19 hours |
Nothing is recorded when a slot is skipped. There is no job-log entry and no line in the scheduler report file. The only evidence is that the time column has moved on without a corresponding run.
Read a job's setting with btjlist -N -F "%w" <job_number>; it prints Skip, Delay, Delall or Ctchup.
Daylight Saving and Time Zones
Xi-Batch holds every time as an internal clock value, and the repeat arithmetic for Minutes, Hours, Days, Weeks and both monthly units is done on that value in whole seconds. A daylight-saving change therefore moves the displayed time of those jobs: a daily job set for 03:00 runs at 02:00 or 04:00 local time once the clocks change, and stays there. Years is the exception - a yearly repeat is rebuilt through the calendar and keeps its local clock time.
There is no automatic correction. The command that makes it is btdst, which is a link to btstart and is installed with it.
btdst [-R] start-date end-date adjustment
The two dates take the same forms as the btdst reference page describes - dd/mm, mm/dd or yy/mm/dd, chosen by time zone exactly as for -T, optionally followed by a comma and hh:mm, with midnight assumed. The adjustment is a number of seconds, and the sign is the one that surprises people:
- Clocks going forward (into summer time): use a negative adjustment, normally -3600. The stored time is unchanged by the transition, so it now displays an hour later than it did; subtracting an hour puts it back.
- Clocks going back: use a positive adjustment, normally 3600.
# UK, clocks forward on 29 March 2026: correct the jobs due that week
btdst 29/3 5/4 -3600
# UK, clocks back on 25 October 2026
btdst 25/10 1/11 3600
What btdst changes, and what it leaves alone. It adds the adjustment to the next run time of every job whose next run time falls between the two dates, and writes a dst adjust entry to the job log for each one. It leaves alone: jobs with no time set; jobs repeating in Minutes or Hours, on the grounds that an hour makes no difference to them; and jobs owned by another host, unless -R is given. The reference manual advises against -R, and the code supports the advice - the adjustment is applied to this scheduler's copy only and is not sent to the owning host.
It is a one-off sweep, not a policy. Nothing runs btdst automatically and the repeat rule is unchanged, so it has to be run again at every transition. The natural arrangement is an Xi-Batch job of its own, repeating yearly, for each of the two transitions.
It requires a running scheduler and Write administration file privilege, and reports Scheduler not running (exit 6) or No permission for DST adjustment (exit 3) if either is missing. A start date at or after the end date, or an adjustment of zero, is refused.
One more effect worth knowing. When btr converts the time you type, it uses the UTC offset in force at midday on the date you named. On the date of a transition that is the offset for the afternoon, so a time set for the small hours of that one date can come out an hour away from what you asked for. Setting such a job for a time well clear of the transition avoids the question entirely.
What Happens After a Run
When a job finishes, the scheduler moves it to the back of the queue and recalculates its next run time. Whether it becomes eligible again depends on how it ended.
- Ended inside the normal exit-code range
- The time is advanced and the progress code is cleared, so the job is eligible for its next slot.
- Ended in error, aborted, or cancelled
- The time is advanced, and the progress code is left at Err, Abrt or Canc. A job in one of those states is passed over by every later pass of the scheduler, so the job never runs again until someone clears it.
The normal and error ranges are a property of the job, set with -X; the defaults are 0 for normal and 1 to 255 for error.
-j and -J control only the time, not the progress code. -j, the default, advances the next run time after a failed run. -J leaves it where it was. Neither makes the job repeat after a failure. What -J does give you is the evidence: the job's time column stops moving, which is visible in a listing, whereas with the default the schedule goes on looking healthy while nothing runs.
Clearing a stuck job:
# Look for a repeat specification alongside a progress code of Err, Abrt or Canc
btjlist -H -F "%N %H %P %T %r"
# Make one eligible again
btjchange -N <job_number>
In btq the same thing is the P key on the job list, setting the progress code back to nil.
Running Once, Retaining and Deleting
Run once and delete (the default):
btr script.sh
The job is removed from the queue when it finishes.
Run once and retain:
btr -o script.sh
The job stays on the queue marked Done. It can be made eligible again with btjchange -N.
Delete a finished job after a delay:
# Remove it 24 hours after it ends
btr -o -t 24 script.sh
The delete time is a number of hours, counted from the moment the job ended. It applies only to jobs sitting in Done, Err, Abrt or Canc, and only on the machine that owns the job; a job that has never run is never removed by it. The removal is written to the job log as auto delete. A value of 0, the default, keeps the job indefinitely.
Remove the time settings altogether:
btjchange -U <job_number>
Limiting How Long a Job May Run
-Y sets a maximum elapsed run time, as seconds, mm:ss or hh:mm:ss. When it is exceeded the scheduler kills the job. With -W and -2 it does so in two stages: the signal named by -W first, logged as exceeded runtime, and then SIGKILL after the grace period given by -2, logged as exceeded grace period. Without a signal or a grace period it goes straight to SIGKILL.
# Give the job an hour, then SIGTERM, then SIGKILL 30 seconds later
btr -T "02:00" -r Days:1 -Y 1:00:00 -W 15 -2 30 long-report.sh
A job killed this way ends outside its normal exit range, so a repeating job stops repeating - see What Happens After a Run.
Running a Job Outside Its Schedule
Three commands override the run time without changing it permanently. All three are links to btjdel and take job numbers.
- btjgo
- Run the job now, ignoring the run time. The next run time is untouched, so this inserts an extra run.
- btjgoadv
- Run the job now and advance the next run time, bringing the next run forward and then resuming the sequence.
- btjadv
- Advance the next run time without running the job and without looking at conditions.
Conditions and load levels still apply to btjgo and btjgoadv. On the btq job list the same three operations are f (run now, leaving the time alone), g (run now and advance the time) and a (advance the time only) - note that the two letters are the opposite way round from the way the command names read.
Reading and Changing a Job's Time Settings
The listing escapes are the reliable way to see what a job actually carries.
btjlist -H -F "%N %T %r %a %w %d" <job_number>
| Escape | Shows |
|---|---|
| %t | Next run time, short - hh:mm if it is within a day, otherwise a date |
| %T | Next run time in full, dd/mm/yyyy hh:mm |
| %r | Repeat specification, or Delete or Retain |
| %a | Days avoided |
| %w | Missed-slot action: Skip, Delay, Delall or Ctchup |
| %d | Delete time in hours |
| %l %y %g | Maximum run time, signal, grace period |
| %b %f | Time the last run started and ended |
btjlist -T switches the default format from %t to %T, which is the quickest way to see full dates without writing a format string.
Every time option above is also accepted by btjchange, which takes job numbers and applies the change to each one.
# Move a job to 04:00 and make it skip missed slots
btjchange -T "04:00" -S <job_number>
Complete Scheduling Examples
Daily Backup, Weekdays Only
btr -T "02:00" \
-r Days:1 \
-A "Sat,Sun,Hday" \
-J \
-f S -s 'backup_status=Running' \
-f N -s 'backup_status=Complete' \
-f EA -s 'backup_status=Failed' \
/usr/local/bin/backup.sh
The assignment syntax is covered in the conditions and assignments article; -J is here so that a failure leaves the stale time visible.
Month-End Processing on the Last Working Day
btr -T "2026/3/31,18:00" \
-r Monthse:1:31 \
-A "Sat,Sun,Hday" \
/usr/local/bin/month-end-close.sh
31 March 2026 is a Tuesday. Choose a start date that is itself a working day: avoidance is applied to calculated times, never to the date you give with -T, so a first run set on a Saturday will happen on that Saturday.
Frequent Monitoring, Every 5 Minutes, Every Day
btr -r Minutes:5 \
-A "-" \
-S \
/usr/local/bin/monitor-system.sh
-A "-" clears the shipped weekend defaults, which a monitoring job does not want, and -S stops a restart from firing a burst of catch-up runs.
Quarterly Report, Every 3 Months on the 1st
btr -T "2026/4/1,09:00" \
-r Monthsb:3:1 \
-A "Sat,Sun,Hday" \
-h "Quarterly report" \
/usr/local/bin/quarterly-report.sh
Best Practices
Check what you actually submitted:
Run btjlist -H -F "%N %H %T %r %a %w" immediately after submitting. Three of the options in this article are accepted with a wrong argument rather than refused, and the listing is the only thing that will tell you.
Set an explicit time on every repeating job:
Without -T the first run time is whenever the job happened to be submitted, and the whole schedule is built from it.
State the avoid days even when they are the defaults:
The defaults come from the help file rather than from the command, so a job that relies on them is relying on something the command line does not show. Writing -A "Sat,Sun" or -A "-" makes the intention explicit.
Choose the missed-slot action deliberately:
The default, -H, catches up run for run. For a five-minute monitoring job that is a burst of runs after every restart; use -S. For a nightly job that must happen even if it happens late, the default is right.
Use -J on repeating jobs that matter:
A repeating job stops repeating after a failure either way, but with -J its time stops advancing, which is something a listing or a monitoring script can see.
Plan for the daylight-saving changes:
Anything that repeats in days or longer moves by an hour at each transition until btdst is run. Schedule the two btdst runs as jobs rather than relying on someone remembering.
Test Monthse before you rely on it:
The target day is interpreted against the month named in -T and stored as an offset from the end of the month. Submit it, then read %T and %r back and confirm the first date is the one you expect.