Reputation: 87
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?
Upvotes: 2
Views: 4686
Reputation: 1
Something like this?
function weekOfMonth($date) {
$firstOfMonth = strtotime(date("Y-m-01", $date));
$lastWeekNumber = (int)date("W", $date);
$firstWeekNumber = (int)date("W", $firstOfMonth);
if (12 === (int)date("m", $date)) {
if (1 == $lastWeekNumber) {
$lastWeekNumber = (int)date("W", ($date - (7*24*60*60))) + 1;
}
} elseif (1 === (int)date("m", $date) and 1 < $firstWeekNumber) {
$firstWeekNumber = 0;
}
return $lastWeekNumber - $firstWeekNumber + 1;
}
function weeks($month, $year){
$lastday = date("t", mktime(0, 0, 0, $month, 1, $year));
return weekOfMonth(strtotime($year.'-'.$month.'-'.$lastday));
}
$result = [];
for ($year = 2017; $year < 2020; $year++){
for ($month = 1; $month < 13; $month++) {
$numOfWeeks = weeks($month, $year);
$result[$year][$month]['numOfWeeks'] = $numOfWeeks;
$daysInFirstWeek = 8 - date('N', strtotime($year.'-'.$month.'-01'));
$result[$year][$month]['daysPerWeek']['week_1'] = $daysInFirstWeek;
$startDay = date('d', strtotime('next Monday', strtotime($year.'-'.$month.'-01')));
for ($i = 1; $i < ($numOfWeeks - 1); $i++) {
$startDay += 7;
$result[$year][$month]['daysPerWeek']['week_'.($i+1)] = 7;
}
//last week
$result[$year][$month]['daysPerWeek']['week_'.($i+1)] = date('t', strtotime($year.'-'.$month.'-01')) - $startDay + 1;
}
}
echo json_encode($result)."\n";
Upvotes: 0
Reputation: 1647
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!
Upvotes: 8