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

PHP 日曆元件兩版本實作:解決 2038 年問題 (mktime vs DateTime)

     編輯:PHP 相關知識

32 位元系統上 mktime() 最大支援至 2038-01-19 (Y2K38 問題)。本文提供兩版日曆實作:版本一 判斷架構並限制年份範圍;版本二 改用 DateTime 類別完全繞過 32 位元限制,並支援任意年份。兩版均輸出 HTML 表格,含上/下月年導航、今日高亮、週末配色。

版本一:相容 mktime + 架構偵測

<?php
/**
 * 我的日曆 (mktime 版) - 自動偵測 32/64 位元
 * @param int $year 年份
 * @param int $month 月份
 * @param string $timezone 時區
 * @return string HTML 表格
 */
function myCalender($year = '', $month = '', $timezone = 'Asia/Shanghai') {
    date_default_timezone_set($timezone);
    $year = abs(intval($year));
    $month = abs(intval($month));

    // 偵測 32 位元系統
    $is32 = (strtotime('2039-10-10') === false);
    if ($is32) {
        if ($year < 1970 || $year >= 2038) $year = date('Y');
    } else {
        if ($year <= 0) $year = date('Y');
    }
    if ($month <= 0 || $month > 12) $month = date('m');

    // 導航年月計算
    $prevYear = $year - 1;
    $prevMonth = $month - 1; if ($prevMonth <= 0) { $prevMonth = 12; $prevYear = $year - 1; }
    $nextYear = $year + 1;
    $nextMonth = $month + 1; if ($nextMonth > 12) { $nextMonth = 1; $nextYear = $year + 1; }

    // 表頭 HTML
    $html = '<table width="500" border="1">'
        . '<tr align="center">'
        . '<td><a href="?y='.$prevYear.'">上一年</a></td>'
        . '<td><a href="?y='.$year.'&m='.$prevMonth.'">上一月</a></td>'
        . '<td><a href="?">回到今天</a></td>'
        . '<td><a href="?y='.$nextYear.'&m='.$nextMonth.'">下一月</a></td>'
        . '<td><a href="?y='.$nextYear.'">下一年</a></td>'
        . '</tr>'
        . '<tr align="center"><td colspan="5">'.$year.'年'.$month.'月</td></tr>'
        . '<tr><td colspan="5"><table width="100%" border="1">'
        . '<tr align="center">'
        . '<td style="background:#DAF0DD">一</td><td style="background:#DAF0DD">二</td>'
        . '<td style="background:#DAF0DD">三</td><td style="background:#DAF0DD">四</td>'
        . '<td style="background:#DAF0DD">五</td>'
        . '<td style="background:#F60;color:#fff;font-weight:bold">六</td>'
        . '<td style="background:#F60;color:#fff;font-weight:bold">日</td></tr>';

    $currentDay = date('Y-m-j');
    $lastDay = date('j', mktime(0,0,0, $nextMonth, 0, $year)); // 當月最後一天

    $day = 1; $line = '';
    while ($day <= $lastDay) {
        $cday = "$year-$month-$day";
        $week = date('N', mktime(0,0,0, $month, $day, $year)); // 1=Mon ... 7=Sun

        if ($day == 1) {
            $line = '<tr align="center">';
            $line .= str_repeat('<td> </td>', $week - 1);
        }
        $style = ($cday == $currentDay) ? 'style="color:red;"' : '';
        $line .= "<td $style>$day</td>";

        if ($week == 7) { $line .= '</tr>'; $html .= $line; $line = '<tr align="center">'; }
        if ($day == $lastDay) {
            if ($week != 7) $line .= str_repeat('<td> </td>', 7 - $week);
            $line .= '</tr>'; $html .= $line; break;
        }
        $day++;
    }
    $html .= '</table></td></tr></table>';
    return $html;
}

// 32 位元偵測函式
function is32() { return strtotime('2039-10-10') === false; }
?>

