~/blog / dont-add-86400-seconds.md
Don't add 86,400 seconds to get the next day
It looks harmless: a day has 86,400 seconds, so to loop over a range of days, keep adding 86,400 to a timestamp.
date_default_timezone_set('Europe/Berlin');
$date = strtotime('2009-10-23 00:00:00');
$end = strtotime('2009-10-26 00:00:00');
while ($date < $end) {
echo date('Y-m-d', $date), PHP_EOL;
$date += 86400;
}
Expected output:
2009-10-23
2009-10-24
2009-10-25
Actual output:
2009-10-23
2009-10-24
2009-10-25
2009-10-25
On 25 October 2009, clocks in Europe went back one hour when daylight saving time ended, so that day was 25 hours long. Midnight plus 86,400 seconds lands at 23:00 on the same calendar day. The day is printed twice, and from then on every timestamp is one hour off. In spring the opposite happens: the day is 23 hours long, and a day can be skipped.
The bug only appears in time zones with daylight saving time, and only two nights a year. That is why it so often survives testing and shows up in production reports.
Do this instead
Let PHP do calendar arithmetic. Add a calendar day, not a fixed number of seconds:
$period = new DatePeriod(
new DateTimeImmutable('2009-10-23', new DateTimeZone('Europe/Berlin')),
new DateInterval('P1D'),
new DateTimeImmutable('2009-10-26', new DateTimeZone('Europe/Berlin'))
);
foreach ($period as $day) {
echo $day->format('Y-m-d'), PHP_EOL;
}
Or, for a single step:
$next = $date->modify('+1 day'); // with DateTimeImmutable
Two more rules prevent most date bugs:
- Store timestamps in UTC and convert to the user's time zone only when displaying them.
- Never assume that a day has 24 hours, a month has 30 days or a year has 365 days. Use
DateInterval,DatePeriodor a library such as Carbon.