← Tech

Cron Job Silent Skip After Timezone Switch: lastRunAt Misread

2026-03-17 DevOps crontimezoneOpenClawlastRunAt

Problem

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.

Root Cause

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.

Diagnosis

# 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.

Fix

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)

Prevention

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.