Cron scheduler timezone bug: nextRunAtMs incorrectly shifted by +8 hours in v2026.2.6
Bug Description
When updating a cron job schedule via cron.update, the calculated nextRunAtMs is incorrectly shifted by +8 hours (UTC+8 offset), causing scheduled tasks to be skipped.
Environment
- OpenClaw Version: 2026.2.6-3 (CLI) / 2026.2.6 (Desktop)
- OS: macOS Darwin 25.2.0 (arm64)
- Node.js: v25.5.0
- System Timezone: Asia/Shanghai (UTC+8)
- croner version: 10.0.1
Steps to Reproduce
- Set system timezone to
Asia/Shanghai(UTC+8) - At 21:17 CST (13:17 UTC), update a cron job with schedule:
``json``
{
"schedule": {
"kind": "cron",
"expr": "0 8,16,0 * * *",
"tz": "Asia/Shanghai"
}
}
- The expected next run should be 00:00 CST (16:00 UTC) on the next day
- But the actual
nextRunAtMsstored is 08:00 CST (00:00 UTC) - exactly 8 hours later
Expected Behavior
| Timestamp | Expected |
|-----------|----------|
| nextRunAtMs | 1770566400000 |
| Local time (CST) | 2026-02-09 00:00:00 |
| UTC | 2026-02-08 16:00:00 |
Actual Behavior
| Timestamp | Actual |
|-----------|--------|
| nextRunAtMs | 1770595200000 |
| Local time (CST) | 2026-02-09 08:00:00 |
| UTC | 2026-02-09 00:00:00 |
The difference is exactly 8 hours (28800000 ms) - the UTC+8 timezone offset.
Root Cause Analysis
I verified that the croner library calculates correctly:
const { Cron } = require('croner');
const now = new Date(1770556659456); // 21:17:39 CST
const cron = new Cron('0 8,16,0 * * *', { timezone: 'Asia/Shanghai' });
const next = cron.nextRun(now);
console.log(next.getTime()); // 1770566400000 ✓ Correct (00:00 CST)
But the Gateway's cron.update returns nextRunAtMs: 1770595200000 (08:00 CST).
This suggests the bug is in how computeNextRunAtMs() or computeJobNextRunAtMs() handles the result - possibly a double timezone conversion or treating CST midnight as UTC midnight.
Impact
- Scheduled tasks at midnight (or crossing midnight) are skipped
- Tasks run 8 hours later than expected
- Affects all users in UTC+ timezones when scheduling jobs that include hour 0
Workaround
Currently, the only workaround is to manually run missed tasks with cron.run --force.
Additional Context
- The
updatedAtMstimestamp is correct (21:17:39 CST) - Other jobs with schedules not including hour 0 appear to work correctly
- The bug was observed when changing from
kind: "every"(8h interval) tokind: "cron"expression
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