32 位元系統上 mktime() 最大支援至 2038-01-19 (Y2K38 問題)。本文提供兩版日曆實作:版本一 判斷架構並限制年份範圍;版本二 改用 DateTime 類別完全繞過 32 位元限制,並支援任意年份。兩版均輸出 HTML 表格,含上/下月年導航、今日高亮、週末配色。
<?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; }
?>
<?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 庫或自行實作農曆轉換。<td><div class="event">...</div></td>。