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

PHP优化两日期间指定星期天数统计的方案咨询

优化星期天数统计的两种高效方案

你的需求是统计两个日期之间符合指定星期编号的天数,原来的循环方法功能没问题,但日期跨度大时效率偏低,下面给你两种优化方案,都能大幅减少执行时间:

方案一:PHP数学计算优化(减少循环次数)

核心思路是先计算完整的周数,每个完整周里符合条件的天数是固定的,直接用「完整周数 × 每周符合天数」得到这部分的总数;然后只需要循环处理剩余的不足一周的天数,这样循环最多只跑7次,完全避免了几百次的循环。

代码示例:

$days = $model->days; // 逗号分隔的星期编号,比如"1,3,5,6"
$targetDays = array_map('intval', explode(',', $days));
$startDate = new DateTime($model->activity_start_date);
$endDate = new DateTime(); // 当前日期

// 计算总天数,处理开始日期晚于结束日期的情况
$interval = $startDate->diff($endDate);
$totalDays = $interval->days;
if ($totalDays < 0) {
    echo 0;
    exit;
}

// 每周符合条件的天数
$daysPerWeek = count($targetDays);
// 完整周数
$fullWeeks = intval($totalDays / 7);
$count = $fullWeeks * $daysPerWeek;

// 处理剩余的天数(最多6天)
$remainingDays = $totalDays % 7;
$tempDate = clone $startDate;
for ($i = 0; $i <= $remainingDays; $i++) {
    $currentDay = (int)$tempDate->format('N');
    if (in_array($currentDay, $targetDays)) {
        $count++;
    }
    $tempDate->modify('+1 day');
}

echo $count;

这个方法把循环次数从几百次降到最多7次,效率提升非常明显,而且逻辑清晰,容易维护。

方案二:直接用MySQL查询统计

如果你的事件数据存在MySQL数据库里,直接用SQL查询统计会更高效,不需要PHP处理日期循环。这里提供两种SQL写法:

写法1:用递归CTE生成日期范围(直观易懂)

适合MySQL 8.0及以上版本,通过递归生成日期序列,然后筛选符合星期编号的日期并计数:

SET @start_date = '2018-04-09';
SET @end_date = CURDATE();
SET @target_days = '1,3,5,6'; -- 目标星期编号(1=周一,7=周日)

SELECT COUNT(*) AS total_count
FROM (
    WITH RECURSIVE date_range AS (
        SELECT @start_date AS date
        UNION ALL
        SELECT DATE_ADD(date, INTERVAL 1 DAY)
        FROM date_range
        WHERE date < @end_date
    )
    SELECT * FROM date_range
) AS dates
WHERE FIND_IN_SET(DAYOFWEEK(date) - 1, @target_days) > 0;
-- 注:MySQL的DAYOFWEEK返回1=周日,2=周一...,减1后和你的星期编号规则匹配

写法2:数学计算的SQL(更高性能)

和PHP优化思路一致,用数学计算避免生成所有日期记录,性能最优,适合超大日期跨度的场景:

SET @start_date = '2018-04-09';
SET @end_date = CURDATE();
SET @target_days = '1,3,5,6';

-- 计算每周符合条件的天数
SET @days_per_week = (
    SELECT COUNT(*) 
    FROM (
        SELECT TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(@target_days, ',', n), ',', -1)) AS day 
        FROM (SELECT 1 n UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7) nums 
        WHERE n <= LENGTH(@target_days) - LENGTH(REPLACE(@target_days, ',', '')) + 1
    ) AS tmp
);

-- 计算总天数、完整周数、剩余天数
SET @total_days = DATEDIFF(@end_date, @start_date) + 1;
SET @full_weeks = FLOOR(@total_days / 7);
SET @remaining_days = @total_days % 7;

-- 统计剩余天数里符合条件的数量
SET @remaining_count = (
    SELECT COUNT(*)
    FROM (
        SELECT DATE_ADD(@start_date, INTERVAL i DAY) AS date
        FROM (SELECT 0 i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6) nums
        WHERE i < @remaining_days
    ) AS tmp
    WHERE FIND_IN_SET(DAYOFWEEK(date) - 1, @target_days) > 0
);

-- 输出总次数
SELECT @full_weeks * @days_per_week + @remaining_count AS total_count;

验证示例

以你提到的2018-04-9至2018-05-9为例,假设目标星期是周一、周三(编号1、3):

  • 总天数31天,完整周数4周(28天),每周符合2天,这部分是8次;
  • 剩余3天里,2018-05-07(周一)、2018-05-09(周三)符合条件,加2次;
  • 总计8+1=9次(因为2018-05-09是结束日期,需要包含),和你的示例结果一致。

内容的提问来源于stack exchange,提问作者Yasin Patel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 04:28:21