You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Django ORM计算两日期间周末天数并调整截止日期

调整Django ORM截止日期计算逻辑,跳过周末仅按工作日计算

原代码直接将days_to_post转为时长叠加到approved_at上,会把周六、周日纳入截止日期计算,不符合仅按工作日统计的需求。以下是修改后的ORM查询逻辑,通过统计目标时间段内的周末天数,自动调整总天数来跳过周末:

from django.db.models import Case, When, F, ExpressionWrapper, IntegerField, DateTimeField, DurationField
from django.db.models.functions import ExtractWeekDay, Extract
import timezone

# 假设DEFAULT_DAYS_TO_POST是你定义的默认工作日数
Article.objects.annotate(
    # 计算不跳过周末的临时截止日期,用于确定统计周末的时间范围
    temp_due_date=ExpressionWrapper(
        F('approved_at') + Case(
            When(days_to_post__isnull=False, then=F('days_to_post') * timezone.timedelta(days=1)),
            default=timezone.timedelta(days=DEFAULT_DAYS_TO_POST),
            output_field=DurationField()
        ),
        output_field=DateTimeField()
    ),
    # 统计temp_due_date与approved_at之间的周末天数
    weekend_days=Case(
        When(
            days_to_post__isnull=False,
            then=ExpressionWrapper(
                # 总天数减去工作日数,得到周末天数
                Extract(F('temp_due_date') - F('approved_at'), 'days')
                - (
                    # 完整周的工作日数:总周数*5
                    (Extract(F('temp_due_date') - F('approved_at'), 'days') // 7) * 5
                    + Case(
                        # 情况1:起始周几 <= 结束周几,未跨周末
                        When(
                            ExtractWeekDay(F('approved_at')) <= ExtractWeekDay(F('temp_due_date')),
                            then=Case(
                                # 结束日是周末,工作日算到周五
                                When(ExtractWeekDay(F('temp_due_date')) >= 6, then=5 - ExtractWeekDay(F('approved_at')) + 1),
                                # 起始和结束都是工作日,直接算差值+1
                                When(ExtractWeekDay(F('approved_at')) <= 5, then=ExtractWeekDay(F('temp_due_date')) - ExtractWeekDay(F('approved_at')) + 1),
                                default=0,
                                output_field=IntegerField()
                            )
                        ),
                        # 情况2:起始周几 > 结束周几,跨了至少一个周末
                        default=Case(
                            # 起始日是工作日,算到周五的天数
                            When(ExtractWeekDay(F('approved_at')) <=5, then=5 - ExtractWeekDay(F('approved_at')) +1),
                            # 结束日是工作日,算从周一到结束日的天数
                            When(ExtractWeekDay(F('temp_due_date')) <=5, then=ExtractWeekDay(F('temp_due_date'))),
                            default=0,
                            output_field=IntegerField()
                        ) + (Extract(F('temp_due_date') - F('approved_at'), 'days') //7 -1)*5
                    )
                ),
                output_field=IntegerField()
            )
        ),
        default=0,
        output_field=IntegerField()
    ),
    # 计算实际需要叠加的总天数:原工作日数 + 需跳过的周末天数
    actual_days=Case(
        When(days_to_post__isnull=False, then=F('days_to_post') + F('weekend_days')),
        default=DEFAULT_DAYS_TO_POST + F('weekend_days'),
        output_field=IntegerField()
    ),
    # 最终仅按工作日计算的截止日期
    due_date=ExpressionWrapper(
        F('approved_at') + F('actual_days') * timezone.timedelta(days=1),
        output_field=DateTimeField()
    ),
    # 保留原有的截止日期时长和剩余天数计算
    due_date_duration=ExpressionWrapper(
        F('due_date') - timezone.now(),
        output_field=DurationField()
    ),
    due_days_=Extract('due_date_duration', 'days'),
)

关键逻辑说明

  1. temp_due_date:先算出不考虑周末的临时截止日期,用来划定统计周末天数的时间范围。
  2. weekend_days:通过「总天数 - 工作日数」得到时间段内的周末数量,分两种场景处理跨周情况:
    • 起始日和结束日在同一周内:直接计算中间的工作日数量。
    • 起始日和结束日跨周:拆分计算第一周剩余工作日、最后一周工作日,加上中间完整周的工作日数。
  3. actual_days:把原工作日数和需要跳过的周末天数相加,得到实际要叠加的总天数,确保跳过所有周末。
  4. due_date:用审核通过日期加上实际总天数,得到符合需求的工作日截止日期。

示例验证

如果approved_at是周五(周5),days_to_post=3:

  • temp_due_date = 周五 + 3天 = 周一
  • 这段时间内的周末天数是2(周六、周日)
  • actual_days = 3 + 2 = 5
  • due_date = 周五 +5天 = 下周三,完全符合需求。

内容的提问来源于stack exchange,提问作者Moin Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 09:47:44