关于php:查找一个月的每周时间(从星期一开始)

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

我正在尝试查找给定月份和年份的每周时段。日期应从星期一开始,到星期日结束。如果每月的1号是星期日(例如2011年5月),则它应该是第一个元素。

2011年5月

  • 5月1日(星期日)
  • 5月2日-5月8日(星期一-星期日)
  • 5月9日-5月15日(星期一-星期日)
  • 5月17日-6月22日(星期一-星期日)
  • 5月23日-5月29日(星期一-星期日)
  • 5月30日-5月31日(星期一-星期二)

2012年9月

  • 9月1日-9月2日
  • 9月3日-9月9日
  • 9月10日至9月16日
  • 9月17日至9月23日
  • 9月24日至9月30日

我正在使用此功能来计算两个日期的星期数-即该月的第一天和该月的最后一天。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}

奇怪的是,对于一月,当我期望为[1、2、3、4、5]时,它返回的周数为[52、1、2、3、4]。

一旦有了周数,就可以像这样使用它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//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);
    }

由于getWeekNumbers函数未返回我期望的星期数,因此上述函数的输出为

  • 2012年12月24日至12月30日
  • Jan 02-Jan 08(2012)
  • Jan 09-Jan 15(2012)
  • Jan 16-Jan 22(2012)
  • Jan 23-Jan 29(2012)

请注意,第一行(12月24日至12月30日)是当年(2012年)的结束日期,而不是去年(2011年)的结束日期。

理想情况下,我希望它看起来像enter image description here

有任何想法吗?谢谢!!


如果您需要选定月份的所有星期,以及选定星期的所有日期,那么这就是您所需要的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()函数返回多维数组。 第一个关键是星期几。 2级是数组,其日期保存为DateTime对象。

获取示例:

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

我多了一点时间,所以写了一个例子;-)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$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;

输出如下:
enter image description here


以下内容假定用户可以选择运行报表所要使用的月份和年份(发布的值分别为1-12和YYYY)。 可能有一种更优雅的方法可以执行此操作,但这似乎对我有用。 另外,在帖子的顶部,您说您希望星期是星期一-星期日。 但是,您在底部的示例/屏幕截图显示了周日至周日。 以下是周一至周日最初规定的目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$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"])) ."\
"
;
}

这很完美!!! phpfiddle在这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?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).'';
foreach ($weeks as $week){
   echo fDate($week['start']).' '.fDate($week['end']).'';
}
echo 'To: '.fDate($end).'';

/*   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);}