Cron and Timezones: UTC, Local Time, and DST Pitfalls
by Sinthuyan · April 18, 2026
Timezone bugs in cron jobs are among the most subtle production failures. The job runs — just at the wrong time, or twice, or not at all during one specific week in March. Understanding how each scheduler handles timezone is essential for any job that needs to run at a wall-clock business time.
The Default: System Timezone
Standard Unix cron evaluates expressions against the server's local timezone. Run date on the server to see which timezone it's using. Cloud VMs provisioned without explicit timezone configuration typically default to UTC. If your application team wrote the schedule expecting EST but the server is UTC, the 9 AM job runs at 2 PM (or 1 PM during EDT).
Two Schools of Thought
There are two defensible approaches to cron timezones:
- UTC everywhere: all servers run UTC, schedules are expressed in UTC. Predictable across DST transitions, consistent between environments. Downside: "9 AM Monday" requires mental conversion and must be updated when business hours extend across a DST change.
- Local time: schedules expressed in business timezone, handled by the scheduler. Easier to reason about for business hours jobs. Downside: DST transitions require careful testing to avoid skipped or doubled runs.
My recommendation: use UTC for infrastructure jobs (backups, cleanups, system maintenance), use local timezone for business-hours jobs (reports, notifications, customer-facing pipelines) — with explicit DST testing.
CRON_TZ: Per-Crontab Timezone
GNU cron (which is what most Linux systems run) supports a CRON_TZ environment variable at the top of the crontab file. Setting it changes the timezone for all entries that follow:
CRON_TZ=America/New_York— all following entries use Eastern Time- You can change it mid-file to mix timezones in one crontab
- BSD cron does not support
CRON_TZ— this is a GNU-specific extension
Verify it's working: add a test entry * * * * * date >> /tmp/cron-test.log and check that the timestamps in the log match the expected timezone.
Systemd Timers: Native DST Handling
Systemd's OnCalendar= directive accepts timezone identifiers directly in the calendar specification: OnCalendar=Mon..Fri 09:00:00 America/New_York. Systemd handles DST transitions natively — when clocks spring forward, systemd correctly skips the missing hour and does not double-fire when clocks fall back.
This is one of the strongest arguments for migrating recurring jobs from cron to systemd timers on modern Linux systems.
Kubernetes 1.27+: Per-CronJob Timezone
Kubernetes CronJob gained native timezone support in version 1.27 via the spec.timeZone field. Set it to any IANA timezone identifier:
timeZone: "Europe/London"timeZone: "Asia/Singapore"timeZone: "America/Chicago"
Before 1.27, the only option was to express the schedule in UTC and document the offset.
AWS EventBridge
EventBridge Rules evaluate all cron expressions in UTC with no timezone override. EventBridge Scheduler (the newer product) supports per-schedule timezone via the ScheduleExpressionTimezone field in the API or ScheduleExpression Timezone in CloudFormation. If you're managing time-sensitive schedules in AWS, use EventBridge Scheduler.
DST-Safe Scheduling Rules
- Avoid scheduling jobs between 1:00 AM and 3:00 AM in any timezone that observes DST
- If you must run in that window, test explicitly by calculating next run times across a DST transition date
- For jobs that absolutely cannot miss: add a monitoring alert that fires if the job hasn't run within 30 minutes of its scheduled time
UTC Offset Quick Reference
- New York: UTC-5 (EST) / UTC-4 (EDT)
- London: UTC+0 (GMT) / UTC+1 (BST)
- Berlin: UTC+1 (CET) / UTC+2 (CEST)
- Singapore: UTC+8 (no DST)
- Tokyo: UTC+9 (no DST)
- Sydney: UTC+10 (AEST) / UTC+11 (AEDT)
Regions with no DST (Singapore, Japan, most of Asia) are safe to use with literal UTC offsets. Regions that observe DST require either timezone-aware scheduler support or annual schedule reviews.