Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

sherwinmdev's avatar

Carbon Period "group by" week number

I need to group dates into week numbers that's in a date range.

Let's say the date range is from December 26, 2022 to January 27, 2023.

Week 1 = December 26 - December 30, 2022 Week 2 = January 2 - January 6, 2023 ... ... Week 5 = January 23 - January 27, 2023

My approach is to go with arrays. Here's what I have so far.

$next_carbon_week_number = NULL;
$week_number = 0;
$array_period = CarbonPeriod::create('2022-12-26', '2023-01-27')->toArray();
foreach ($array_period as $date)
{
		if ($carbon_week_number == $next_carbon_week_number)
		{
				$next_carbon_week_number = $carbon_week_number;
		}else{
				$week_number++;
				$next_carbon_week_number = $carbon_week_number;
		}

		$array_dates[] = ['week_number' => $week_number, 'date' => $date->format('Y-m-d');
}

/*
$array_dates is something like this
array:23 [▼
  0 => array:2 [▼
    "week_number" => 1
    "date" => "2022-12-26"
  ]
  1 => array:2 [▼
    "week_number" => 1
    "date" => "2022-12-27"
  ]
  2 => array:2 [▼
    "week_number" => 1
    "date" => "2022-12-28"
  ]
  3 => array:2 [▼
    "week_number" => 1
    "date" => "2022-12-29"
  ]
  4 => array:2 [▼
    "week_number" => 1
    "date" => "2022-12-30"
  ]
  5 => array:2 [▼
    "week_number" => 2
    "date" => "2023-01-03"
  ]
  6 => array:2 [▼
    "week_number" => 2
    "date" => "2023-01-04"
  ]
]
*/

I want to display in a table like

Week 1 (colspan 5) | Week 2 (colspan 5)

12/26 | 12/27 | 12/28 | 12/29 | 12/30 | 1/2 | 1/3 | 1/4 | 1/5

I hope I'm making sense. Am I on the right track? Is there a carbon feature I missed that makes this easy?

Thanks.

0 likes
10 replies
tykus's avatar
tykus
Best Answer
Level 104

Strictly speaking, 2022-12-26 is Week 53, not Week 0:

collect(CarbonPeriod::create('2022-12-26', '2023-01-27'))
  ->reject(fn($date) => $date->isWeekend()) // remove weekends
  ->map(fn($date) => ['week_number' => $date->week, 'date' => $date->format('Y-m-d')])

If you really want a 0 indexed array to represent the week number, you could groupBy the 'week_number' key and then re-key the result using the values method:

collect(CarbonPeriod::create('2022-12-26', '2023-01-27'))
  ->reject(fn($date) => $date->isWeekend()) // remove weekends
  ->map(fn($date) => ['week_number' => $date->week, 'date' => $date->format('Y-m-d')])
  ->groupBy('week_number')
  ->values();

This is now grouped data, so you will need a nested loop to output the table

webrobert's avatar

don't need a carbon feature...

@foreach(collect(CarbonPeriod::create('2022-12-26', '2023-01-27'))->chunk(5) as $week)
	<div class="colspan5"> {{-- or table --}}
	@foreach($week as $day)
		<span>{{ $day->format('m/d') }}</span>
	@endforeach
	</div>
@endforeach
tykus's avatar

Quite brittle, doesnt filter out weekends, and also assumes the first date if the beginning is the first week

webrobert's avatar

@tykus yeah I made no assumptions on the date inputs as none where made in the question. Its just that I literally made a month calendar to render today with offsets - so its fresh on my mind.

webrobert's avatar

@tykus okay, with dates and weekends ;)

$start = CarbonImmutable::now()->startOf('week');
$end   = $start->addDays(27);
$days  = collect(CarbonPeriod::create($start, $end));
@foreach($days->chunk(7) as $week)
    <div class="colspan5 flex gap-2"> {{-- or table --}}
        @foreach($week as $day)
            @if($day->isWeekday()) <span>{{ $day->format('m/d') }}</span> @endif
        @endforeach
    </div>
@endforeach
sherwinmdev's avatar

Thank you @tykus and @webrobert

To add some info, the date period is a start/end date of school class. So I cannot include weekends and holidays. I have a DB table that stores holidays and non-attendant days. It looks like I was on the right track with arrays but using Laravel's collection simplifies it. I'm not finished with the code but your information has put me in the right direction. I just have to add the other logic during the loop like the student information. This page is supposed to display the attendance information for each student enrolled for the class. Here is what I have so far.

	$core_class_dates = collect(CarbonPeriod::create('2022-12-26', '2023-01-27'))
            ->reject(fn($date) => $date->isWeekend())
			// removes dates from holidays db table stored in an array
            ->reject(function ($date) use($holidays)
            {
                return (in_array($date->format('Y-m-d'), $holidays));
            })
            ->map(fn($date) => ['week_number' => $date->week, 'date' => $date->format('Y-m-d')])
            ->groupBy('week_number');


        $week_counter = 1;
        foreach ($core_class_dates as $c)
        {
            echo count($c);
            echo "<table border='1'><tr>";
            echo "<td colspan=\"count($c)\">week $week_counter</td></tr><tr>";
            foreach ($c as $d)
            {
                echo "<td>".$d['date']."</td>";
            }
            $week_counter++;
            echo "</tr>";

            echo "</table>";
        }

I have to use a $week_counter initialized as 1 because no matter what the start date is, that week is the "first week" of the class. So yes, in a calendar year, December 26 is considered 52/53rd week of the year. But for my use, it should be labeled as Week 1.

The code above just prints out things so I can adjust and move things around. The start and end dates are not hard coded but comes off a DB query and can change depending on the class selected.

Thank you again. Hopefully, student data won't throw this whole thing off.

webrobert's avatar

@sherwinmdev you’ve butchered the two solutions. @tykus gave you a solution for your logic and mine was for the blade. Don’t use echo to build a table. Pass $core_class_dates to the view and loop it with blade.

sherwinmdev's avatar

@webrobert the code i shared is just for me to see what it looks like in the view.

"The code above just prints out things so I can adjust and move things around. The start and end dates are not hard coded but comes off a DB query and can change depending on the class selected."

i just wanted to share what i'm playing with until i get the student data added to it. the above code is not what will be my final version. i wanted to share just in case someone else had a similar question and can visualize the output quickly.

webrobert's avatar

@sherwinmdev same answer.

For the data dd() or return the json

For the table, Echoing table data is not going to be faster than spinning up a view and playing with it there. You’ll also have the benefit of the ide helping to complete the html.

1 like
sherwinmdev's avatar

@webrobert yup, in my controller i had

dd($core_class_dates);

for some weird reason i have a hard time seeing arrays and json just printed out. i have to loop it and echo it to see it. after all these years my brain just can't visualize it unless i loop it in an echo :(

1 like

Please or to participate in this conversation.