Wednesday, 14 May 2014

Never calculate your own timestamps

I came across a function in a PHP application I'm working on, the function was designed to take in a year and populate an array with weeks in that year, including the start timestamp, end timestamp and description. The detail is irrelevant but if you are interested it would look something like this: -

[
  "1388966400_1389484800" => "Mon, 6 Jan, 2014 - Sun, 12 Jan, 2014",
  "1389571200_1390089600" => "Mon, 13 Jan, 2014 - Sun, 19 Jan, 2014",
  "1390176000_1390694400" => "Mon, 20 Jan, 2014 - Sun, 26 Jan, 2014"
]

When I first saw the function it achieved this by doing something like the following: -

$oneDay = 1 * 60 * 60 * 24;
$oneWeek = $oneDay * 7;
// assuming the weekStartTimestamp is set to the first Monday
$weekStartTimestamp = $weekEndTimestamp + $oneDay;
$weekEndTimestamp = $weekEndTimestamp + $oneWeek;

On the face of it this should work no problem. As can be seen by running the following PHP, as you can see we start with midnight on the 29th, add one day and get midnight on the 30th.

$timestamp = strtotime('2014-03-29');
$one_day = 1*60*60*24;
echo date('d-m-Y H:i:s', $timestamp);
echo "\n";
$timestamp = $timestamp + $one_day;
echo date('d-m-Y H:i:s', $timestamp);

// output
29-03-2014 00:00:00
30-03-2014 00:00:00

The issue arises when one day is not equal to 1*60*60*24. This might not seen obvious but when the clocks go forwards from GMT to BST, you only need to add 23 hours to get from midnight one day to midnight the next day. And likewise when the clocks go back from BST to GMT you would need to add 25 hours. The timestamps would still be for the same day, but if you are relying on the exact date and time then this would obviously cause serious issues! As shown here: -

$timestamp = strtotime('2014-03-29');
$one_day = 1*60*60*24;
echo date('d-m-Y H:i:s', $timestamp);
echo "\n";
$timestamp = $timestamp + $one_day;
echo date('d-m-Y H:i:s', $timestamp);
echo "\n";
$timestamp = $timestamp + $one_day;
echo date('d-m-Y H:i:s', $timestamp);

// output
29-03-2014 00:00:00
30-03-2014 00:00:00
31-03-2014 01:00:00 // adding a day goes to 1am!

Fortunately, PHP provides a function to handle any such calculations you need to do with timestamps. This is the strtotime function, which to quote PHP lets you "Parse about any English textual datetime description into a Unix timestamp". The example below shows how this could be used instead of adding one day as above, and produces the expected results.

$timestamp = strtotime('2014-03-29');
echo date('d-m-Y H:i:s', $timestamp);
echo "\n";
$timestamp = strtotime('+1 day', $timestamp);
echo date('d-m-Y H:i:s', $timestamp);
echo "\n";
$timestamp = strtotime('+1 day', $timestamp);
echo date('d-m-Y H:i:s', $timestamp);

// output
29-03-2014 00:00:00
30-03-2014 00:00:00
31-03-2014 00:00:00 // this time we get midnight!

The lesson to learn from this is to never try to calculate timestamps yourself! Always use the built-in functions to handle any calculations you require!