2026-10-15 · 8 min read
- cron
- scheduling
- timezones
Cron has a schedule clock and a process clock. The schedule clock decides when the five fields match. The process clock is whatever timezone the command inherits after it starts. Logs, filenames, and database session zones often follow the process clock, so a job can print the right local time and still have fired in the wrong hour.
The variable that selects the schedule clock is not portable. Read the manual for the cron binary on that host before you copy a preamble from another machine.
Match the variable to the daemon
cronie documents CRON_TZ as the timezone for the crontab entries. The fields are interpreted in that zone. TZ, if you set it in the same file, is exported to the command and does not by itself move the firing time. A job that formats local time with TZ can look correct while CRON_TZ is unset and the daemon is using the system zone.
Other Vixie-derived crons have used TZ for the schedule and have ignored CRON_TZ. On those hosts the opposite mistake happens: a CRON_TZ line from a cronie runbook does nothing, and a TZ line changes when the job fires. Record the package name next to the crontab.
- Put the variable on its own line above the job.
- Use an IANA name the host's zoneinfo contains.
- Keep abbreviations such as EST out of the schedule.
- Record the system zone separately from both variables.
Translate the zone when you leave cron
A Kubernetes CronJob keeps the five fields in spec.schedule and the IANA name in spec.timeZone. CRON_TZ inside the schedule string is not that field, and the expression parser will reject it or misread it. systemd timers use OnCalendar in their own grammar. Expand the intended civil time and write the timer in that grammar instead of pasting the crontab preamble.
A user crontab is five fields and a command. A system crontab inserts a user between them. Copying a line across those files shifts every column to the right.
Preview instants, then confirm a transition day
Daylight-saving transitions create a local hour that is skipped or repeated. Daemons differ on whether the job runs twice, once, or not at all in that hour. A preview of the next five runs on an ordinary day does not answer the transition day. Capture a log from the previous transition, or run a fixture when the next one is close.
Log lines may be stamped in the daemon's zone even when the table is scheduled in another zone. Convert the log to UTC before you compare it with a preview. An hour of disagreement is often the log zone, not a late job.
Write the civil time above the fields
Above the job, write the IANA zone, which variable selects it, and the civil time in one sentence. The five fields stay the executable form. Diff both when the schedule changes so a reviewer can see that the clock and the fields moved together.
Paste the five fields into Flashman's cron tool and set the timezone input to the schedule zone. The result is five UTC instants. The tool does not parse a CRON_TZ line and does not emulate every daemon's skipped-hour rule. Convert a log line in the timestamp tool, diff it with the preview, and keep the sentence in the Markdown editor next to the crontab.