Back to blog

Time Zone Changes Are Moving When Your Cron Jobs Run

In 2026 Alberta, British Columbia, and Morocco stopped changing their clocks, and each tzdata release quietly shifts when local-time cron jobs fire. Here is how to keep scheduled tasks on time.

CronGuard TeamCron Job Monitoring Experts
6 min read
Close-up of a high-density network switch panel with rows of small ports, several aqua fiber-optic cables plugged in, and orange status lights glowing along the rows

When a jurisdiction stops changing its clocks

Twice a year most of us lose or gain an hour. In 2026 a few places decided to stop. Alberta went to permanent UTC-06. British Columbia settled on permanent UTC-07. Morocco is dropping its seasonal switch and staying on UTC.

None of that showed up on a server you run. Once those rules land in your time zone database, every cron job pinned to a local clock in one of those zones fires at a different absolute time than it did the week before, and nobody touched a crontab.

What actually changed in tzdata this year

The IANA time zone database shipped three releases in the first half of 2026, each one moving real schedules.

  • 2026a, on 1 March, brought Moldova into the EU transition times it has actually used since 2022.
  • 2026b, on 22 April, recorded British Columbia's move to permanent UTC-07 after its 8 March spring-forward.
  • 2026c, on 8 July, recorded Alberta's move to permanent UTC-06 and Morocco's planned move to permanent UTC on 20 September.

One detail is worth reading twice. Alberta's change took effect legally on 18 June, but the tz maintainers model it as 1 November at 02:00, and did the same for British Columbia, so tools like CLDR could absorb a mid-year switch. Database and law disagree on purpose, and which answer your server gives depends on its tzdata version.

Why cron cares about the time zone database

Classic cron reads the wall clock. A line like 30 2 * * * means 02:30 in whatever zone the crontab runs under, which by default is the machine's local zone. You override it per table with CRON_TZ:

CRON_TZ=America/Edmonton
30 2 * * * /opt/reports/nightly.sh

That says 02:30 in Edmonton. In UTC that used to be 09:30 in winter and 08:30 in summer. Under permanent UTC-06 it is 08:30 all year. A monitor that learned the job at 09:30 UTC now sees it an hour early every winter. Nothing broke; the offset under the schedule moved.

The two DST hazards that never fully go away

Fewer transitions worldwide does not retire the old spring and autumn hazards where clocks still switch. The crontab manual is blunt about it. Non-existent times during the spring-forward gap "will never match, causing jobs scheduled during the missing times not to be run," and times that occur twice during the autumn fall-back "will cause matching jobs to be run twice." A 02:30 job in a switching zone is skipped every March and doubled every November.

Abolishing daylight saving removes those two dates in one zone. It does nothing about the offset shift a tzdata update applies wherever a rule changed, and nothing for hosts whose tzdata is stale. In that gap, two machines in one cluster can disagree about what 02:30 Edmonton means.

Other schedulers read the same database

systemd timers

A systemd timer can carry an IANA zone in the calendar expression and resolves it against the same database:

[Timer]
OnCalendar=*-*-* 02:30 America/Edmonton

Kubernetes CronJobs

The .spec.timeZone field has been stable since Kubernetes 1.27:

apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-report
spec:
  timeZone: "America/Edmonton"
  schedule: "30 2 * * *"

Without it the schedule follows the controller manager's own zone, usually UTC, and the offset comes from the control plane's tzdata, not your pod's. Setting TZ or CRON_TZ in the schedule string is rejected outright.

What to do before the next release lands

Pick UTC or a wall clock, per job

Decide whether each job belongs to a wall clock or to UTC. Anything measured in absolute time, like backups and billing cut-offs, should run in UTC, which has no transitions to trip over. Save local zones for jobs that have to line up with someone's morning.

Pin the zone and keep tzdata current

Pin the zone you mean with CRON_TZ, the zone suffix on OnCalendar, or .spec.timeZone, rather than trusting /etc/localtime. Then keep tzdata current on every host and base image. A stale container runs last year's rules while the host next to it runs this year's.

Monitor by schedule, not by the clock

Monitor by the schedule you expect, with a grace window wide enough to survive an hour of drift on a transition day. Alert on a missing check-in, not on a fixed UTC minute, and a job that shifts by an hour stops being a 3 a.m. page.

Frequently asked questions about time zone changes and cron

Why did my cron job start running an hour earlier without any change to the crontab? A tzdata update changed the UTC offset of the zone your schedule is pinned to. After Alberta moved to permanent UTC-06, a 02:30 local job that used to land at 09:30 UTC in winter now lands at 08:30 UTC all year. The crontab line never changed; the rules under it did.

Does abolishing daylight saving time make cron jobs safe from the skipped and doubled hour? Only in the zones that stopped switching. Anywhere that still changes its clocks, the crontab manual is explicit: jobs in the missing spring hour never run, and jobs in the repeated autumn hour run twice. Abolition removes those two dates for one zone and nothing for the rest of your fleet.

Should scheduled jobs use local time or UTC? Prefer UTC for anything measured in absolute time, such as backups and billing cut-offs, because UTC has no transitions and no offset surprises. Reserve local zones for jobs that must align with a person's morning, and pin that zone explicitly.

Why do two servers in the same cluster fire the same scheduled job at different times? Usually because their time zone databases are out of step. A rule change ships in a tzdata release, and until every host and image installs it, some machines apply the new offset and some the old one. The maintainers even model the 2026 Alberta and British Columbia changes as 1 November, so even an up-to-date host can differ from the law.

How does a Kubernetes CronJob decide which time zone to use? Set spec.timeZone to an IANA name such as America/Edmonton, stable since Kubernetes 1.27; without it the schedule follows the controller manager's local zone. The offset comes from the control plane's time zone data, not your pod's, and putting TZ or CRON_TZ inside the schedule string is rejected with a validation error.

Further reading


Conclusion: A time zone rule change is the one edit to your scheduling that you never make yourself. When a jurisdiction drops daylight saving or shifts its offset, the next tzdata release rewrites when every local-time job fires, on whichever servers updated first. Pin the zone you mean, keep tzdata current everywhere, and watch for the absence of a successful run, not a fixed minute on the clock.

Sources: tz database NEWS, crontab(5) manual, Kubernetes CronJob documentation.

Share

Related posts

A dark server rack lit green from within, showing rows of patch-panel ports with black and colored network cables looped between them, and a blurred second rack on the left
ReliabilityDurable Job Queues Are Replacing Cron: New Failure Modes
A monitor showing the htop process viewer, with coloured CPU and memory meters above a list of running processes
Best Practices10 Cron Job Best Practices Every Engineer Should Follow
A monitor in a dark room showing a Linux terminal with neofetch output for a Debian system: kernel version, uptime, package count, shell and CPU details next to the Debian swirl in ASCII art
DevOpsCron Alternatives Compared: systemd Timers, Celery Beat, and More

Set up your first monitor.
It'll take 30 seconds.