【问题标题】:How to get number of weeks per month in PHP?如何在 PHP 中获取每月的周数?
【发布时间】:2018-07-02 13:28:35
【问题描述】:

我目前正在生成一个 html 表格,其中包含标题中的日期

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

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

这是在for 循环中,它使$week1 变为全年的周数。

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

到目前为止,为了确定一个月中的周数,我曾经以编程方式计算该月中的星期一数,并将其用作日期行上方行的 colspan,因此,有未计算的几年一月的几周,这使得表格发生了变化。

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>
    [...]

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

谁能知道如何计算周数,包括从前一年发生的额外一周?

【问题讨论】:

  • 所以基本上问题是如何找出一年中的周数?或一年中有多少个星期一?您能否在一个问题中重新表述整个帖子?它的本质..
  • 还可以考虑发布 for loop 来创建整个事情 :)
  • @Keloo 这将是每个月的 ISO 周数,我想我刚刚弄明白了,如果可行,我会尝试一些方法并发布答案

标签: php algorithm date


【解决方案1】:

给定月份的周数基本上是一个月中的天数,加上一个偏移量,该偏移量取决于该月从一周中的哪一天开始,除以 7。这是使用 DateInterval 的解决方案和DatePeriod:

/**
 * 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       

【讨论】:

  • 你解决的问题是它与其他月份叠加,就像2月和3月一样,其中2月的最后一周由一天组成,其余的也被计算在内3 月,但即便如此,我还是要看看你用过的那些课程
  • 嗨。哦,以为这就是你想要的。一定是误读了你的意图。预期的行为是;如果一周的任何部分落在一个月内,则将其视为该月的一周,所以是的,给定的一周可能会落在 2 个月内。无论如何,是的,看看这两个类是个好主意。应该提供更清洁的解决方案。
  • P.S.实际上,如果您将 ceil() 更改为 floor() 我认为它可能会给出您想要的计数 - 如果在一个月内部分下降,则上周不计算在内。使用选项更新。
【解决方案2】:

最近做了一个与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;
}

【讨论】:

  • 有机会我会试试这个,因为我刚刚发现 Carbon 是什么
  • 那个库确实很有趣
  • 我选择了这个答案,因为它包含一个库,如果有更多的操作要使用,它可以用来做更多的日期。无论如何,谢谢大家,你们都得到了我的 +1
【解决方案3】:

所以,这是我的转变,使用用于生成每周第一天的函数

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;
}

因此,我们的想法是获取完整的周数数组并将其用于 colspan 的循环

<?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 月的那一周。

也许,还有一些东西需要在某个地方进行调整。无论如何,我会测试其他答案,我想我会标记最容易使用的答案。

编辑

看起来我不必分解获得的日期,因为我最初认为我必须在某些条件下使用从前一年开始的一周的年份

【讨论】:

    猜你喜欢
    • 2021-06-07
    • 2019-12-18
    • 1970-01-01
    • 2012-09-22
    • 1970-01-01
    • 2015-11-14
    • 2018-01-27
    • 2014-08-17
    • 1970-01-01
    相关资源
    最近更新 更多