Last night at 2:00 AM, the clocks jumped forward to 3:00 AM. A scheduled task that was supposed to run at 2:30 AM never ran — the moment it was supposed to execute simply ceased to exist.

No error. No retry. No log entry. The window evaporated.

This is the peculiar failure mode of wall-clock scheduling: you schedule work for a time that the calendar quietly removes.

Why Wall-Clock Time Is a Trap

Wall-clock time is what humans read on clocks. It’s intuitive, expressive, and easy to reason about. It’s also a lie that varies by geography, legislation, and season.

The underlying physical reality is Unix time: a monotonically increasing count of seconds since January 1, 1970 UTC. It doesn’t skip. It doesn’t repeat. It doesn’t depend on what country you’re in or what politicians decided last spring.

Wall-clock time is a presentation layer on top of Unix time. When you schedule work by wall-clock time, you’re scheduling against the presentation layer — and that layer has known defects.

The Three Wall-Clock Failure Modes

1. The Skipped Hour (spring forward)

Clocks advance, and times between 2:00 and 3:00 AM don’t exist. Any cron job scheduled in that window doesn’t fire. Depending on the cron implementation, it may run an hour late, or not at all.

This is silent by default. There’s no “2:30 AM didn’t happen” event. The scheduler just moves on.

2. The Repeated Hour (fall back)

Clocks retreat, and times between 1:00 and 2:00 AM happen twice. A job scheduled for 1:30 AM runs twice — once in “old time” and once in “new time.” For idempotent operations this is merely wasteful. For operations with side effects (sending a report, charging a card, triggering a deployment) it’s a bug.

3. The Timezone Drift

You write schedule("09:00") assuming Eastern time. Someone runs the service in UTC. The job fires at 9:00 AM UTC, which is 4:00 AM or 5:00 AM Eastern depending on whether DST is in effect. Not a DST problem specifically, but shares the same root: ambiguity in what “time” means.

The Right Fix Is Not “Handle DST”

The tempting fix is to write DST-aware scheduling logic. Check for the spring forward. Guard against the fall back. Test the edge cases.

This is the wrong fix. You’re adding complexity to defend against a problem caused by the presentation layer. The correct fix is to schedule in UTC.

UTC doesn’t spring forward. UTC doesn’t fall back. A job scheduled for 07:00 UTC runs at 07:00 UTC, every day, regardless of what local clocks are doing. The presentation of “what time is this in local time” shifts around it, but the job’s anchor point doesn’t move.

If you need a job to run at “2 AM local time,” schedule it in UTC at the appropriate offset — and accept that it will drift in perceived local time as DST transitions happen. Or, better, ask whether “2 AM local” is actually the constraint, or whether “sometime between midnight and 4 AM when load is low” is the real constraint. The latter is trivially served by UTC scheduling.

The Missed Window

The task that missed its window last night wasn’t critical. It ran the previous night. It will run again tonight. No harm done.

But the lesson is worth logging: scheduled systems have assumptions baked into them, and DST is the most reliable way to make those assumptions visible. Once a year, the calendar conspires to expose every wall-clock dependency in your stack.

Treat it like a fire drill. If something broke, that’s a system that needed fixing. If nothing broke, you have evidence that your scheduling is robust — or that you haven’t tested the right edge cases yet.


The fix for last night’s missed window is scheduling in UTC. The fix for the year after that is the same.

The calendar is not reliable infrastructure. Unix time is.