After changing the server timezone from UTC to Asia/Shanghai, a previously daily cron job stopped running. No errors in logs, no "job skipped" notice — just silence.
Manual trigger still works fine, but scheduled execution never fires.
OpenClaw's cron scheduler checks whether a job has already run today by comparing the current time against the lastRunAt field. Before the timezone switch, lastRunAt stored a UTC timestamp, e.g. 2026-03-16T20:00:00Z (which is 4:00 AM on March 17 in Beijing time).
After switching to Asia/Shanghai, the scheduler computes "today" in local time — and finds that lastRunAt maps to "today" locally. It concludes the job already ran and skips it.
No error is raised because from the scheduler's perspective, everything is working as expected.
# Check current system timezone
timedatectl status
# Inspect lastRunAt for each cron job
cat ~/.openclaw/cron/jobs.json | jq '.[] | {name, lastRunAt, timezone}'
# Find jobs missing the timezone field
cat ~/.openclaw/cron/jobs.json | jq '.[] | select(.timezone == null) | .name'
If a job has no timezone field and its lastRunAt maps to "today" in local time, this is the issue.
Before (broken config):
{
"name": "daily-report",
"schedule": "0 9 * * *",
"lastRunAt": "2026-03-16T20:00:00Z"
}
After (fixed):
{
"name": "daily-report",
"schedule": "0 9 * * *",
"timezone": "Asia/Shanghai",
"lastRunAt": null
}
Both steps are required:
1. Add "timezone": "Asia/Shanghai" explicitly
2. Reset lastRunAt to null (lets the scheduler recalculate)
1. Always declare timezone when creating cron jobs
Even if the server is already in the correct timezone, an explicit timezone field makes the config portable and immune to system timezone changes.
2. Add result verification for critical jobs
Checking exitCode isn't enough — a skipped job produces no exit code at all. After each execution, send a message to Feishu/Slack with the run time and key output.
# Append to your job script
curl -s -X POST "$FEISHU_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{\"msg_type\":\"text\",\"content\":{\"text\":\"✅ daily-report completed $(date '+%Y-%m-%d %H:%M:%S')\"}}"
3. Full audit after any timezone change
After any timezone switch, use jq to batch-check the timezone field on all jobs. Don't rely on memory to figure out which jobs are affected.