date() 與 mktime()PHP 內建強大的日期處理函式,生成月曆只需兩個關鍵資訊:
date('t', $timestamp)date('w', $timestamp)(0=週日, 6=週六)mktime(0,0,0,$month,1,$year) 可產生該月第一天的 Unix 時間戳。
<?php
$month = isset($_GET['m']) ? (int)$_GET['m'] : (int)date('n');
$year = isset($_GET['y']) ? (int)$_GET['y'] : (int)date('Y');
// 邊界檢查
$month = max(1, min(12, $month));
$year = max(1970, min(2038, $year));
$firstDayTs = mktime(0, 0, 0, $month, 1, $year);
$startWeek = (int)date('w', $firstDayTs); // 0-6
$dayNum = (int)date('t', $firstDayTs); // 28-31
// 上/下月連結
$prevMonth = $month === 1 ? 12 : $month - 1;
$prevYear = $month === 1 ? $year - 1 : $year;
$nextMonth = $month === 12 ? 1 : $month + 1;
$nextYear = $month === 12 ? $year + 1 : $year;
?>
<table class="calendar">
<caption>
<a href="?m=<?= $prevMonth ?>&y=<?= $prevYear ?>">«</a>
<?= $year ?> 年 <?= $month ?> 月
<a href="?m=<?= $nextMonth ?>&y=<?= $nextYear ?>">»</a>
</caption>
<thead>
<tr><th>日</th><th>一</th><th>二</th><th>三</th><th>四</th><th>五</th><th>六</th></tr>
</thead>
<tbody>
<tr>
<?php
// 前導空白格
for ($i = 0; $i < $startWeek; $i++) echo '<td> </td>';
for ($day = 1; $day <= $dayNum; $day++) {
$isToday = ($day === (int)date('j') && $month === (int)date('n') && $year === (int)date('Y'));
$class = $isToday ? ' class="today"' : '';
echo "<td$class>$day</td>";
// 週六換行
if (($startWeek + $day) % 7 === 0) {
if ($day !== $dayNum) echo '</tr><tr>';
}
}
// 尾部填滿
$endWeek = ($startWeek + $dayNum) % 7;
if ($endWeek !== 0) {
for ($i = $endWeek; $i < 7; $i++) echo '<td> </td>';
}
?>
</tr>
</tbody>
</table>
Calendar 類別(可複用、可擴充)<?php
class Calendar {
private int $year;
private int $month;
private array $weeks = ['日','一','二','三','四','五','六'];
private string $baseUrl;
public function __construct(int $year = 0, int $month = 0, string $baseUrl = '') {
$this->year = $year ?: (int)date('Y');
$this->month = $month ?: (int)date('n');
$this->baseUrl = $baseUrl ?: basename($_SERVER['PHP_SELF']);
$this->clamp();
}
private function clamp(): void {
$this->month = max(1, min(12, $this->month));
$this->year = max(1970, min(2038, $this->year));
}
public function render(): string {
$firstDayTs = mktime(0,0,0, $this->month, 1, $this->year);
$startWeek = (int)date('w', $firstDayTs);
$dayNum = (int)date('t', $firstDayTs);
$todayStr = date('Y-n-j');
$html = '<table class="calendar">';
$html .= $this->renderHeader();
$html .= '<thead><tr>';
foreach ($this->weeks as $w) $html .= "<th>$w</th>";
$html .= '</tr></thead><tbody><tr>';
// 前導空格
for ($i = 0; $i < $startWeek; $i++) $html .= '<td> </td>';
for ($day = 1; $day <= $dayNum; $day++) {
$dateStr = "$this->year-$this->month-$day";
$class = ($dateStr === $todayStr) ? ' class="today"' : '';
$html .= "<td$class>$day</td>";
if (($startWeek + $day) % 7 === 0 && $day !== $dayNum) {
$html .= '</tr><tr>';
}
}
// 尾部填滿
$endWeek = ($startWeek + $dayNum) % 7;
if ($endWeek !== 0) {
for ($i = $endWeek; $i < 7; $i++) $html .= '<td> </td>';
}
$html .= '</tr></tbody></table>';
return $html;
}
private function renderHeader(): string {
$pm = $this->month === 1 ? 12 : $this->month - 1;
$py = $this->month === 1 ? $this->year - 1 : $this->year;
$nm = $this->month === 12 ? 1 : $this->month + 1;
$ny = $this->month === 12 ? $this->year + 1 : $this->year;
$url = fn($m,$y) => "$this->baseUrl?m=$m&y=$y";
return "<caption>"
. "<a href='{$url($pm,$py)}'>«</a> "
. "<strong>{$this->year} 年 {$this->month} 月</strong> "
. "<a href='{$url($nm,$ny)}'>»</a>"
. "</caption>";
}
}
// 使用方式
$cal = new Calendar(
isset($_GET['y']) ? (int)$_GET['y'] : 0,
isset($_GET['m']) ? (int)$_GET['m'] : 0
);
echo $cal->render();
?>
<style>
.calendar { width: 100%; max-width: 400px; margin: 1rem auto; border-collapse: collapse; font-family: system-ui, sans-serif; }
.calendar caption { padding: .5rem; font-size: 1.1rem; text-align: center; background: #f0f0f0; border: 1px solid #ddd; border-bottom: none; }
.calendar th, .calendar td { width: 14.28%; height: 2.5rem; text-align: center; border: 1px solid #ddd; font-size: .9rem; }
.calendar th { background: #333; color: #fff; font-weight: 600; }
.calendar td.today { background: #333; color: #fff; font-weight: bold; border-color: #333; }
.calendar a { text-decoration: none; color: #0066cc; }
@media (max-width: 480px) {
.calendar th, .calendar td { height: 2rem; font-size: .8rem; }
}
</style>
render() 中結合資料庫查詢,為特定日期加上事件點點或彈窗連結。ChineseCalendar 庫(如 overtrue/laravel-chinese-calendar)顯示農曆節氣。fetch() 請求片段,無刷新翻頁。PHP 內建日期函式足以構建功能完整的月曆。程序式寫法適合快速原型,物件導向封裝則利於維護與擴充。搭配語意化 HTML 與彈性 CSS,即可得到無障礙、響應式的日曆元件,作為排程系統、行事曆、報表篩選器的基礎 UI。