PHP匹配日期间隔数据并在表格动态展示考勤信息
员工考勤明细请假展示功能修复
核心问题点
现有代码的两个异常完全是日期判断逻辑错误导致,具体问题如下:
- 直接使用
d.m.Y格式字符串做日期大小比较:该格式字符顺序为「日.月.年」,字符串逐位比对时优先级为日>月>年,比如会判定01.02.2022(2月1日)早于03.01.2022(1月3日),日期先后顺序完全混乱。 - 假期区间判断逻辑完全错误:目标逻辑是判断「当前日期落在假期起止日期范围内」,现有代码写为
(($dateFrom <= $monthDate) || ($dateFrom >= $dateTo)),不仅错把逻辑与&&写成逻辑或||,第二个判断条件完全没有关联当前遍历日期,仅拿假期开始时间和结束时间比对,必然出现误判。 - 请假类型硬编码:数据库
emp_leaves表存储的请假类型是SL,代码中固定输出CL,没有读取表中实际的leave字段值。 - 循环内重复查库:在遍历每日的内层循环中重复执行考勤、请假查询,属于典型N+1查询问题,数据量上升后性能极差,也容易引发数据不一致。
修复后完整代码
<?php // 替换为你实际的数据库连接配置 $conn = new mysqli('localhost','db_user','db_password','db_name'); if ($conn->connect_error) { die("数据库连接失败: " . $conn->connect_error); } $sql = "SELECT * FROM employee_detail"; $result = $conn->query($sql); echo "<table border='1' cellpadding='5'><tr><th>EmpId</th><th>Name</th>"; $month = 1; // 测试2月时修改该值即可 $year = 2022; // 提前生成当月日期列表、当月起止日期,统一用Y-m-d格式做大小比较(该格式支持直接字符串比大小) $monthDateList = []; $monthStart = date('Y-m-01', mktime(0, 0, 0, $month, 1, $year)); $monthEnd = date('Y-m-t', mktime(0, 0, 0, $month, 1, $year)); $last_day_month = date('t', strtotime($monthStart)); for($d=1; $d<=$last_day_month; $d++) { $time=mktime(12, 0, 0, $month, $d, $year); $showDate = date('d.m.Y', $time); echo '<th>'.$showDate.'</th>'; $monthDateList[] = [ 'show' => $showDate, 'compare' => date('Y-m-d', $time), 'is_sunday' => date('w', $time) == 0 ]; } echo "</tr>"; while($row = $result->fetch_assoc()) { $empId = $row['empid']; $name = $row['name']; echo "<tr>"; echo "<td>".$empId."</td>"; echo "<td>".$name."</td>"; // 提前查询该员工当月所有考勤记录,存入数组避免循环重复查库 $attendanceMap = []; $sql1 = "SELECT * FROM emp_attendance WHERE empid=$empId AND dt BETWEEN '$monthStart' AND '$monthEnd'"; $result1 = $conn->query($sql1); while($row1 = $result1->fetch_assoc()) { $attendanceMap[$row1['dt']] = $row1; } // 提前查询该员工当月所有生效请假记录(仅查询和当月有交集的假期) $leaveList = []; $sql2 = "SELECT * FROM emp_leaves WHERE empid=$empId AND dt_to >= '$monthStart' AND dt_from <= '$monthEnd'"; $result2 = $conn->query($sql2); while($row2 = $result2->fetch_assoc()) { $leaveList[] = [ 'from' => $row2['dt_from'], 'to' => $row2['dt_to'], 'type' => $row2['leave'] ]; } // 遍历当月每一天渲染单元格 foreach($monthDateList as $dateItem) { $compareDate = $dateItem['compare']; $cellContent = ''; $hasRecord = 0; // 优先判断当天是否属于请假区间 foreach($leaveList as $leave) { // 正确区间判断:当前日期 >= 假期开始 且 当前日期 <= 假期结束 if($compareDate >= $leave['from'] && $compareDate <= $leave['to']) { $cellContent = $leave['type']; $hasRecord = 1; break; } } // 无请假则判断是否有打卡记录 if(!$hasRecord && isset($attendanceMap[$compareDate])) { $att = $attendanceMap[$compareDate]; $cellContent = "InTime:".$att['in_time']."<br/>OutTime:".$att['out_time']; $hasRecord = 1; } // 最终单元格展示逻辑 if($hasRecord) { echo "<td>".$cellContent."</td>"; } else { echo $dateItem['is_sunday'] ? "<td>Off</td>" : "<td></td>"; } } echo "</tr>"; } echo "</table>"; $conn->close(); ?>
修复效果说明
- 所有用于逻辑判断的日期统一使用
Y-m-d格式,从根源解决字符串顺序错误导致的日期判定失效问题 - 修正假期区间判断规则,严格匹配「当前日期在请假起止范围内」的逻辑,替换错误的逻辑或运算符为逻辑与
- 移除硬编码的CL标识,直接读取数据表中存储的实际请假类型,示例中empid=7的SL假期会正常展示
- 优化查询逻辑,每个员工仅查询一次当月考勤、一次当月有效假期,避免循环查库带来的性能问题和判断错误
- 调整展示优先级:请假日期优先展示请假标识,无请假则展示打卡记录,避免内容拼接混乱
- 测试验证:1月考勤中empid=7的03.01-05.01日期会正常展示SL标识;$month设置为2时,该员工1月的假期和2月无交集,不会再错误展示请假标识。
内容的提问来源于stack exchange,提问作者user2222
相关产品推荐
相关产品推荐

