← BLOG

Cron job running at the wrong time: fix UTC and DST errors

A cron expression has no useful meaning until its timezone is known. Check the clock inside the running process, then pin an IANA timezone or schedule explicitly in UTC.

RUNNING APPS · 19 AUGUST 2026 · 5 MIN READ

A cron job that runs at the wrong hour is usually interpreting the expression in a different timezone than you expected. Print the time inside the deployed process, identify which scheduler owns the expression, then set an explicit IANA timezone or convert the schedule to UTC.

First, observe the clock that runs the job

Your laptop clock is irrelevant once the code is deployed. Log both an ISO timestamp and the timezone reported inside the process.

JS
console.log({
  iso: new Date().toISOString(),
  zone: Intl.DateTimeFormat().resolvedOptions().timeZone,
});
Run this in the same process that owns the schedule.

If the ISO time is correct but the displayed local time differs, the clock is fine. The assumption about timezone is wrong. Cloud containers commonly use UTC because it is stable and has no daylight-saving shift.

Find which layer owns the schedule

SCHEDULERWHERE TIMEZONE IS SETCOMMON MISTAKE
Linux cronSystem timezone or CRON_TZ when supportedEditing the expression as if the server used laptop time
Platform cronProvider setting or documented defaultAssuming every provider uses local time
node-cronThe timezone option in codeOmitting it and inheriting the runtime default
Python schedulerTrigger or scheduler configurationUsing a naive datetime without a zone
Database schedulerDatabase or extension configurationComparing UTC rows with local calendar dates

Do not fix the wrong layer. Changing the container timezone will not alter a platform scheduler that runs outside the container. Editing a platform cron will not change node-cron running inside your app.

Pin business time in code

For a report that must run at 9:00 AM in Bangkok, state that timezone next to the expression. node-cron v4 accepts an IANA timezone and can prevent overlapping runs.

JS
import cron from 'node-cron';

cron.schedule('0 9 * * 1-5', runReport, {
  timezone: 'Asia/Bangkok',
  noOverlap: true,
});
The schedule now says which 9:00 AM it means.

Use names such as Asia/Bangkok or America/New_York, not fixed abbreviations such as EST. An IANA zone carries the historical and daylight-saving rules. A numeric offset does not.

Choose what should happen at daylight-saving changes

REQUIREMENTSAFER SCHEDULE
Run every fixed elapsed intervalUse UTC or an interval timer
Run at a local business hourUse an IANA timezone
Run once per local calendar dayStore the local date already processed
Run during the clock-change windowMake the job safe to skip or repeat

When a clock moves forward, some local times do not exist. When it moves backward, some occur twice. Scheduler behavior varies, so a money-moving or notification job should be idempotent: record a business key such as report:2026-08-19 and refuse to process it twice.

If the hour is right but the date is wrong

The scheduler may be correct while the query calculates "today" in UTC. A job that fires at 00:15 Bangkok time still runs on the previous UTC date. Pass an explicit business date and timezone into the job instead of deriving both from the server clock halfway through the task.

How this behaves on Spocket

Spocket cron workloads are always-on processes, and UTC is the default unless your code sets another timezone. The schedule lives in your repository rather than a separate dashboard field. A restart brings the scheduler back, but a missed run is not replayed automatically.

Use /run/cron for the hosting path and /documentation/logs-and-status to inspect actual run timestamps. If your task can exceed its interval, add overlap protection before deploying it.

Need somewhere to put it?

Spocket runs apps, workers and scrapers that have to stay awake. Deploy from Claude or Cursor by asking, from $3/mo. First app is free for 7 days, no card.

Cron job hostingRead the quickstart
Spocket
BlogDocsTermsPrivacyHome