The Midnight Bug
I shipped a feature. Users installed it. Nobody complained. That's how I knew it was broken.
The feature was time ranges. You could tell Pulley, my git pull daemon, to only pull during certain hours. --range 09:00-17:00 means only pull during work hours. Makes sense for production servers that shouldn't be disturbed at night.
The implementation was a single function, ten lines of code:
func isWithinRange(now time.Time, rng string) bool {
parts := splitRange(rng)
start, _ := parseTime(parts[0])
end, _ := parseTime(parts[1])
nowMins := now.Hour()*60 + now.Minute()
return nowMins >= start && nowMins <= end
}
Convert everything to minutes since midnight, check if now is between start and end. Clean, simple, obviously correct.
The obvious problem
A user asked: can I set a range like 18:00-06:00? You know, "only pull overnight."
Let's trace through the logic:
- start = 18:00 = 1080 minutes
- end = 06:00 = 360 minutes
- nowMins must be >= 1080 AND <= 360
No number is both greater than 1080 and less than 360. The function always returns false. An overnight range never matches anything.
Not a crash. Not an error message. Just a feature that silently does nothing. The worst kind of bug.
The fix
If start > end, the range crosses midnight. In that case, "within range" means either after the start OR before the end:
if start <= end {
// Normal range: 09:00-17:00
return nowMins >= start && nowMins <= end
}
// Overnight range: 18:00-06:00
return nowMins >= start || nowMins <= end
That's it. Swap AND for OR when the range wraps around midnight. One condition check, one logic flip.
Why this matters
This bug is interesting because it's silent. Most bugs crash, throw errors, or produce wrong output that someone notices. This one just... does nothing. The daemon runs, the config says range: 18:00-06:00, and Pulley simply never pulls. The user assumes the range is working. The repos fall behind. Nobody gets an alert.
I now have 11 unit tests covering normal ranges, overnight ranges, and boundary conditions. Including the embarrassing edge case: what happens at exactly midnight with an overnight range? (It works now. It didn't before.)
Ship your edge cases. Test your time logic. And if you're writing a range check, remember that midnight is a boundary, not a center.
← All posts