Search code examples
phplaravellaravel-4php-carbon

Get the number of weeks in a month and the corresponding days


I'm pretty sure I'm overcomplicating this but atm I have no clue how to do it in another way.

I want to create an array which contains the number of a week in a month as a key and the days of this week as the value.

I'm using this function to get all days of a week:

public function getDaysOfWeek($year, $month, $day)
{
    $firstMondayThisWeek = new DateTime($year . '/' . $month . '/' . $day, new DateTimeZone("Europe/Berlin"));
    $firstMondayThisWeek->modify('tomorrow');
    $firstMondayThisWeek->modify('last Monday');

    $nextSevenDays = new DatePeriod(
        $firstMondayThisWeek,
        DateInterval::createFromDateString('+1 day'),
        6
    );

    return $nextSevenDays;
}

And this function to build the array:

public function getWeekAndDays($year, $month)
{
    $weeksInMonth = Carbon::createFromDate($year, $month)->endOfMonth()->weekOfMonth;   
    $weekBegin = Carbon::createFromDate($year, $month)->startOfMonth();

    $weeks = [];

    for($i=1; $i<=$weeksInMonth; $i++)
    {
        $collection = new \stdClass();
        $collection->week = $i;
        $collection->days = $this->getDaysOfWeek($year, $month, $weekBegin->day);

        $weekBegin->addWeek(0);

        $weeks[] = $collection;
    }

    return $weeks;
}

For all months except february I'm getting 5 weeks. For February I'm getting 4 weeks and so I'm not able to save the month-overlapping days.

Am I completely wrong here? What is a possible way to solve this task?


Solution

  • My friend I feel your pain, working with calendar data is annoying. Here's a function I wrote a while back that builds an array of weeks and days, separated by months. It's not the cleanest code but it works.

    It will start from the date you pass in $today as "Y-m-d" (or default to the current date), then work back to the first week of the current month, start there, and then go forward for $scheduleMonths months building an array indexed first by month and then by week.

    It's a bit hard to explain here, but it's self-contained so you can just copy/paste it into your code and then dd() the output to see what it looks like and if it works for you, and modify it from there. There's some formatting you may need to adjust as it was done that way for my specific use case, but the business logic should be sound.

    You should be able to extract the number of weeks in a given month from the output (so if that's all you need you can prob simplify this once you confirm it's working).

    public function getWeeks($today = null, $scheduleMonths = 6) {
    
        $today = !is_null($today) ? Carbon::createFromFormat('Y-m-d',$today) : Carbon::now();
    
        $startDate = Carbon::instance($today)->startOfMonth()->startOfWeek()->subDay(); // start on Sunday
        $endDate = Carbon::instance($startDate)->addMonths($scheduleMonths)->endOfMonth();
        $endDate->addDays(6 - $endDate->dayOfWeek);
    
        $epoch = Carbon::createFromTimestamp(0);
        $firstDay = $epoch->diffInDays($startDate);
        $lastDay = $epoch->diffInDays($endDate);
    
        $week=0;
        $monthNum = $today->month;
        $yearNum = $today->year;
        $prevDay = null;
        $theDay = $startDate;
        $prevMonth = $monthNum;
    
        $data = array();
    
        while ($firstDay < $lastDay) {
    
            if (($theDay->dayOfWeek == Carbon::SUNDAY) && (($theDay->month > $monthNum) || ($theDay->month == 1))) $monthNum = $theDay->month;
            if ($prevMonth > $monthNum) $yearNum++;
    
            $theMonth = Carbon::createFromFormat("Y-m-d",$yearNum."-".$monthNum."-01")->format('F Y');
    
            if (!array_key_exists($theMonth,$data)) $data[$theMonth] = array();
            if (!array_key_exists($week,$data[$theMonth])) $data[$theMonth][$week] = array(
                'day_range' => '',
            );
    
            if ($theDay->dayOfWeek == Carbon::SUNDAY) $data[$theMonth][$week]['day_range'] = sprintf("%d-",$theDay->day);
            if ($theDay->dayOfWeek == Carbon::SATURDAY) $data[$theMonth][$week]['day_range'] .= sprintf("%d",$theDay->day);
    
            $firstDay++;
            if ($theDay->dayOfWeek == Carbon::SATURDAY) $week++;
            $theDay = $theDay->copy()->addDay();
            $prevMonth = $monthNum;
        }
    
        $totalWeeks = $week;
    
        return array(
            'startDate' => $startDate,
            'endDate' => $endDate,
            'totalWeeks' => $totalWeeks,
            'schedule' => $data,
        );
    
    }
    

    I hope this helps you at least get started!