1

I've made an program in PHP that stores in a MySQL database when it's working. The table that shows whether the program is working or not looks like this:

Table_name: downtime
Working(int)    Time(datetime)
1               2011-05-06 15:18:55
0               2011-05-06 15:20:21
1               2011-05-06 15:20:24
0               2011-05-06 16:05:13
1               2011-05-06 16:05:15
0               2011-05-06 16:27:59
1               2011-05-06 16:28:01
0               2011-05-06 16:37:35
1               2011-05-06 16:37:37

Now I would like to retrieve the 'downtime' between to dates. Say date 1 is 2011-05-06 15:20:24 and date 2 is 2011-05-06 16:28:15, then I would like the script to return the sum of the differences between the 1's and 0's in that period. How can I make this?

Important to know if when date 1 for example is 2011-05-06 15:20:22, then there are two extra seconds downtime. It's hard to think of a (almost) perfect solution.

Kara
  • 6,115
  • 16
  • 50
  • 57
Simon
  • 5,464
  • 6
  • 49
  • 85

5 Answers5

2

This code should work, I've tested it with your example data:

// Example data
// Note: assumes $data is sorted by date already
$data = array(
    "2011-05-06 15:18:55"=>1,
    "2011-05-06 15:20:21"=>0,
    "2011-05-06 15:20:24"=>0,
    "2011-05-06 16:05:13"=>0,
    "2011-05-06 16:05:15"=>0,
    "2011-05-06 16:27:59"=>1,
    "2011-05-06 16:28:01"=>0,
    "2011-05-06 16:37:35"=>0,
    "2011-05-06 16:37:37"=>1
    );

$startTime = strtotime("2011-05-06 15:18:55");
$endTime = strtotime("2011-05-06 16:28:01");

$totalUptime = 0;    
$lastUptime = 0;

foreach ($data as $dateString => $isWorking)
{
    $timestamp = strtotime($dateString);

    if ($lastWorking && $lastTimestamp)
    {
        $allowedStart = $lastTimestamp;
        if ($allowedStart<$startTime)
            $allowedStart = $startTime;

        $allowedEnd = $timestamp;
        if ($allowedEnd>$endTime)
            $allowedEnd = $endTime;

        $diff = $allowedEnd-$allowedStart;

        if ($diff>0)
        {
            $totalUptime += $diff;
        }
    }

    if ($timestamp>=$endTime)
    {
        break;
    }

    $lastTimestamp = $timestamp;    
    $lastWorking = $isWorking;
}

print "Total uptime: ".$totalUptime." seconds";

You just need to populate $data with your own records from the database.

Oliver Emberton
  • 951
  • 4
  • 14
  • What if I have 10000 records or so? How should I select them – Simon May 06 '11 at 17:56
  • 1
    This code will work as long as you have the records within the date range you're testing, plus one record earlier and later. So I suggest just querying the date range plus a bit (an hour? a day? - depends on how frequently you update). An extra few records won't slow down your script noticeably. – Oliver Emberton May 06 '11 at 18:11
1

Go through all the rows converting the timestamps to unix epochs with strtotime(), then just find the difference between them, and add it up to get total down time.

JohnD
  • 3,884
  • 1
  • 28
  • 40
  • What would this look like in PHP? It's hard to find the correct code that be the question's answer – Simon May 06 '11 at 17:25
1

The solution I am using now is the following:

    $objects = Get("table","`date` BETWEEN '".$date_start."' AND '".$date_end."'");   

    $prev = new stdClass();
    $prev->new = 1;
    $prev->date = $date_start;

    $next = new stdClass();
    $next->new = 0;
    $next->date = $date_end;
    $objects = array_merge(array($prev),$objects,array($next));

    $uptime = 0;
    $downtime = 0;
    $prevtime = false;
    foreach($objects as $object)
    {
        $newtime = strtotime($object->date);
        if($prevtime && $object->new == 1)
        {
            $downtime += $newtime - $prevtime;
        }
        elseif($prevtime && $object->new == 0)
        {
            $uptime += $newtime - $prevtime;
        }
        $prevtime = $newtime;
    }     

Get(); is my function to communicate to the database: it returns objects (records) found by the query, replace it with the following if you want to test/use this code:

$objects = array();
$result = mysql_query("SELECT * FROM table WHERE `date` BETWEEN '".$date_start."' AND '".$date_end."'");
while($object = mysql_fetch_object($result))
{
    $objects[] = $object;    
}
Simon
  • 5,464
  • 6
  • 49
  • 85
0
select timediff(
(
    select `time`
    from downtime
    where working = 1
    and `time` > t1.`time`
    order by `time` asc
    limit 1
),
t1.`time`
)
from downtime t1
where t1.working = 0

This should give you several rows, one for each period of downtime. Each row shows a time difference and you would still have to add those rows together to find the actual downtime. I'm not sure what happens when you use the sum() function of MySQL on time differences, it probably just returns seconds. You could limit the time field as desired, though I have not tested what happens when start time or end time is in a downtime period.

Arjan
  • 9,784
  • 1
  • 31
  • 41
-1

Edit: I misunderstood the question, ignore this answer

The answer depends very much on how you've stored that data in MySQL. You'll most likely need a query like:

SELECT COUNT(*) FROM table WHERE working='1' AND DATE>='startDate' AND DATE<='endDate';

Which will give you the number of '1's in a given range.

Oliver Emberton
  • 951
  • 4
  • 14
  • The MySQL table is in the quetion, so that's the way it's stored;) – Simon May 06 '11 at 16:50
  • You voted me down for _answering your question_!? Your question doesn't stipulate that is an extract from your MySQL, or the datatype of the columns. In any case, my answer should work. – Oliver Emberton May 06 '11 at 16:54
  • 3
    I downvoted you because you didn't answer his question. You're getting a total amount of rows where working is 1 in a range, not calculating the total amount of downtime as was asked. – JohnD May 06 '11 at 16:56
  • @oliver: you're just counting the 'up' points anyways, OP wants actual uptime. The machine may have seen as up 5 times, but the real answer is "was up for 15 hours 10 minutes 3 seconds" type thing. – Marc B May 06 '11 at 16:58
  • I'm not sure what you're expecting. The SQL I specified is about as specific as I can get without knowing more (e.g. what your table is called) or writing the PHP to do the database query itself, which I'm guessing you already know. All you need to do is count the number of rows with Working set to 1 between two dates, which is what that SQL does. – Oliver Emberton May 06 '11 at 16:59
  • @Oliver Once again that's not what he asked for. – JohnD May 06 '11 at 17:00
  • 1
    @JohnD / @Kevin - apologies, I see what you mean now. – Oliver Emberton May 06 '11 at 17:00
  • @Kevin - I've tried to answer your question properly now, sorry for the misunderstanding. – Oliver Emberton May 06 '11 at 17:35