How Cron Handles Timezones — A Practical Guide

"It worked fine in staging" is a phrase that shows up a lot in timezone bugs, because a cron expression has no idea what timezone it's supposed to mean — it just gets interpreted against whatever clock the scheduler is using. For a team spread across India and servers hosted in US or EU regions, this is one of the most common sources of "why did the job run at 3:30 AM" surprises. If you need the syntax basics first, start with our cron expression complete guide — this one assumes you already know the five fields and focuses purely on the clock they're evaluated against.

Local server time — Asia/Kolkata (IST, UTC+5:30)00:0024:0009:00UTC+5:30 offsetUTC00:0024:0003:300 9 * * * (CRON_TZ=Asia/Kolkata) = 30 3 * * * (UTC)

The core rule: cron has no built-in timezone awareness

A cron expression like 0 9 * * * just means "when the hour field equals 9 and the minute field equals 0" — according to whatever clock the cron daemon reads. On a Linux server, that's the system's configured local timezone by default. Check it with:

$ timedatectl
               Local time: Fri 2026-07-11 14:32:07 IST
           Universal time: Fri 2026-07-11 09:02:07 UTC
                 Time zone: Asia/Kolkata (IST, +0530)

# If a job says "0 9 * * *" on this server, it fires at
# 9:00 AM IST, which is 3:30 AM UTC.

Setting a specific timezone for cron jobs

Most modern cron implementations (Vixie cron, cronie, and the cron used in Debian/ Ubuntu/RHEL) support a CRON_TZ variable that overrides the timezone for entries below it, without changing the whole server's timezone:

# This entry always fires at 9:00 AM IST, regardless of server timezone
CRON_TZ=Asia/Kolkata
0 9 * * * /opt/scripts/daily-report.sh

# A later entry in the same crontab, unaffected, runs in UTC
CRON_TZ=UTC
0 0 * * * /opt/scripts/utc-midnight-job.sh

This is safer than relying on the server's system timezone, because it survives a server migration, a container rebuild, or an ops team changing the default OS timezone for unrelated reasons.

The classic failure: server timezone changes silently

  • A server gets rebuilt from a base image that defaults to UTC instead of the previous IST configuration — every "9 AM" job now fires at 2:30 PM.
  • An app migrates from a self-managed VM to a managed container platform, whose containers default to UTC regardless of the previous host timezone.
  • Daylight saving time shifts a job's real-world time by an hour on regions that observe DST (India does not observe DST, but many US/EU teams working with Indian teams do, causing coordination confusion twice a year).

The fix is always the same: don't depend on "whatever the server happens to be set to." Pin the timezone explicitly with CRON_TZ, or better, run everything in UTC and do timezone conversion in application code where it's testable.

How cloud schedulers handle timezone

  • GitHub Actions — scheduled workflows always run in UTC, with no timezone option in the YAML. A 9:00 AM IST report needs cron: '30 3 * * *' (3:30 AM UTC).
  • AWS EventBridge Scheduler — lets you set an explicit timezone per schedule (unlike the older EventBridge Rules cron, which is UTC-only), so you can specify Asia/Kolkata directly and it handles DST-aware regions correctly.
  • AWS EventBridge Rules (classic) — cron and rate expressions are always evaluated in UTC; you must convert manually.
  • GCP Cloud Scheduler — accepts an explicit timezone parameter per job, similar to EventBridge Scheduler.
  • Kubernetes CronJob — historically used the controller's local timezone; recent Kubernetes versions support an explicit timeZone field on the CronJob spec.

In-process schedulers handle it the same way, just with a code-level parameter instead of a platform setting: Node's node-cron accepts a timezone option directly (see cron vs setInterval in Node.js for how that's wired up), and Python's APScheduler accepts the same idea via BackgroundScheduler(timezone=...) — covered in cron jobs with Python's schedule library and APScheduler.

Practical recommendations

  1. Prefer running servers in UTC and setting CRON_TZ only for jobs that must align with a business-facing local time (e.g. "send report at 9 AM IST to the Mumbai team").
  2. Document the intended timezone directly as a comment next to every crontab entry — future you (and teammates) will thank you.
  3. For cloud schedulers without timezone support (GitHub Actions, classic EventBridge), always write the UTC-converted time and add a comment showing the IST equivalent.
  4. Test timezone-sensitive jobs around DST transition dates if any part of your team or infrastructure observes DST — even though India doesn't, US/EU-hosted infrastructure often does.

