The bug that only happened after daylight saving time changed
It passed every test for months, then the clocks went back and bookings vanished for an hour. Here is the investigation, the root cause, and the checklist that came out of it.
This is a case study, names and details blurred, of a bug that taught me more about date testing than any course did. It's a good example of the worst kind of defect: one that's invisible to every test you'd normally write, sits dormant for months, and then triggers on a date the calendar chooses for you. Here's how it went.
Context
A booking system — appointments in 30-minute slots, shown in the user's local timezone. Solid feature, well-tested, live for months without a date-related complaint. The team had moved on. Then, on a Sunday in late October, support started getting reports that the 1:00–2:00am slots "didn't exist" — users couldn't book them, and a handful of existing bookings in that window had silently disappeared from the calendar view.
Symptoms
- Only the 01:00–01:59 local hour was affected.
- Only on one specific Sunday.
- Some users saw duplicate slots instead of missing ones.
- It couldn't be reproduced on Monday — by the time the team looked, the calendar rendered fine again.
That last point is what makes these bugs vicious: by the time you investigate, the conditions that triggered it are gone.
Investigation
The "can't reproduce it now" was the tell. A bug that's there on one date and gone the next isn't random — something about that date was special. October, early hours of a Sunday, one hour affected... the team realised it was the night the clocks went back: the end of daylight saving time, when 02:00 falls back to 01:00 and the 01:00 hour happens twice.
Setting a test machine's clock to that night reproduced it instantly. The calendar logic generated slots by taking midnight and adding 30 minutes repeatedly. On the fall-back night, that local hour is ambiguous — 01:30 exists twice, as two different absolute instants — and the code, working in naive local time, either skipped the repeated hour or collided the two instances on top of each other. Spring forward would have done the mirror-image damage: the skipped hour (02:00 jumps straight to 03:00) would have produced slots for a time that doesn't exist.
Root cause
Slot times were stored and computed in naive local time — wall-clock strings with no timezone offset — instead of as unambiguous absolute instants (UTC) converted to local only for display. Naive local time works perfectly 363 days a year and breaks on the two days the offset changes. The tests all ran on machines in a single timezone, on ordinary dates, so they never saw the ambiguous hour. The bug wasn't in any one function; it was in the representation.
What the tests missed
Nothing in the suite ever exercised a DST transition, a non-default timezone, or the ambiguous/skipped hour. Every test implicitly assumed "local time advances one hour per hour, always" — which is true except for the exact case that broke. The coverage looked complete because it covered all the behaviour and none of the time.
The preventive checklist
This is the reusable part — the date/time scenarios I now check on anything time-sensitive (and the timezone test-data generator helps produce the awkward inputs). It's the deeper version of the timezone item in the 12 API bugs I check first.
Date & time test scenarios
- The DST fall-back night: the repeated 01:00 hour (does it duplicate or vanish?)
- The DST spring-forward night: the skipped hour (slots for a time that doesn't exist?)
- A user in a different timezone from the server
- A user whose timezone has a non-whole-hour offset (e.g. UTC+05:30, +08:45)
- Midnight, and the day/month/year boundaries
- Feb 29 in a leap year, and Feb 29 in a non-leap year
- A timestamp round-trip: stored, retrieved, displayed — same absolute instant?
- Storage in UTC (absolute instants), local time used only for display
// RELATED QA.CODES RESOURCES
Checklist
Common Bug
// related
The checkout bug that passed every happy-path test
Every checkout test was green, but combining two discounts and a gift card drove the total negative — and issued credit. A case study in testing invariants, not just features.
The API pagination bug that looked like a frontend issue
Items vanished and duplicated on scroll; the frontend took the blame for two sprints. The cause was an unstable API sort over a non-unique key. Follow the symptom down the stack.