版本二:DateTime 物件導向版 (推薦)

<?php
/**
 * 我的日曆 (DateTime 版) - 無 2038 限制、支援 PHP 5.2+
 */
function myCalender($year = '', $month = '', $timezone = 'Asia/Shanghai') {
    date_default_timezone_set($timezone);
    $year = abs(intval($year));
    $month = abs(intval($month));

    $now = new DateTime();
    if ($year <= 0) $year = (int)$now->format('Y');
    if ($month <= 0 || $month > 12) $month = (int)$now->format('m');

    // 導航年月
    $prevYear = $year - 1;
    $prevMonth = $month - 1; if ($prevMonth <= 0) { $prevMonth = 12; $prevYear = $year - 1; }
    $nextYear = $year + 1;
    $nextMonth = $month + 1; if ($nextMonth > 12) { $nextMonth = 1; $nextYear = $year + 1; }

    $html = '<table width="500" border="1">'
        . '<tr align="center">'
        . '<td><a href="?y='.$prevYear.'">上一年</a></td>'
        . '<td><a href="?y='.$prevYear.'&m='.$prevMonth.'">上一月</a></td>'
        . '<td><a href="?">回到今天</a></td>'
        . '<td><a href="?y='.$nextYear.'&m='.$nextMonth.'">下一月</a></td>'
        . '<td><a href="?y='.$nextYear.'">下一年</a></td>'
        . '</tr>'
        . '<tr align="center"><td colspan="5">'.$year.'年'.$month.'月</td></tr>'
        . '<tr><td colspan="5"><table width="100%" border="1">'
        . '<tr align="center">'
        . '<td style="background:#DAF0DD">一</td><td style="background:#DAF0DD">二</td>'
        . '<td style="background:#DAF0DD">三</td><td style="background:#DAF0DD">四</td>'
        . '<td style="background:#DAF0DD">五</td>'
        . '<td style="background:#F60;color:#fff;font-weight:bold">六</td>'
        . '<td style="background:#F60;color:#fff;font-weight:bold">日</td></tr>';

    $currentDay = $now->format('Y-m-j');
    // 取得當月最後一天:下月 0 號
    $lastDay = (new DateTime("$year-$nextMonth-0"))->format('j');

    $day = 1; $line = '';
    while ($day <= $lastDay) {
        $cday = "$year-$month-$day";
        $dt = new DateTime("$year-$month-$day");
        $week = (int)$dt->format('N');

        if ($day == 1) {
            $line = '<tr align="center">';
            $line .= str_repeat('<td> </td>', $week - 1);
        }
        $style = ($cday == $currentDay) ? 'style="color:red;"' : '';
        $line .= "<td $style>$day</td>";

        if ($week == 7) { $line .= '</tr>'; $html .= $line; $line = '<tr align="center">'; }
        if ($day == $lastDay) {
            if ($week != 7) $line .= str_repeat('<td> </td>', 7 - $week);
            $line .= '</tr>'; $html .= $line; break;
        }
        $day++;
    }
    $html .= '</table></td></tr></table>';
    return $html;
}
?>

兩版本比較

特性mktime 版DateTime 版
32 位元支援需判斷並限制年份原生支援任意年份
程式碼複雜度較低 (傳統函式)稍高 (物件導向)
效能極快略慢 (物件建立)
PHP 版本需求PHP 4+PHP 5.2+
推薦場景舊專案維護、極致效能新專案、跨平台、長期維護

進階擴充建議

  • 農曆/節氣:引入 ChineseCalendar 庫或自行實作農曆轉換。
  • 行事曆事件:將 $day 迴圈內加入資料庫查詢,輸出 <td><div class="event">...</div></td>。
  • 前端互動:搭配 JavaScript (FullCalendar, Vue, React) 實現 AJAX 切換月份、拖曳事件。
  • 樣式現代化:移除內聯 style,改用 CSS 類別與 Flex/Grid 佈局。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved