基于Laravel与php-Carbon的日历逻辑:日期解析器开发问题
企业日程表日期解析器实现问题与解决方案思路
我正在为企业日程表开发一个日期解析器,需求是将指定日期对应到上月的同周同日;若遇特殊情况需适配合理日期。但因各月周结构存在差异,且处理31日时会出现月份错误,尝试了以下两段逻辑代码仍存在多日期解析错误的问题,希望获取正确实现思路。
尝试过的代码实现
第一段代码
public function parser($day = "2 feb 2023") { $date = Carbon::parse($day); $dayOfWeek = $date->copy()->dayOfWeek; $numberOfWeek = $date->copy()->weekNumberInMonth; $newDate = $date->copy()->subMonth()->startOfWeek(Carbon::SUNDAY)->next($dayOfWeek); $lastOfMonthNewDate = $date->copy()->subDay(1)->subMonth(1)->startOfWeek()->next($dayOfWeek); if (($date->day == 31) && ($lastOfMonthNewDate == $date->isSameMonth($lastOfMonthNewDate))) { return $lastOfMonthNewDate->copy()->subMonth(1)->startOfWeek()->next($dayOfWeek); } else if ($date->day == 31) { return $lastOfMonthNewDate; } else if ($newDate == $date->isSameMonth($newDate)) { return $newDate->copy()->subMonth(1)->startOfWeek()->next($dayOfWeek); } else { return $newDate; } }
更新后的第二段代码
public function parser2($day){ $date = Carbon::parse($day); $dayOfWeek = $date->copy()->dayOfWeek; $date4Week = $date->copy()->subWeek(4); $date5Week = $date->copy()->subWeek(5); $currentWeek = $date->copy()->weekNumberInMonth; $prevWeek = $date->copy()->subWeek(4)->weekNumberInMonth; if ($date->isSameMonth($date4Week) == 1) { if ($currentWeek != $date5Week->copy()->weekNumberInMonth) { return $date->subMonth(); }else{ return $date4Week->copy()->subWeek(); } }else { if ($prevWeek != $currentWeek) { return $date5Week; }else{ return $date4Week; } } }
正确实现思路
核心逻辑定义
先明确「同周同日」的业务规则:指原日期是当月第N个星期X,目标是找到上月对应的第N个星期X;如果上月没有第N个星期X,则取当月最后一个星期X。
步骤拆解
- 提取关键信息:获取原日期的星期几(
dayOfWeek)和当月周序号(weekOfMonth),注意可根据业务调整周起始日,比如设置周日为一周开头:Carbon::setWeekStartsAt(Carbon::SUNDAY)。 - 判断上月周数匹配度:计算上月的总周数,若原日期的周序号≤上月总周数,直接取上月对应周的星期X;若超过,则取上月最后一个星期X。
- 自动适配特殊日期:像31日这类大日期无需单独判断,通过周序号逻辑就能自动适配到上月合理日期。
参考实现代码
public function parser($day = "2 feb 2023") { // 按需设置周起始日,示例为周日开头 // Carbon::setWeekStartsAt(Carbon::SUNDAY); $date = Carbon::parse($day); $targetDayOfWeek = $date->dayOfWeek; $targetWeekNumber = $date->weekOfMonth; // 上月起始与结束日期 $prevMonthStart = $date->copy()->subMonth()->startOfMonth(); $prevMonthEnd = $prevMonthStart->copy()->endOfMonth(); // 上月总周数 $prevMonthTotalWeeks = $prevMonthEnd->weekOfMonth; if ($targetWeekNumber <= $prevMonthTotalWeeks) { // 取上月第N个星期X $result = $prevMonthStart->copy()->startOfWeek()->addWeeks($targetWeekNumber - 1)->next($targetDayOfWeek); } else { // 取上月最后一个星期X $result = $prevMonthEnd->copy()->startOfWeek()->previous($targetDayOfWeek); // 若上月最后一天正好是目标星期,直接取该日期 if ($prevMonthEnd->dayOfWeek === $targetDayOfWeek) { $result = $prevMonthEnd; } } return $result; }
测试验证案例
- 2023年3月31日(周五,当月第5个周五)→ 2023年2月24日(2月仅4个周五,取最后一个)
- 2023年2月2日(周四,当月第1个周四)→ 2023年1月5日(1月第1个周四)
- 2023年1月31日(周二,当月第5个周二)→ 2022年12月27日(12月最后一个周二)
内容的提问来源于stack exchange,提问作者XxnumbxX
相关产品推荐
相关产品推荐

