Django自定义过滤器实现跨天考勤时长计算问题求助
跨天考勤时长计算解决方案
核心思路
跨天考勤的本质是将单条跨日期的考勤记录拆分为多个单日片段,分别计算每个片段的时长后求和。例如:
- 考勤记录:2024-05-20 22:00(上班)→ 2024-05-21 06:00(下班)
- 拆分后:
- 2024-05-20:22:00 → 23:59:59,时长1小时59分59秒
- 2024-05-21:00:00:00 → 06:00,时长6小时
- 总时长:7小时59分59秒
1. 修复calculate_daily_duration过滤器
替换custom_filters.py中的原有逻辑,添加跨天判断与拆分计算:
from django import template from datetime import timedelta, datetime from django.utils import timezone # 若项目启用时区,需导入 register = template.Library() @register.filter def calculate_daily_duration(check_in, check_out): if not check_in or not check_out: return timedelta(0) # 统一转为时区感知时间(若项目未启用时区可删除此部分) if timezone.is_naive(check_in): check_in = timezone.make_aware(check_in) if timezone.is_naive(check_out): check_out = timezone.make_aware(check_out) # 判断是否为同一天考勤 if check_in.date() == check_out.date(): return check_out - check_in else: # 计算当日剩余时长:从上班时间到当日23:59:59 end_of_day = timezone.make_aware(datetime.combine(check_in.date(), datetime.max.time())) duration_day1 = end_of_day - check_in # 计算次日时长:从次日00:00:00到下班时间 start_of_next_day = timezone.make_aware(datetime.combine(check_out.date(), datetime.min.time())) duration_day2 = check_out - start_of_next_day # 返回总时长 return duration_day1 + duration_day2
2. 按天拆分考勤记录(可选)
如果需要在模板中分别展示每日的考勤片段,新增一个过滤器用于拆分跨天记录:
@register.filter def split_overtime_records(check_in, check_out): records = [] if not check_in or not check_out: return records # 时区处理(同上) if timezone.is_naive(check_in): check_in = timezone.make_aware(check_in) if timezone.is_naive(check_out): check_out = timezone.make_aware(check_out) current_date = check_in.date() end_date = check_out.date() while current_date <= end_date: if current_date == check_in.date() and current_date == end_date: # 同一天的完整记录 duration = check_out - check_in records.append({ 'date': current_date, 'start': check_in, 'end': check_out, 'duration': duration }) elif current_date == check_in.date(): # 考勤起始日的片段 end_of_day = timezone.make_aware(datetime.combine(current_date, datetime.max.time())) duration = end_of_day - check_in records.append({ 'date': current_date, 'start': check_in, 'end': end_of_day, 'duration': duration }) elif current_date == end_date: # 考勤结束日的片段 start_of_day = timezone.make_aware(datetime.combine(current_date, datetime.min.time())) duration = check_out - start_of_day records.append({ 'date': current_date, 'start': start_of_day, 'end': check_out, 'duration': duration }) else: # 跨多天时的完整单日记录(如上班20号,下班22号,中间21号为完整一天) start_of_day = timezone.make_aware(datetime.combine(current_date, datetime.min.time())) end_of_day = timezone.make_aware(datetime.combine(current_date, datetime.max.time())) duration = end_of_day - start_of_day records.append({ 'date': current_date, 'start': start_of_day, 'end': end_of_day, 'duration': duration }) current_date += timedelta(days=1) return records
3. 模板中使用示例
仅展示总时长:
<p>考勤总时长: {{ attendance.check_in|calculate_daily_duration:attendance.check_out }}</p>
按天展示拆分后的考勤片段:
{% for record in attendance.check_in|split_overtime_records:attendance.check_out %} <div class="attendance-item"> <h4>{{ record.date }}</h4> <p>时段: {{ record.start|date:"H:i" }} - {{ record.end|date:"H:i" }}</p> <p>当日时长: {{ record.duration }}</p> </div> {% endfor %}
注意事项
- 若项目未启用Django时区,可删除所有
timezone.make_aware相关代码,直接使用原生datetime。 - 确保模型中的
check_in和check_out字段为DateTimeField类型,且存储的时间格式正确。
内容的提问来源于stack exchange,提问作者Markmeplease
相关产品推荐
相关产品推荐

