首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 動態月曆生成函式:支援翻頁、自訂樣式與 URL 參數控制

     編輯:PHP 綜合知識

函式特色

  • 函式 calendar($MM, $YYYY) 接受月份與年份參數,預設為當月。
  • 自動計算該月天數、首日星期、尾日星期。
  • 生成含「上月/下月」導航連結的 HTML 表格,連結保留當前參數(如 NB)。
  • 表頭顯示星期(Sun-Sat),儲存格對齊靠上,適合填入行事曆事件。
  • 使用 strftime 格式化,支援本地化月份名稱(需伺服器 locale 設定)。

完整程式碼

<?php
/**
 * 動態月曆生成
 * @param int|string $MM   月份 (1-12),空則當月
 * @param int|string $YYYY 年份 (四位數),空則当年
 */
function calendar($MM = '', $YYYY = '') {
    if ($MM === '') $MM = date("m");
    if ($YYYY === '') $YYYY = date("Y");

    if (!checkdate($MM, 1, $YYYY)) return; // 非法日期靜默返回

    // 關鍵日期資訊
    $days       = (int)strftime("%d", mktime(0,0,0, $MM+1, 0, $YYYY)); // 該月總天數
    $firstDay   = (int)strftime("%w", mktime(0,0,0, $MM, 1, $YYYY));   // 1號星期幾 (0=Sun)
    $lastDay    = (int)strftime("%w", mktime(0,0,0, $MM, $days, $YYYY)); // 最後一天星期幾
    $monthName  = strftime("%b %Y", mktime(0,0,0, $MM, 1, $YYYY));     // 如 "Jan 2024"
    $preMonth   = strftime("%m", mktime(0,0,0, $MM-1, 1, $YYYY));
    $preYear    = strftime("%Y", mktime(0,0,0, $MM-1, 1, $YYYY));
    $nextMonth  = strftime("%m", mktime(0,0,0, $MM+1, 1, $YYYY));
    $nextYear   = strftime("%Y", mktime(0,0,0, $MM+1, 1, $YYYY));

    $self = $_SERVER['PHP_SELF'];
    $nb   = isset($_GET['NB']) ? $_GET['NB'] : '';

    echo "<table border=\"1\" cellpadding=\"1\" cellspacing=\"1\">\n";
    echo "<tr>\n";
    echo "<th valign=\"top\"><a href=\"$self?NB=$nb&MM=$preMonth&YY=$preYear\">P</a></th>\n";
    echo "<th colspan=\"5\" valign=\"top\">$monthName</th>\n";
    echo "<th valign=\"top\"><a href=\"$self?NB=$nb&MM=$nextMonth&YY=$nextYear\">N</a></th>\n";
    echo "</tr>\n";

    echo "<tr style=\"font-family:Verdana;font-size:x-small\">\n";
    foreach (['Sun','Mon','Tue','Wed','Thu','Fri','Sat'] as $d) echo "<th>$d</th>\n";
    echo "</tr>\n";

    $current = 1;
    for ($row = 1; $row <= 5; $row++) { // 最多 5 週(含跨月補齊最多 6 列,這裡固定 5 列可能不足,見下方改進)
        echo "<tr align=\"left\" valign=\"top\" style=\"font-family:Verdana;font-size:x-small\">\n";
        $diff = $firstDay - $lastDay;

        if ($firstDay > $lastDay && $current == 1 && $diff != 1) {
            // 情況:首日星期 > 尾日星期,且非緊湊排列(如 31 天月份)
            for ($x = $lastDay; $x >= 0; $x--) {
                $pd = $days - $x;
                echo "<td>$pd</td>\n";
            }
            for ($z = 1; $z < $firstDay - $lastDay; $z++) echo "<td> </td>\n";
            for ($y = $firstDay; $y < 7; $y++) {
                echo "<td>$current</td>\n";
                $current++;
            }
        } elseif ($firstDay != 0 && $current == 1) {
            // 一般情況:第一列前面補空格
            for ($z = 1; $z <= $firstDay; $z++) echo "<td> </td>\n";
            for ($y = $firstDay; $y < 7; $y++) {
                echo "<td>$current</td>\n";
                $current++;
            }
        } else {
            // 後續列:填滿 7 格或直到月份結束
            for ($u = 1; $u <= 7 && $current <= $days; $u++) {
                echo "<td>$current</td>\n";
                $current++;
            }
        }
        echo "</tr>\n";
        if ($current > $days) break; // 提前結束
    }
    echo "</table>\n";
}
?>

使用範例

<?php
// 直接顯示當月
calendar();

// 或指定年月
calendar(12, 2024);
?>

已知問題與改進建議

問題改進方案
固定 5 列可能不足(如 31 天且週六起,需 6 列)改用 while ($current <= $days) 迴圈動態生成列,不限制列數。
跨月補齊邏輯複雜且有邊界錯誤採用統一算法:
1. 計算第一天前需補幾格 $pad = $firstDay。
2. 總格數 $total = $pad + $days。
3. 列數 ceil($total/7)。
4. 雙層迴圈輸出,索引 $idx = $row*7 + $col,若 $idx < $pad 輸出空格,$idx >= $pad+$days 輸出空格,否則輸出 $idx - $pad + 1。
硬編碼樣式、難以客製化輸出純資料結構(二維陣列),由模板引擎或獨立渲染函式生成 HTML,支援 CSS class 注入。
依賴 strftime(PHP 8.1+ 棄用)改用 IntlDateFormatter 或 DateTime 格式化,確保長期相容。

現代化重構片段(資料分離版)

function getCalendarData(int $month, int $year): array {
    $firstDay = (int)(new DateTime("$year-$month-01"))->format('w');
    $days     = (int)(new DateTime("$year-$month-01"))->format('t');
    $weeks = [];
    $day = 1;
    for ($row = 0; $day <= $days; $row++) {
        $weeks[$row] = [];
        for ($col = 0; $col < 7; $col++) {
            $idx = $row * 7 + $col;
            if ($idx < $firstDay) {
                $weeks[$row][$col] = null; // 上月補齊
            } elseif ($day <= $days) {
                $weeks[$row][$col] = $day++;
            } else {
                $weeks[$row][$col] = null; // 下月補齊
            }
        }
    }
    return ['weeks' => $weeks, 'monthName' => (new DateTime("$year-$month-01"))->format('F Y')];
}

此版本回傳純陣列,前端可自由渲染為 Table、Flex Grid、React/Vue 組件等。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved