Search code examples
phptimedurationperiodweek-number

Find weekly periods (starting on a Monday) for a month


I'm trying to find the weekly periods for a given month and year. Dates should start on a Monday and end on a Sunday. If the 1st of the month is a Sunday (Ex May 2011), it should be the first element.

May 2011

  • May 1 (Sunday)
  • May 2 - May 8 (Monday - Sunday)
  • May 9 - May 15 (Monday - Sunday)
  • May 17 - Ma6y 22 (Monday - Sunday)
  • May 23 - May 29 (Monday - Sunday)
  • May 30 - May 31 (Monday - Tuesday)

September 2012

  • September 1 - September 2
  • September 3 - September 9
  • September 10 - September 16
  • September 17 - September 23
  • September 24 - September 30

I am using this function to calculate the week numbers for two dates - I.e. the 1st day of the month and last day of the month.

public function getWeekNumbers($startDate, $endDate)
{
    $p = new DatePeriod(
            new DateTime($startDate),
            new DateInterval('P1W'),
            new DateTime($endDate)
    );

    $weekNumberList = array();

    foreach ($p as $w)
    {
        $weekNumber = $w->format('W');
        $weekNumberList[] = ltrim($weekNumber, '0');
    }

    return $weekNumberList;
}

Strangely, for the month of January, it returns week numbers of [52, 1, 2, 3, 4] when I'm expecting [1, 2, 3, 4, 5].

Once I have the week numbers, I'm using them like so:

//The following loop will populate the dataset with the entire month's durations - regardless if hours were worked or not.
    $firstDayOfMonth = date('Y-m-d', strtotime("first day of {$this->year}-{$monthName}"));
    $lastDayOfMonth = date('Y-m-d', strtotime("last day of {$this->year}-{$monthName}"));

    foreach ($this->getWeekNumbers($firstDayOfMonth, $lastDayOfMonth) as $key => $weekId)
    {
        // find first mоnday of the year
        $firstMon = strtotime("mon jan {$this->year}");

        // calculate how many weeks to add
        $weeksOffset = $weekId - date('W', $firstMon);

        $beginDays = $weeksOffset * 7;
        $endDays = ($weeksOffset * 7) + 6;

        $searchedMon = strtotime(date('Y-m-d', $firstMon) . " +{$beginDays} days");
        $searchedSun = strtotime(date('Y-m-d', $firstMon) . " +{$endDays} days");

        echo date("M d", $searchedMon) . " - " . date("M d", $searchedSun);
    }

Since, the getWeekNumbers function isn't returning the week numbers I'm expecting, it's not surprising that the output of the above function is

  • Dec 24 - Dec 30 (2012)
  • Jan 02 - Jan 08 (2012)
  • Jan 09 - Jan 15 (2012)
  • Jan 16 - Jan 22 (2012)
  • Jan 23 - Jan 29 (2012)

Note that the 1st line (Dec 24 - Dec 30) is the end of the current year (2012) and not the end of last year (2011).

Ideally, I want it to look like enter image description here

Any ideas? Thanks!!


Solution

  • If you need all weeks for selected month, and all dates for selected week, then this is all you need:

    function getWeekDays($month, $year)
    {
        $p = new DatePeriod(
            DateTime::createFromFormat('!Y-n-d', "$year-$month-01"),
            new DateInterval('P1D'),
            DateTime::createFromFormat('!Y-n-d', "$year-$month-01")->add(new DateInterval('P1M'))
        );
    
        $datesByWeek = array();
        foreach ($p as $d) {
            $dateByWeek[ $d->format('W') ][] = $d;
        }
        return $dateByWeek;
    }
    

    getWeekDays() function returns multi dimension array. first key is week number. 2 level is array, that has dates saved as DateTime object.

    Fetch example:

    print_r( getWeekDays(5, 2011) ); # May 2011
    print_r( getWeekDays(9, 2012) ); # Sep 2012
    

    I had a little time extra, so I written an example ;-)

    $datesByWeek = getWeekDays(8, 2012);
    $o = '<table border="1">';
    $o.= '<tr><th>Week</th><th>Monday</th><th>Tuesday</th><th>Wednesday</th><th>Thursday</th><th>Friday</th><th>Saturday</th><th>Sunday</th></tr>';
    foreach ($datesByWeek as $week => $dates) {
        $firstD = $dates[0];
        $lastD = $dates[count($dates)-1];
    
        $o.= "<tr>";
        $o.= "<td>" . $firstD->format('M d') . ' - ' . $lastD->format('M d') . "</td>";
        $N = $firstD->format('N');
        for ($i = 1; $i < $N; $i++) {
            $o.= "<td>-</td>";
        }
        foreach ($dates as $d) {
            $o.= "<td>" . $d->format('d.') . " / 0.00</td>";
                # for selected date do you magic
        }
        $N = $lastD->format('N');
        for ($i = $N; $i < 7; $i++) {
            $o.= "<td>-</td>";
        }
        $o.= "</tr>";
    }
    $o.= '</table>';
    echo $o;
    

    Output looks like: enter image description here