在PHP开发里,生成指定周几的周期性日期序列是很常见的需求,比如业务需要统计每周一的数据,或者定时任务需要按每周三触发,这时候就需要稳定的日期生成逻辑。这类需求的核心是先确定起始日期,再按照每周固定的间隔筛选出目标周几的日期,直到达到结束条件。

基础实现思路
生成指定周几的周期性日期序列,核心步骤分为三步:首先确定日期范围的起始和结束边界,然后遍历整个日期范围,最后判断当前遍历到的日期是否为目标周几,符合条件就加入结果集。PHP的DateTime类和相关日期函数可以很好地支撑这个逻辑。
具体实现方法
方法一:使用DateTime类循环遍历
这种方式逻辑清晰,适合新手理解,通过DateTime的modify方法逐步推进日期,再用format方法获取当前日期的周几信息做判断。
<?php
/**
* 生成指定周几的周期性日期序列
* @param string $startDate 起始日期,格式Y-m-d
* @param string $endDate 结束日期,格式Y-m-d
* @param int $targetWeekDay 目标周几,1-7对应周一至周日
* @return array 生成的日期序列数组
*/
function generateWeeklyDates($startDate, $endDate, $targetWeekDay) {
$result = [];
// 初始化起始日期对象
$currentDate = new DateTime($startDate);
$endDateObj = new DateTime($endDate);
// 如果起始日期晚于结束日期,直接返回空数组
if ($currentDate > $endDateObj) {
return $result;
}
// 遍历日期范围
while ($currentDate <= $endDateObj) {
// 获取当前日期的周几,w参数返回0-6对应周日到周六,转换为1-7对应周一到周日
$currentWeekDay = $currentDate->format('w');
$currentWeekDay = $currentWeekDay == 0 ? 7 : $currentWeekDay;
// 判断是否为目标周几
if ($currentWeekDay == $targetWeekDay) {
$result[] = $currentDate->format('Y-m-d');
}
// 推进到下一天
$currentDate->modify('+1 day');
}
return $result;
}
// 测试示例:生成2024-01-01到2024-02-01之间所有周一的日期
$dates = generateWeeklyDates('2024-01-01', '2024-02-01', 1);
print_r($dates);
?>
方法二:直接计算初始目标日期再按周推进
上面的方法需要遍历每一天,当日期范围很大时效率会稍低,这种优化方式先计算第一个符合条件的目标日期,之后每次直接加7天,减少循环次数。
<?php
/**
* 优化版生成指定周几的周期性日期序列
* @param string $startDate 起始日期,格式Y-m-d
* @param string $endDate 结束日期,格式Y-m-d
* @param int $targetWeekDay 目标周几,1-7对应周一至周日
* @return array 生成的日期序列数组
*/
function generateWeeklyDatesOptimize($startDate, $endDate, $targetWeekDay) {
$result = [];
$startDateObj = new DateTime($startDate);
$endDateObj = new DateTime($endDate);
// 起始日期晚于结束日期,返回空数组
if ($startDateObj > $endDateObj) {
return $result;
}
// 获取起始日期的周几
$startWeekDay = $startDateObj->format('w');
$startWeekDay = $startWeekDay == 0 ? 7 : $startWeekDay;
// 计算第一个目标日期和起始日期的偏移天数
$offset = $targetWeekDay - $startWeekDay;
if ($offset < 0) {
$offset += 7;
}
// 得到第一个目标日期
$firstTargetDate = clone $startDateObj;
if ($offset > 0) {
$firstTargetDate->modify("+{$offset} days");
}
// 如果第一个目标日期超出结束日期,返回空数组
if ($firstTargetDate > $endDateObj) {
return $result;
}
// 从第一个目标日期开始,每次加7天,直到超过结束日期
$currentDate = $firstTargetDate;
while ($currentDate <= $endDateObj) {
$result[] = $currentDate->format('Y-m-d');
$currentDate->modify('+7 days');
}
return $result;
}
// 测试示例:生成2024-01-01到2024-02-01之间所有周三的日期
$dates = generateWeeklyDatesOptimize('2024-01-01', '2024-02-01', 3);
print_r($dates);
?>
注意事项
- PHP的
format('w')方法返回的周几是0对应周日,6对应周六,使用时需要转换才能对应1-7的周一到周日,避免判断错误。 - 如果传入的起始日期和结束日期是字符串,需要确保格式是
DateTime类可以识别的,否则会抛出异常,建议可以在函数开头做日期合法性校验。 - 当目标周几和起始日期的周几一致时,偏移天数应该是0,不要错误地加7天,否则会漏掉第一个符合条件的日期。
- 如果需要生成无限周期的日期,可以把结束日期参数去掉,改为生成指定数量的日期,只需要修改循环条件即可。
扩展场景
如果需求是生成从今天开始未来10个每周五的日期,只需要把起始日期设为今天,结束日期设为足够远的日期,或者修改上面的优化函数,把结束条件换成计数条件即可,核心逻辑和上面的一致。
<?php
/**
* 生成从指定日期开始的N个指定周几的日期
* @param string $startDate 起始日期,格式Y-m-d
* @param int $targetWeekDay 目标周几,1-7对应周一至周日
* @param int $count 需要生成的日期数量
* @return array 生成的日期序列数组
*/
function generateNWeeklyDates($startDate, $targetWeekDay, $count) {
$result = [];
if ($count <= 0) {
return $result;
}
$startDateObj = new DateTime($startDate);
$startWeekDay = $startDateObj->format('w');
$startWeekDay = $startWeekDay == 0 ? 7 : $startWeekDay;
$offset = $targetWeekDay - $startWeekDay;
if ($offset < 0) {
$offset += 7;
}
$currentDate = clone $startDateObj;
if ($offset > 0) {
$currentDate->modify("+{$offset} days");
}
for ($i = 0; $i < $count; $i++) {
$result[] = $currentDate->format('Y-m-d');
$currentDate->modify('+7 days');
}
return $result;
}
// 测试:从今天开始生成5个每周五的日期
$today = date('Y-m-d');
$dates = generateNWeeklyDates($today, 5, 5);
print_r($dates);
?>