1
votes

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!!

3
did you solve your problem? you can up-vote many anwsers here if they are useful. You can select an anwser as the one that closes the issue. But in other case you can post your own anwser to the issue to share how it was solved.Luis Siquot

3 Answers

4
votes

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

2
votes

The following assumes that the user can pick the month and year for which they wan to run the report (the value posted being 1-12 for month and YYYY for year). There may be a more elegant way to do it, but this seems to work for me. Also, at the top of your post, you say that you want the weeks to be Monday - Sunday. However, your example/screenshot at the bottom shows weeks being Sunday to Saturday. The below is for the originally-stated goal of Monday - Sunday.

$month = $_POST["month"];
$year = $_POST["year"];

$endDate = date("t", strtotime($year."-".$month."-01"));

$dayOfWeekOfFirstOfMonth = date("w", strtotime($year."-".$month."-01"));
$lastDayOfFirstWeek = 8 - $dayOfWeekOfFirstOfMonth;

$weeksArray = array(array("firstDay"=>1, "lastDay"=>$lastDayOfFirstWeek));

$loopDate = $lastDayOfFirstWeek + 1;

while($loopDate < $endDate)
{
    $weeksArray[] = array("firstDay"=>$loopDate, "lastDay"=>($loopDate+6 > $endDate ? $endDate : $loopDate+6));
    $loopDate+=7;
}

foreach($weeksArray as $week)
{
    echo date("M d", strtotime($year."-".$month."-".$week["firstDay"])) . " - " . date("M d", strtotime($year."-".$month."-".$week["lastDay"])) . "\n";
}
2
votes

this works perfect!!! phpfiddle here

<?php
// start and end must be timestamps !!!!
$start = 1346976000;  //  Thu 2012-09-06
$end   = 1348704000;  //  Tue 2012-09-26

// generate the weeks 
$weeks = generateweeks($start, $end);

// diaplay the weeks
echo 'From: '.fDate($start).'<br>';
foreach ($weeks as $week){
   echo fDate($week['start']).' '.fDate($week['end']).'<br>';
}
echo 'To: '.fDate($end).'<br>';

/*   outputs this:
From: Thu 2012-09-06
Thu 2012-09-06 Sun 2012-09-09
Mon 2012-09-10 Sun 2012-09-16
Mon 2012-09-17 Sun 2012-09-23
Mon 2012-09-24 Wed 2012-09-26
To: Wed 2012-09-26
*/


// $start and $end must be unix timestamps (any range)
//  returns an array of arrays with 'start' and 'end' elements set 
//  for each week (or part of week) for the given interval
//  return values are also in timestamps
function generateweeks($start,$end){
    $ret = array();
    $start = E2D($start);
    $end = E2D($end);

    $ns = nextSunday($start);

    while(true){
        if($ns>=$end) {insert($ret,$start,$end);return $ret;}
        insert($ret,$start,$ns);
        $start = $ns +1;
        $ns+=7;
    }
}



// helper function to append the array and convert back to unix timestamp
function insert(&$arr, $start, $end){$arr[] = array('start'=>D2E($start), 'end'=>D2E($end));}
// recives any date on CD format returns next Sunday on CD format
function nextSunday($Cdate){return $Cdate + 6  - $Cdate % 7;}
// recives any date on CD format returns previous Monday on CD format // finaly not used here
function prevMonday($Cdate){return $Cdate      - $Cdate % 7;}   
// recives timestamp returns CD
function E2D($what){return floor($what/86400)+2;}     // floor may be optional in some circunstances
// recives CD returns timestamp
function D2E($what){return ($what-2)*86400;}          // 24*60*60
// just format the timestamp for output, you can adapt it to your needs
function fDate($what){return date('D Y-m-d',$what);}