如何获得每月使用PHP的周数?

时间:2018-07-02 13:28:36

标签: php algorithm date

我当前正在生成一个html表,该表的标题中包含日期

这些日期大概是整个一年中的每周的第一天(显示的年份可能会根据过滤器而变化),它们是由以下代码生成的

<?php $firstDay = strtotime($year . 'W' . str_pad($week, 2, '0', STR_PAD_LEFT)); ?>
<td><?= date('d/m/Y', $firstDay) ?></td>

这是在for循环内,该循环使$week1到全年的周数。

我注意到,有些年来,上一年的12月的最后一个星期一也被包含在其中,例如当我在过滤器中选择29/12/2014时的2015

到目前为止,为了确定一个月中的星期数,我习惯于通过编程方式计算所述月份中的星期一数,并将其用作日期行上方行的colspan,因此,没有计数几年的1月为数周,这使表格发生了变化。

2015年呈现的html看起来像这样

<table>
    <thead>
        <tr>
            <th colspan="4">January</th>
            <th colspan="4">February</th>
            [...]
        </tr>
        <tr>
            <!-- Under January cell -->
            <th>29/12/2014</th>
            <th>05/01/2015</th>
            <th>12/01/2015</th>
            <th>19/01/2015</th>
            <!-- Under February cell -->
            <th>26/01/2015</th>
            <th>02/02/2015</th>
            [...]
        </tr>
    </thead>
    [...]

在每月搜索和测试了很多星期的计数器之后,我发现这些计数器似乎都没有包含上一年的最后一个星期一。

有人能找出如何计算包括上一年发生的那一周在内的周数吗?

4 个答案:

答案 0 :(得分:1)

最近有一个与Carbon类似的列表。 这里是一些示例输出的片段:

<?php

use Carbon\Carbon;

$counter = Carbon::createFromDate(2018, 1, 1);
$end = $counter->copy()->endOfYear();
$months = [];

for (; $counter <= $end; $counter->addWeek()) {
    // just to avoid E_NOTICE
    if (!isset($months[$counter->month])) {
        $months[$counter->month] = [
            'count' => 0,
            'weekstarts' => [],
        ];
    }

    ++$months[$counter->month]['count'];
    $months[$counter->month]['weekstarts'][] = $counter->copy();
}

foreach ($months as $month) {
    printf('Month has %d weeks', $month['count']);
    echo PHP_EOL;
    foreach ($month['weekstarts'] as $num => $weekStartDate) {
        printf('Week %d starts at %s', $num + 1, $weekStartDate);
        echo PHP_EOL;
    }
    echo PHP_EOL;
}

答案 1 :(得分:1)

您可以尝试使用此功能:

id

答案 2 :(得分:1)

给定月份的周数基本上是一个月中的天数,加上偏移量,该偏移量取决于该月开始在一周中的哪一天除以7。这是使用{{3 }}和DateInterval

/**
 * Get number of weeks starting *Monday*, occuring in a given month.
 * A week occurs in a month if any day from that week falls in the month.
 * @param $month_date is expected to be DateTime.
 * @param $count_last count partial week at the end of month as part of month.
 */
function weeks_in_month($month_date, $count_last=true) {
  $fn = $count_last ? 'ceil' : 'floor';
  $start = new DateTime($month_date->format('Y-m'));
  $days = (clone $start)->add(new DateInterval('P1M'))->diff($start)->days;
  $offset = $month_date->format('N') - 1;
  return $fn(($days + $offset)/7);
}

/**
 * Wrapper over weeks_in_month() to get weeks in month for each month of a given year.
 * @param $year_date is expected to be a DateTime.
 */
function year_month_weeks($year_date, $count_last=true) {
  $start = new DateTime($year_date->format('Y') . '-01');
  $year_month_weeks = [];
  foreach(new DatePeriod($start, new DateInterval('P1M'), 11) as $month) {
    $year_month_weeks[] += weeks_in_month($month, $count_last);
  }
  return $year_month_weeks;
}

print_r(year_month_weeks(new DateTime('2008-01'), true));

礼物:

Array
(
    [0] => 5
    [1] => 5
    [2] => 6
    [3] => 5
    [4] => 5
    [5] => 6
    [6] => 5
    [7] => 5
    [8] => 5
    [9] => 5
    [10] => 5
    [11] => 5
)

这可以根据cal的输出进行验证。对于2008年的前6个月,应该在3月的6月中有6周的时间,其余的5周:

>ncal -Mb -m 1 -A 5 2008
                            2008
      January               February               March          
Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  
    1  2  3  4  5  6               1  2  3                  1  2  
 7  8  9 10 11 12 13   4  5  6  7  8  9 10   3  4  5  6  7  8  9  
14 15 16 17 18 19 20  11 12 13 14 15 16 17  10 11 12 13 14 15 16  
21 22 23 24 25 26 27  18 19 20 21 22 23 24  17 18 19 20 21 22 23  
28 29 30 31           25 26 27 28 29        24 25 26 27 28 29 30  
                                            31                    

       April                  May                   June          
Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  Mo Tu We Th Fr Sa Su  
    1  2  3  4  5  6            1  2  3  4                     1  
 7  8  9 10 11 12 13   5  6  7  8  9 10 11   2  3  4  5  6  7  8  
14 15 16 17 18 19 20  12 13 14 15 16 17 18   9 10 11 12 13 14 15  
21 22 23 24 25 26 27  19 20 21 22 23 24 25  16 17 18 19 20 21 22  
28 29 30              26 27 28 29 30 31     23 24 25 26 27 28 29  
                                            30       

答案 3 :(得分:0)

所以,这是我的转机,使用了用于生成每周第一天的功能

function getIsoWeeksInYear($year) {
    $date = new DateTime;
    $date->setISODate($year, 53);
    return ($date->format("W") === "53" ? 53 : 52);
}

function getWeeksPerMonth($year) {
    $allWeeks = $this->getIsoWeeksInYear($year);
    $allFirstDays = [];

    for ($i = 0; $i < $allWeeks; $i++) {
        $allFirstDays[] = date('m/Y', strtotime($year . "W" . str_pad($i, 2, "0", STR_PAD_LEFT)));
    }
    $countWeek = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
    foreach ($allFirstDays as $firstDay) {
        $theMonth = explode('/', $firstDay);
        for ($i = 1; $i < 13; $i++) {
            if ($theMonth[0] == $i) {
                $countWeek[$i - 1]++;
            }
        }
    }
    return $countWeek;
}

因此,我们的想法是获取整个星期数数组,并将其循环用于colspans

<?php $span = getWeeksPerMonth($year) ?>
<?php for ($i = 1; $i < 13; $i++): ?>
    <th colspan="<?= $span[$i - 1] ?>"><?= strftime('%B', mktime(0, 0, 0, $i)) ?></th>
<?php endfor ?>

对于下面的行,我只使用我之前在问题中已经使用的

<?php for ($i = 0; $i < getIsoWeeksInYear($year); $i++): ?>
    <?php $firstDay = strtotime($year . 'W' . str_pad($week, 2, '0', STR_PAD_LEFT)); ?>
    <th><?= date('d/m/Y', $firstDay) ?></th>
<?php endfor ?>

因此,可能在1月的某个星期包含上一年,而在12月的某个星期可能包含明年。

也许,某些地方仍有一些东西需要调整。无论如何,我会测试其他答案,我想我会标记最简单的答案。

修改

看起来好像不必爆炸获取的日期,因为我最初以为在上一年开始的那一周必须在某种情况下使用年份