求助:含英国公共假日的奇数周周五收集日期检查器开发
解决你的下一次收集日期检查器问题
看起来你已经有了一个不错的开头!让我们一步步完善这个逻辑,满足你设定的核心规则:仅在每年奇数周的周五收集,若该周包含英国银行假日则调整为周六。
第一步:明确核心逻辑细节
首先要确认几个关键点:
- 这里的「奇数周」采用ISO周数(
date("W")返回的就是ISO标准的周数,范围1-53,每周从周一开始) - 「该周包含英国银行假日」指的是周五所在的ISO周(周一至周日)内存在银行假日
第二步:完善「下一个奇数周周五」的查找逻辑
你的现有代码已经开始获取周五,但还需要循环找到下一个属于奇数周的周五。这里要注意:如果当前周是奇数,但周五已经过去了,我们需要直接找下一个奇数周的周五。
// 获取当前日期 $currentDate = new DateTime(); // 初始化下一个候选周五 $nextCollectionFriday = clone $currentDate; $nextCollectionFriday->modify('this friday'); // 如果当前周五已经过了,直接切换到下一周的周五 if ($nextCollectionFriday < $currentDate) { $nextCollectionFriday->modify('+1 week'); } // 循环筛选,找到下一个奇数周的周五 while (intval($nextCollectionFriday->format("W")) % 2 === 0) { $nextCollectionFriday->modify('+1 week'); }
第三步:添加英国银行假日检查逻辑
接下来需要判断这个周五所在的周是否包含英国银行假日。我们可以写一个辅助函数来判断某一天是否是英国银行假日,然后检查该周的所有日期是否存在假日。
辅助函数:判断日期是否为英国银行假日
function isUKBankHoliday(DateTime $date): bool { $year = $date->format('Y'); $dateStr = $date->format('Y-m-d'); // 固定假日(含周末调休规则) $fixedHolidays = [ // 新年:1月1日,周末则调至周一 (new DateTime("$year-01-01"))->modify('next monday if Saturday or Sunday')->format('Y-m-d'), // 圣诞节:12月25日,周末则调至周一 (new DateTime("$year-12-25"))->modify('next monday if Saturday or Sunday')->format('Y-m-d'), // 节礼日:12月26日,周末则调休(圣诞周六则节礼日周一,圣诞周日则节礼日周二) (new DateTime("$year-12-26"))->modify('next monday if Saturday')->modify('next tuesday if Sunday')->format('Y-m-d'), ]; // 移动假日(动态计算) // 复活节周一:复活节后的第一个周一(复活节为春分月圆后的第一个周日) $easterSunday = new DateTime(); $easterSunday->setTimestamp(easter_date($year)); $easterMonday = clone $easterSunday; $easterMonday->modify('+1 day'); $fixedHolidays[] = $easterMonday->format('Y-m-d'); // 五月初银行假日:5月第一个周一 $mayEarly = new DateTime("$year-05-01"); if ($mayEarly->format('w') !== '1') { $mayEarly->modify('next monday'); } $fixedHolidays[] = $mayEarly->format('Y-m-d'); // 五月末银行假日:5月最后一个周一 $mayLate = new DateTime("$year-05-31"); if ($mayLate->format('w') !== '1') { $mayLate->modify('previous monday'); } $fixedHolidays[] = $mayLate->format('Y-m-d'); // 八月银行假日:8月最后一个周一 $august = new DateTime("$year-08-31"); if ($august->format('w') !== '1') { $august->modify('previous monday'); } $fixedHolidays[] = $august->format('Y-m-d'); // 检查当前日期是否在假日列表中 return in_array($dateStr, $fixedHolidays); }
检查目标周是否包含银行假日
现在我们需要遍历$nextCollectionFriday所在周的所有日期,确认是否存在银行假日:
// 获取目标周五所在周的周一和周日 $weekMonday = clone $nextCollectionFriday; $weekMonday->modify('Monday this week'); $weekSunday = clone $nextCollectionFriday; $weekSunday->modify('Sunday this week'); // 遍历该周每一天,检查是否有银行假日 $hasBankHolidayInWeek = false; $currentCheckDate = clone $weekMonday; while ($currentCheckDate <= $weekSunday) { if (isUKBankHoliday($currentCheckDate)) { $hasBankHolidayInWeek = true; break; } $currentCheckDate->modify('+1 day'); } // 根据检查结果调整最终收集日期 $nextCollectionDate = clone $nextCollectionFriday; if ($hasBankHolidayInWeek) { $nextCollectionDate->modify('+1 day'); // 调整为周六 } // 输出最终结果 echo "下一次收集日期:" . $nextCollectionDate->format('Y-m-d (l)');
第四步:整合测试与优化
把上面的代码整合到一起,就可以得到完整的检查器了。如果需要处理特殊年份的额外假日(比如2023年的加冕礼假日),直接在fixedHolidays数组中添加对应日期即可。
小提示
- ISO周数的计算基于周一,和我们的需求完全匹配
- 银行假日的调休规则已经按照英国实际规定处理
- 动态计算的移动假日(复活节、五月假日等)可以适配任意年份
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

