Как узнать количество недель в месяц в PHP? - PullRequest
0 голосов
/ 02 июля 2018

В настоящее время я создаю HTML-таблицу, которая содержит даты в заголовке

Эти даты являются предположительно первыми днями каждой недели в течение всего года (отображаемый год может меняться в зависимости от фильтра), и они генерируются следующим кодом

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

Это внутри цикла for, который заставляет $week перейти от 1 к количеству недель в году.

Что я заметил, так это то, что в некоторые годы сюда включается и последний понедельник декабря предыдущего года, например 29/12/2014, когда я выбираю 2015 в фильтре.

До сих пор, чтобы определить количество недель в месяце, я использовал программно подсчитывать число понедельников в указанном месяце и использовал его в качестве colspan для строки над строкой дат, и, таким образом, здесь не учитываются недели на январь на несколько лет, что делает смену таблицы.

Рендеринг HTML за год 2015 будет выглядеть следующим образом

<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 голосов
/ 03 июля 2018

Количество недель, приходящихся на данный месяц, - это, как правило, количество дней в месяце плюс смещение, которое зависит от того, с какого дня недели начался месяц, деленное на 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. Для первых 6 месяцев 2008 года должно быть 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       
0 голосов
/ 02 июля 2018

Вы можете попробовать эту функцию:

  <?php

        /**
         * Returns the amount of weeks into the month a date is
         * @param $date a YYYY-MM-DD formatted date
         * @param $rollover The day on which the week rolls over
         */
        function getWeeks($date, $rollover)
        {
            $cut = substr($date, 0, 8);
            $daylen = 86400;

            $timestamp = strtotime($date);
            $first = strtotime($cut . "00");
            $elapsed = ($timestamp - $first) / $daylen;

            $weeks = 1;

            for ($i = 1; $i <= $elapsed; $i++)
            {
                $dayfind = $cut . (strlen($i) < 2 ? '0' . $i : $i);
                $daytimestamp = strtotime($dayfind);

                $day = strtolower(date("l", $daytimestamp));

                if($day == strtolower($rollover))  $weeks ++;
            }

            return $weeks;
        }


        //
        echo getWeeks("2011-06-11", "sunday"); //outputs 2, for the second week of the month
    ?>
0 голосов
/ 02 июля 2018

Итак, это был мой оборот, использующий функцию, используемую для генерации первых дней каждой недели

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

Таким образом, в январе может быть некоторая неделя, включенная в предыдущий год, а в декабре недельная неделя.

Может быть, еще есть что-то, что можно настроить. В любом случае, я опробую другие ответы и думаю, что в качестве ответа я выберу самый простой.

Редактировать

Похоже, мне не нужно было взрывать полученную дату, поскольку я изначально думал, что мне придется использовать год в некоторых условиях для недели, начинающейся с предыдущего года

0 голосов
/ 02 июля 2018

Недавно сделал листинг довольно похожим на Carbon . Вот Snipped с некоторым примером вывода:

<?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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...