Common Timezone Mistakes

  • Assuming crontab times are already in UTC. Most Linux installs default to the system's local timezone, not UTC — check with timedatectl before you assume, especially on a freshly provisioned server.
  • Placing CRON_TZ below the jobs it's meant to affect. CRON_TZ only applies to crontab lines that come after it in the same file — putting it at the bottom, or after the job it's supposed to control, silently does nothing.
  • Hardcoding a UTC offset instead of an IANA zone name. Using +05:30 instead of Asia/Kolkata looks equivalent today, but offsets don't auto-adjust for DST-observing regions — a named zone does.
  • Testing a timezone-sensitive job only once, outside DST season. A job that fires correctly in July can silently shift an hour after a DST transition in November or March on any DST-observing infrastructure it touches.
  • Assuming every cloud scheduler behaves the same way. GitHub Actions and classic AWS EventBridge Rules are UTC-only; EventBridge Scheduler and GCP Cloud Scheduler accept an explicit timezone. Mixing these up produces an off-by-N-hours bug that's easy to miss in code review.
  • Not re-checking timezone after a platform migration. Moving from a self-managed VM to a managed container platform often silently resets the default timezone to UTC, shifting every "local time" job that didn't pin its timezone explicitly.

Putting It Together: A Multi-Region Schedule

A more realistic case than a single crontab line: a SaaS product with an India-facing report and a UTC-based backup, both defined explicitly so neither depends on whatever timezone the server happens to default to:

# /etc/cron.d/reports — always explicit, never relies on server default

# 9:00 AM IST daily sales report for the Mumbai team
CRON_TZ=Asia/Kolkata
0 9 * * * app /opt/scripts/daily-sales-report.sh

# Midnight UTC backup — independent of any region's business hours
CRON_TZ=UTC
0 0 * * * app /opt/scripts/nightly-backup.sh

The same explicitness applies to a Kubernetes CronJob, where recent versions support a native timeZone field instead of relying on the controller's local clock:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-sales-report
spec:
  schedule: "0 9 * * *"
  timeZone: "Asia/Kolkata"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: report
              image: myorg/daily-sales-report:latest
          restartPolicy: OnFailure

Both examples make the timezone a first-class, visible part of the schedule instead of an assumption baked into the host. If you also need to tune how often each of these fires — every 5 minutes, every 30 minutes, or hourly — see our cron expression examples for every 5, 10, 15, and 30 minutes, or build the base expression with the free Cron Expression Generator and then add the timezone yourself.

Frequently Asked Questions

Does cron use UTC or local server time?

Standard Linux cron uses the system's local timezone by default, not UTC, unless the CRON_TZ environment variable is set or the server itself is configured to run in UTC. This differs from many cloud schedulers, which default to UTC.

What timezone does GitHub Actions cron use?

GitHub Actions scheduled workflows always run in UTC. There is no way to set a different timezone in the workflow YAML, so you must calculate the UTC-equivalent time yourself, for example 9:00 AM IST becomes 3:30 AM UTC.

Can I set a specific timezone for a single cron job on Linux?

Yes, on systems using Vixie cron or cronie you can set CRON_TZ=Asia/Kolkata on its own line directly above a crontab entry, and that line will be evaluated in the specified timezone regardless of the system default.

How do I convert 9 AM IST to UTC for a cron job?

IST (Asia/Kolkata) is UTC+5:30 year-round with no DST, so subtract 5 hours and 30 minutes: 9:00 AM IST becomes 3:30 AM UTC, written as 30 3 * * * for a UTC-only scheduler like GitHub Actions or classic AWS EventBridge Rules.

Does Daylight Saving Time affect cron jobs?

It affects any job scheduled with a named timezone that observes DST, such as America/New_York or Europe/London — the job's real-world trigger time shifts by an hour twice a year. India (Asia/Kolkata) does not observe DST, so IST-only schedules are unaffected, but US- or EU-hosted infrastructure and cross-timezone coordination with Indian teams often are.

Try the Free Cron Expression Generator

Describe your schedule with a specific time in mind and double-check the generated expression against the timezone your scheduler actually runs in.

Related articles