PHP date() 函數功能強大,支援豐富的格式化字元。本文實作一個 LampDate 類,給定起始/結束日期,自動生成完整的 HTML 表格日曆,支援多月份跨越、今日高亮、範圍標記、CSS/JS 外掛,展示 date() 進階應用。
| 字元 | 說明 | 範例 |
|---|---|---|
| 日 | ||
| d | 月份中的第幾天(雙位數,前導零) | 01-31 |
| D | 星期幾縮寫(3 字母) | Mon-Sun |
| j | 月份中的第幾天(無前導零) | 1-31 |
| l | 星期幾完整名稱 | Sunday-Saturday |
| N | ISO-8601 星期數(1=週一,7=週日) | 1-7 |
| S | 日期序數後綴 | st, nd, rd, th |
| w | 星期數(0=週日,6=週六) | 0-6 |
| z | 年份中的第幾天 | 0-365 |
| 週 | ||
| W | ISO-8601 年份週數 | 42 |
| 月 | ||
| F | 月份完整名稱 | January-December |
| m | 月份雙位數 | 01-12 |
| M | 月份縮寫 | Jan-Dec |
| n | 月份無前導零 | 1-12 |
| t | 該月總天數 | 28-31 |
| 年 | ||
| L | 是否閏年 | 1/0 |
| Y | 4 位數年份 | 2024 |
| y | 2 位數年份 | 24 |
| 時間 | ||
| H | 24 小時制雙位數 | 00-23 |
| i | 分鐘雙位數 | 00-59 |
| s | 秒數雙位數 | 00-59 |
| 完整格式 | ||
| c | ISO 8601 | 2024-01-15T10:30:00+08:00 |
| r | RFC 822 | Mon, 15 Jan 2024 10:30:00 +0800 |
| U | Unix 時間戳 | 1705285800 |
class LampDate {
private $start_year, $start_month, $start_day;
private $end_year, $end_month, $end_day;
private $start_time, $end_time; // 時間戳
private $start, $end; // 起始月首/結束月尾時間戳
private $day_count; // 總天數
private $week = ['星期一','星期二','星期三','星期四','星期五','星期六','星期日'];
private $css, $script; // 外部樣式/腳本路徑
}
public function __construct(
$start_year = '2024', $start_month = '1', $start_day = '1',
$end_year = '2024', $end_month = '12', $end_day = '31',
$css = 'style.css', $script = 'js.js'
) {
// 賦值
$this->start_year = $start_year; $this->start_month = $start_month; $this->start_day = $start_day;
$this->end_year = $end_year; $this->end_month = $end_month; $this->end_day = $end_day;
$this->css = $css; $this->script = $script;
// 關鍵時間戳計算
$this->start_time = strtotime("{$start_year}-{$start_month}-{$start_day}");
$this->end_time = strtotime("{$end_year}-{$end_month}-{$end_day}");
$this->start = strtotime("{$start_year}-{$start_month}-01"); // 起始月第一天
$this->end = strtotime("{$end_year}-{$end_month}-" . date('t', $this->end_time)); // 結束月最後一天
$this->day_count = ($this->end - $this->start) / 86400; // 總天數
}
public function get_datetab($i = 0) {
$str = $this->get_style(); // 載入 CSS/JS
for ($i = 0, $t = 1; $i <= $this->day_count; $i++) {
$current = $this->start + $i * 86400; // 當天時間戳
// 月初:輸出表頭與星期列
if (date('d', $current) == '01') {
$str .= $this->get_caption($i); // 年月標題
$str .= $this->get_week(); // 星期一至日
}
// 月首前空格
$str .= $this->get_space_front($i);
// 日期格
$str .= $this->get_date($i);
// 月尾後空格
$str .= $this->get_space_back($i);
// 換行與補齊行
$str .= $this->get_rows($i, $t);
// 月末結束表格
$str .= $this->get_endtab($i, $t);
}
return $str;
}
// 表頭:年月
private function get_caption($i) {
$current = $this->start + $i * 86400;
return ""
. date('Y年m月', $current)
. " ";
}
// 星期列
private function get_week() {
$str = "";
foreach ($this->week as $w => $name) {
$str .= "$name ";
}
return $str . " ";
}
// 月首前空格(根據星期幾)
private function get_space_front($i) {
$current = $this->start + $i * 86400;
if (date('d', $current) == '01') {
$str = (date('N', $current) == '1') ? "" : ""; // 週一換行
$empty = (int)date('N', $current) - 1; // 週一=0格,週日=6格
$str .= str_repeat(" ", $empty);
return $str;
}
return (date('N', $current) == '1') ? " " : ""; // 非月初只管換行
}
// 日期格(高亮:起始綠、結束紅、今日深紅)
private function get_date($i) {
$current = $this->start + $i * 86400;
$cls = 'date';
if (date('Ymd', $current) == "{$this->start_year}{$this->start_month}{$this->start_day}") $cls = 'start';
elseif (date('Ymd', $current) == "{$this->end_year}{$this->end_month}{$this->end_day}") $cls = 'end';
elseif (date('ymd', $current) == date('ymd')) $cls = 'today';
return "" . date('j', $current) . " ";
}
// 月尾後空格
private function get_space_back($i) {
$current = $this->start + $i * 86400;
if (date('d', $current) == date('t', $current)) {
$empty = 7 - (int)date('N', $current);
return str_repeat(" ", $empty);
}
return '';
}
// 換行與補齊 6 行
private function get_rows($i, &$t) {
$current = $this->start + $i * 86400;
$str = '';
if (date('N', $current) == '7') { // 週日換行
$str .= " ";
if (date('d', $current) == date('t', $current)) $t--; // 月末週日行數修正
$t++;
}
// 月末不足 6 行補空行
if (date('d', $current) == date('t', $current) && $t < 6) {
while ($t < 6) {
$str .= "" . str_repeat(" ", 7) . " ";
$t++;
}
}
return $str;
}
// 結束表格
private function get_endtab($i, &$t) {
$current = $this->start + $i * 86400;
if (date('d', $current) == date('t', $current)) {
$t = 1; // 重置行計數
return "
";
}
return '';
}
// 載入外部 CSS/JS
private function get_style() {
$str = "";
return $str;
}
<?php
require_once 'LampDate.class.php';
// 2024 年全年日曆
$cal = new LampDate('2024', '1', '1', '2024', '12', '31', 'calendar.css', 'calendar.js');
echo $cal->get_datetab();
// 指定範圍:2024 年 3 月 15 日至 5 月 20 日
$cal2 = new LampDate('2024', '3', '15', '2024', '5', '20');
echo $cal2->get_datetab();
table { border-collapse: collapse; margin: 20px auto; font-family: sans-serif; }
.caption { background: #5CACEE; color: white; font-weight: bold; height: 30px; font-size: 1.2em; }
.week { background: #B0E0E6; height: 30px; }
.week th { width: 40px; text-align: center; }
.date { height: 30px; text-align: center; border: 1px solid #eee; }
.empty { background: #EDEDED; }
.today { background: #EE2C2C; color: white; font-weight: bold; }
.start { background: #7CCD7C; color: white; }
.end { background: #EE6363; color: white; }
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.date, .today, .start, .end').forEach(td => {
td.style.cursor = 'pointer';
td.addEventListener('click', () => {
const dateText = td.textContent.trim();
const caption = td.closest('table').querySelector('.caption').textContent;
alert(`選中日期: ${caption} ${dateText}日`);
// 這裡可觸發 AJAX 載入當日行程...
});
});
});
// Carbon 範例
use Carbon\Carbon;
$period = CarbonPeriod::create('2024-01-01', '2024-12-31');
foreach ($period as $date) {
echo $date->format('Y-m-d l') . "\n";
}
透過 date()、strtotime()、時間戳運算,我們實現了功能完整的萬年曆類。理解「時間戳為核心、格式化為表現」的設計思想,能靈活應對各種日期計算與顯示需求。現代專案建議採用 DateTime 或 Carbon 等更高階抽象,減少手動時間戳運算的錯誤風險。