Every engineer meets it eventually. A system that ran perfectly for months suddenly misbehaves on a single weekend in spring or autumn — reports off by an hour, scheduled jobs firing at the wrong moment, timestamps that no longer line up. The cause is almost always the same, and it is one of the oldest and most humbling traps in software: time. Programmers tend to assume that handling dates and times is trivial, a solved problem, beneath their attention. It is not. Time is one of the genuinely hard things in computing, and the bugs it produces are subtle, seasonal, and remarkably persistent. This is a working engineer's account of why, and how to stop getting caught.
Why time is deceptively hard
The trouble begins with a false intuition. We live inside time and treat it as simple, continuous and orderly, so it feels like it should be simple to model in code. But the time humans actually use is not a clean physical quantity — it is a tangle of political and astronomical conventions layered on top of each other, and every one of those layers is a place for a bug to hide. A "date and time" as a person understands it carries an enormous amount of hidden context that code has to get right.
Consider what is quietly bundled into an ordinary local time like "2:30 on Sunday." Which time zone is it in? Is daylight saving in effect? Was there a clock change that day that makes 2:30 ambiguous or nonexistent? Different regions apply different rules, those rules change over time as governments legislate, and the calendar itself has irregularities. None of this is visible when you glance at a clock, but all of it matters the moment you compare two times, store one, or schedule something. Time looks like a number and behaves like a legal document.
The classic failures
The bugs that result cluster into a few recurring shapes, and recognising them is half the battle. The most famous is the daylight saving transition, which is exactly why so many systems break twice a year. When clocks spring forward, an hour of local time simply does not exist; when they fall back, an hour repeats. Code that assumes every local time occurs exactly once, and that days are always twenty-four hours long, quietly produces wrong answers on precisely those two weekends — a scheduled task that runs twice, or not at all, or a duration that comes out an hour short.
The other classic failure is confusing local time with a fixed reference. When a timestamp is stored or sent without recording which zone it belongs to, it becomes ambiguous the instant it leaves the machine that made it: the same "14:00" means different actual moments in different places. Systems that pass naive local times between components, or store them without zone information, accumulate errors that only surface when data crosses a boundary. And underneath it all sit smaller landmines — leap years, and the leap seconds occasionally inserted to keep clocks aligned with the earth's rotation — that break naive arithmetic assuming time flows in perfectly regular units. It is the same category of quiet, assumption-driven wrongness we examined in the reason floating-point math quietly lies to your code.
How to actually handle time
The good news is that decades of pain have produced a set of practices that make time manageable, and they are not complicated — just disciplined. The single most important rule is to store and compute in a fixed, universal reference, typically UTC, and to convert to a user's local time only at the very edge, when you display it. Internally, everything should be an unambiguous moment; the messy business of zones and daylight saving is confined to the presentation layer, where a human actually needs to see a local clock.
The second rule follows from the first: never store or transmit a time without its zone context, so that a moment is never left ambiguous as it moves through the system. And the third is to lean on well-maintained date-and-time libraries rather than rolling your own arithmetic. This matters more than it sounds. The rules for time zones and daylight saving are constantly changing as governments alter their policies, and reputable libraries track those changes through a shared, updated database so your code does not have to. Writing your own date math is one of the most reliable ways to reintroduce every bug the field has already solved. Correct time handling, like the caching layers that make slow systems feel instant, is mostly a matter of using the right well-worn tool in the right place.
Respecting the hardest easy problem
Time endures as a source of bugs because it sits in a peculiar blind spot: it is complex enough to be genuinely difficult, yet familiar enough that engineers underestimate it until it bites. The developer who treats dates as trivial ships the seasonal bug; the one who respects the problem stores in UTC, keeps zone context, converts only at the edges, and trusts a good library to know when Portugal last changed its clocks. That is nearly the whole discipline, and it is unglamorous precisely because getting it right means nothing visibly happens — no report drifts, no job misfires, no weekend gets ruined.
The deeper lesson is a good habit for craftsmanship generally: the problems that look easiest are often the ones that punish overconfidence hardest. Time is the canonical example, a domain where humility and a couple of firm rules beat cleverness every time. Handle it with the respect it has earned from every engineer it has ever embarrassed, and the twice-a-year bug that catches everyone else will simply pass your system by.
Frequently asked questions
Why does software break during daylight saving changes? Because clock changes make local time irregular: in spring an hour vanishes, in autumn an hour repeats. Code that assumes every local time happens exactly once and that days are always 24 hours long produces wrong results on those two weekends — scheduled jobs running twice or not at all, durations off by an hour.
Should I store times in UTC or local time? Store and compute in UTC (or another fixed universal reference), and convert to the user's local time only when displaying it. Keeping everything internal as an unambiguous moment avoids the errors that arise when local times, which depend on zone and daylight-saving rules, are passed around or stored.
Why not write my own date handling code? Because time zone and daylight-saving rules change constantly as governments alter policies, and well-maintained libraries track those changes through a shared, updated database. Rolling your own date arithmetic reintroduces bugs the field has already solved and leaves you responsible for rules that keep shifting.


