0

Using PHP 5.4.45

I've been working on a pretty simplistic timesheet to allow users to clock-in and clock-out, see their hours, etc. So far, everything works pretty smooth, but, I can't seem to figure out a way to split work days by work week. Right now, it will display every hour you've worked day by day. My thought was to create a parent Key => Value array to the last Monday (or start of week) to act as the sorting method.

 CREATE TABLE `#__handiclock` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `userId` int(10) NOT NULL,
   `location` varchar(255) DEFAULT '0.0.0.0',
   `timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
   `modId` int(10),
   `modified` TIMESTAMP,
   `state` tinyint(4) NOT NULL,
   PRIMARY KEY (`id`)
 ) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

With the current code, each clock in and out is stored in a database separately. STATE 1 is clock in, 0 is clock out. This leaves ample room for possible future states of "break" or "paid vacation" etc. By default, the system will use TIMESTAMP, but an admin can alter the hours by setting MODIFIED, so that the original time is never altered.

function parseTimesheet($info) {
    $timesheet = new TimeSheet;
    $timesheet->User = JFactory::getUser()->name;
    $timesheet->Clocks = array();
    $in = 0; $out = 0;
    $clock = new Clock;
    $clock->Time = strtotime('0000-00-00 00:00:00');
    $clock->Breaks = 0;
    $clock->Hours = 0;
    $day = "01/01/1970";
    foreach ($info as $i => $row) {
        if ($row->state == 1) {
            // Clock IN
            $day = date("m/d/Y", strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp));
            $in = strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp);
            if (array_key_exists($day, $timesheet->Clocks)) {
                $clock = $timesheet->Clocks[$day];
                $clock->Breaks += ($in - $out);
            } else {
                $timesheet->Clocks[$day] = new Clock();
                $clock = $timesheet->Clocks[$day];
            }
            $clock->Time = date("g:i A", strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp));
        } else {
            // Clock OUT
            $newday = date("m/d/Y", strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp));
            if ($day != $newday) {
                // crossed midnight, split accordingly
                $mndate = new DateTime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp);
                $mndate->setTime(12,0,0);
                $midnight = $mndate->getTimestamp();
                $clock->Hours += ($midnight - $in);
                $timesheet->Clocks[$day] = $clock;

                $_clock = new Clock; // tomorrows time
                $in = $midnight;
                $out = strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp);
                $_clock->Hours = ($out - $in);
                $timesheet->Clocks[$newday] = $_clock;
                $clock = $timesheet->Clocks[$newday];
            } else {
                $out = strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp);
                $clock->Hours += ($out - $in);
                $timesheet->Clocks[$day] = $clock;
            }
        }
    }
    //echo '<br>Parsing Complete!';
    return $timesheet;
}

The code basically splits each day, and accounts for midnight cross-overs. It automatically calculates break hours, but faults with midnight cross-overs. Not important, as this is more of an estimation factor for an admin to look deeper into.

The actual problem is trying to figure out a math function that will create a KEY same for all the days of the week, but unique for week to week.

Komak57
  • 113
  • 1
  • 3
  • 12

1 Answers1

1

I hope to help you with the code below. I have refactored the code and changed the names of the variables for readability, but the structure of the code is more or less the same.

function parseTimesheet($info) {
    $timesheet = new TimeSheet;
    $timesheet->User = JFactory::getUser()->name;
    $timesheet->Clocks = array();

    $in_timestamp = 0;
    $out_timestamp = 0;
    $in_key = NULL;
    $out_key = NULL;
    foreach ($info as $row) {
        $row_timestamp = strtotime(($row->modified != '0000-00-00 00:00:00') ? $row->modified : $row->timestamp);

        if ($row->state == 1) {
            // Clock IN
            $in_timestamp = $row_timestamp;
            $in_key = date('Y-W', $in_timestamp);

            if (isset($timesheet->Clocks[$in_key])) {
                $timesheet->Clocks[$in_key]->Breaks += ($in_timestamp - $out_timestamp);
            } else {
                $timesheet->Clocks[$in_key] = new Clock();
            }
            // I don't understand what it does, but I have copyed it from the above code
            $timesheet->Clocks[$in_key]->Time = date("g:i A", $row_timestamp);
        } else {
            // Clock OUT
            $out_timestamp = $row_timestamp;
            $out_key = date('Y-W', $out_timestamp);

            // in this way I handle also if someone work for two weeks without break...
            while ($in_key != $out_key) {
                // the way used to compute $border_timestamp depends on the interval delay chosen

                // compute the timestamp of the first day of the week after 
                // note: strtotime('2016W02') returns the timestamp of the monday of the second week of the 2016
                $border_timestamp = strtotime(date('Y\WW', $in_timestamp + 3600*24*7));
                $border_key = date('Y-W', $border_timestamp);

                $timesheet->Clocks[$in_key]->Hours += ($border_timestamp - $in_timestamp);
                $timesheet->Clocks[$border_key] = new Clock();

                // virtually update the IN clock
                $in_timestamp = $border_timestamp;
                $in_key = $border_key;
            }
            $timesheet->Clocks[$in_key]->Hours += ($out_timestamp - $in_timestamp);
        }
    }
    //echo '<br>Parsing Complete!';
    return $timesheet;
}
Roberto Trani
  • 1,217
  • 11
  • 14
  • Clock->Time registers the starting time of a day's work for admin reference. It was originally going to track the actual hours clocked in from start to end of a day, but was deemed inefficient. But yes, it looks like reading over date several times, I've completely missed that 'W' was the week of the year variable I was hoping to find! I can't directly use this code, because of the way I intend to use the KEY in the output, but it definitely clears up some future bugs I may or may not have run into. – Komak57 Apr 26 '16 at 00:19
  • Question, is there a way to change what day of the week date() start on? – Komak57 Apr 26 '16 at 01:36
  • I have rewritten the code to factorize some key points, so if you want to change the granularity of the aggregation, you need to change only 2 or 3 points. The answer to your question is: unfortunately you cannot set that parameter, but you can use a solution similar to this one: [how-to-get-weeks-starting-on-sunday](http://stackoverflow.com/questions/16057039/how-to-get-weeks-starting-on-sunday) – Roberto Trani Apr 26 '16 at 08:05