Question

I want to convert times to rounded times.
Therefore I only want to use intervals in seconds. It works well for quarter hours and full hours. But not for days. Then it is messed up. But I can't understand why

This is my function:

function formatToMySQLTime($sTime,$iInterval){
    switch ($iInterval){
        case 1: // 15 minutes;
            $iDivider = 15 * 60;
            break;
        case 2: // 1 hour
            $iDivider = 60 * 60;
            break;
        case 3: // 1 day
            $iDivider = 60 * 60 * 24;
            break;
    }    
    $iRemainder = strtotime($sTime) % $iDivider;
    $iRoundTime = strtotime($sTime) - $iRemainder;    
    $sTime = date('Y-m-d H:i:s', $iRoundTime);
    return $sTime;
}

Here is the output:

echo formatToMySQLTime('2013-10-21 03:23:00',1);
2013-10-21 03:15:00

echo formatToMySQLTime('2013-10-21 03:23:00',2);
2013-10-21 03:00:00

echo formatToMySQLTime('2013-10-21 03:23:00',3);
2013-10-21 02:00:00

The third output is wrong, it should be 2013-10-21 00:00:00. What is my mistake?

Was it helpful?

Solution

You have timezone issue, because strtotime function is not timezone aware, and date is. Instead of setting global timezone, you can set it locally with appending UTC string to the $sTime variable, and instead of using date() use gmdate():

// it doesn't matter in which timezone your are, function works with UTC
date_default_timezone_set('Europe/Berlin');

function formatToMySQLTime($sTime, $iInterval) {
    switch ($iInterval){
        case 1: // 15 minutes;
            $iDivider = 15 * 60;
            break;
        case 2: // 1 hour
            $iDivider = 60 * 60;
            break;
        case 3: // 1 day
            $iDivider = 60 * 60 * 24;
            break;
    }
    $U = strtotime($sTime . ' UTC');
    $iRoundTime = $U - $U % $iDivider;    
    return gmdate('Y-m-d H:i:s', $iRoundTime);
}
    
echo formatToMySQLTime('2013-10-21 03:23:00',1);
echo formatToMySQLTime('2013-10-21 03:23:00',2);
echo formatToMySQLTime('2013-10-21 03:23:00',3);

Run code.


Update:

Instead of modifying function for every interval, you can make function a little more generic and readable, with the help of DateTime classes, like:

function formatToMySQLTime($sTime, $iInterval) {
    $dt = new DateTime($sTime, new DateTimezone('UTC'));
    $U = $dt->getTimestamp();

    $dt2 = clone $dt;
    $interval = DateInterval::createFromDateString($iInterval);
    $iDivider = $dt2->add($interval)->getTimestamp() - $U;
    
    $dt->setTimestamp($U - $U % $iDivider);
    return $dt->format('Y-m-d H:i:s');
}

echo formatToMySQLTime('2013-10-21 03:23:00', '15 minute');
echo formatToMySQLTime('2013-10-21 03:23:00', '1 hour');
echo formatToMySQLTime('2013-10-21 03:23:00', '1 day');

Run code.

OTHER TIPS

What is my mistake?

The whole approach is flawed.

If you check your $iRemainder, you’ll see it contains a date value like Thu, 01 Jan 1970 23:01:48 +0100 – and subtracting that from your original value and adding “a days worth of seconds” again of course gets messy with the different times caused by DST.

When working with unix timestamps, assuming a day would always consist of a certain amount of seconds is just plain wrong – because of things like DST.

Btw., you are not actually “rounding”, based on your examples – for the first two, you are rounding up to the next 15 minutes or full hour, but for the third one you want to round down to the day – what’s the logic behind that?

I’d suggest you look at the necessary parts of the date individually, and then return a formated date with the rest just set to matching value manually.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top