说明:
指纹打卡考勤机每天的打卡记录类似:09:00 12:00 13:19 18:06,这个程序可以判断上午和下午是否正常打卡并生成考勤表。
只支持导入和导出 xls 后缀的 Excel97-2003 的文件,如果需要支持 Excel2007 的 xlsx 文件可以改程序源码中的 Excel5 为 Excel2007。
以实用为主无美感的界面:
下载地址在演示页面左上角。PHP语言,不需要数据库。
考勤机导出的 xls 文件:
中心款考勤机导出的源数据:
科大款考勤机导出的源数据:
项目部款考勤机的源数据:
程序生成的最后生成的考勤表:
一些判断标准:
迟到超过一个小时后半天按未上班,早退超过一个小时半天也不计(不计:按未上班计、不计为上过班)
当天只打了一次卡的不计,只在上班前了N次的不计,只在中午打了N次的不计,只在下班后打了N次的不计
上午正常打卡,中午打了N次,下午和下午下班未打的,不计下午
中午打了N次,下午下班打过,但是上午没打的,不计上午
上午正常打卡,中午打了一次,下午下班打了一次时,中午那次计为上午下班卡、下午未打上班卡
效果:
08:14 12:00 13:19 18:06 // output: √ √ 08:06 08:20 // output: 休 休 12:06 12:20 // output: 休 休 18:06 18:20 // output: 休 休 08:06 18:20 // output: 未退 未签 09:00 12:00 13:19 18:06 // output: 迟到 √ 09:00 11:00 13:19 18:06 // output: 迟到 未签 11:00 13:19 13:20 18:06 // output: 休 √ 08:00 11:19 13:20 18:06 // output: √ 未签 09:00 11:19 14:20 17:06 // output: 迟到早退 迟到早退 09:00 11:00 13:19 18:06 // output: 迟到 未签 13:19 18:06 // output: 休 √ 08:19 08:19 08:19 12:02 18:06 18:06 18:06 // output: √ 未签
判断当天的打卡记录是否符合标准的函数代码:
function getSignRs($str=''){ global $amStart,$amEnd,$pmStart,$pmEnd; $maxLateTime = 3600; // 最大迟到和早退时间:秒 $rs = array('AM'=>'','PM'=>''); $str = trim($str); $str = preg_replace('/\s+/', '#',$str); $record = explode('#', $str); // $record = array_unique($record); // 1分钟内多条记录合为1条(不使用该功能的原因是允许中午1分钟内按两次:一次下班,一次上班) $totalRecord = count($record); if($totalRecord < 2) return $rs; // 只有一条记录时按当天未上班计 if(!$amStart) $amStart = strtotime('09:00'); if(!$amEnd ) $amEnd = strtotime('12:00'); if(!$pmStart) $pmStart = strtotime('14:00'); if(!$pmEnd ) $pmEnd = strtotime('18:00'); $amRecord = $noonRecord = $pmRecord = array(); $beforeAm = $noon = $afterPm = 0; // 早上、中午、晚上的打卡次数 // 筛选记录到 上午 中午 下午 foreach ($record as $r) { $ts = strtotime($r); if($ts < $amEnd) { if($ts <= $amStart){ // 只记录一个上班前的记录 if(0 == $beforeAm){ $amRecord[] = $r; } $beforeAm++; }else{ $amRecord[] = $r; } } if($ts >= $amEnd && $ts <= $pmStart){ if($noon <= 2) $noonRecord[] = $r; // 只记录2个中午的记录 $noon++; } if($ts > $pmStart){ if($ts >= $pmEnd){ // 只记录一个下班后的记录 if(0 == $afterPm){ $pmRecord[] = $r; } $afterPm++; }else{ $pmRecord[] = $r; } } } if($totalRecord == $noon) return array('AM'=>'休','PM'=>'休'); // 全部是中午打的卡:无视 if($totalRecord == $beforeAm) return array('AM'=>'休','PM'=>'休'); // 全部是上班前打的卡:无视 if($totalRecord == $afterPm) return array('AM'=>'休','PM'=>'休'); // 全部是下班后的卡:无视 // 将中午的记录分给上午和下午 if(!empty($noonRecord)){ $i = 1; foreach ($noonRecord as $r) { if(1 == $i && !empty($amRecord)) // 第一条分给上午,第2条给下午,其它的扔掉 $amRecord[] = $r; else array_unshift($pmRecord,$r); $i++; if($i > 2) break; } } // t:total s:start e:end // 上午的打卡结果 if($amRecord){ $t = count($amRecord); $s = strtotime($amRecord[0]); $e = strtotime($amRecord[$t-1]); if(0 == $t){ $rs['AM'] = ''; }else if(1 == $t){ if($s <= $amStart){ if( count($pmRecord) == 1 && isset($pmRecord[0]) && strtotime($pmRecord[0]) < $pmEnd) $rs['AM'] .= '√'; // 上午打一次,下午在下班前打了一次 else $rs['AM'] .= '未退'; } if($s > $amStart && $s < $amEnd) $rs['AM'] .= ''; // 迟到未退 if($s >= $amEnd) $rs['AM'] .= ''; // 未签 或 中午打了N次,上午没上班 }else{ if($s > $amStart && $s <= $amStart + $maxLateTime) $rs['AM'] .= '迟到'; if($e < $amEnd && $e >= $amEnd - $maxLateTime) $rs['AM'] .= '早退'; if($s > $amStart + $maxLateTime) $rs['AM'] = '休'; // 太晚签到(迟到超过1小时) if($e < $amEnd - $maxLateTime) $rs['AM'] = '休'; // 太早签退(距离下班超过1小时) if(empty($rs['AM'])) $rs['AM'] .= '√'; } } // 下午的打卡结果 if($pmRecord){ $t = count($pmRecord); $s = strtotime($pmRecord[0]); $e = strtotime($pmRecord[$t-1]); if(0 == $t){ $rs['PM'] = ''; }else if(1 == $t){ if($s > $pmStart && count($noonRecord) >= 2) $rs['PM'] = ''; // 迟到未退 if($s >= $pmEnd) $rs['PM'] = '未签'; // 不能和上条行换位 if($s <= $pmStart) $rs['PM'] = ''; // 未退 或 中午多次下午未上班 }else{ if($s > $pmStart && $s <= $pmStart + $maxLateTime) $rs['PM'] .= '迟到'; if($e < $pmEnd && $e >= $pmEnd - $maxLateTime) $rs['PM'] .= '早退'; if($s > $pmStart + $maxLateTime) $rs['PM'] = ''; // 太晚签到(迟到超过1小时) if($e < $pmEnd - $maxLateTime) $rs['PM'] = ''; // 太早签退(距离下班超过1小时) if(empty($rs['PM'])) $rs['PM'] .= '√'; } } if(empty($rs['AM'])) $rs['AM'] = '休'; if(empty($rs['PM'])) $rs['PM'] = '休'; return $rs; }
# 完 #
更新:v2.0版 可以处理多个部门(多个考勤机)。