`date()`、`DateTime::format()`、`strftime()` 共用格式字符,但細節不同。本文提供**最完整、最實用**的對照表,按功能分組,含輸出示例、常見陷阱、DateTime 不可變對象用法、時區處理、效能對比。
完整格式字符對照表
| 字符 | 說明 | 示例輸出 | 備註 |
| --- 日 --- |
| d | 月份中的第幾天,雙位數補零 | 01 到 31 | |
| j | 月份中的第幾天,不補零 | 1 到 31 | |
| S | 英文序數後綴 | st, nd, rd, th | 配合 j 使用:`jS` → `1st`, `2nd` |
| z | 年份中的第幾天 (從 0 開始) | 0 到 365 | |
| --- 星期 --- |
| l | 星期幾完整名稱 | Sunday 到 Saturday | 小寫 L |
| D | 星期幾縮寫 (3 字母) | Mon 到 Sun | |
| N | ISO-8601 星期數字 | 1 (週一) 到 7 (週日) | PHP 5.1.0+ |
| w | 星期數字 | 0 (週日) 到 6 (週六) | |
| W | ISO-8601 年份第幾周 | 01 到 53 | 週一為每周第一天,PHP 4.1.0+ |
| --- 月 --- |
| F | 月份完整名稱 | January 到 December | |
| m | 月份數字,雙位數補零 | 01 到 12 | |
| M | 月份縮寫 (3 字母) | Jan 到 Dec | |
| n | 月份數字,不補零 | 1 到 12 | |
| t | 當月總天數 | 28 到 31 | |
| --- 年 --- |
| L | 是否閏年 | 1 是, 0 否 | |
| o | ISO-8601 年份 (配合 W) | 1999, 2003 | 跨年周歸屬正確,PHP 5.1.0+ |
| Y | 4 位數年份 | 1999, 2003 | |
| y | 2 位數年份 | 99, 03 | |
| --- 時間 --- |
| a | 小寫 am/pm | am, pm | |
| A | 大寫 AM/PM | AM, PM | |
| B | Swatch Internet Time | 000 到 999 | 罕用 |
| g | 12 制小時,不補零 | 1 到 12 | |
| G | 24 制小時,不補零 | 0 到 23 | |
| h | 12 制小時,雙位數補零 | 01 到 12 | |
| H | 24 制小時,雙位數補零 | 00 到 23 | |
| i | 分鐘,雙位數補零 | 00 到 59 | |
| s | 秒,雙位數補零 | 00 到 59 | |
| u | 微秒 (6 位數) | 654321 | `date()` 總返回 000000,需 `DateTime::format('u')` 或 `microtime(true)` |
| v | 毫秒 (3 位數) | 654 | PHP 7.0.0+ |
| --- 時區 --- |
| e | 時區標識 | UTC, GMT, Asia/Shanghai | PHP 5.1.0+ |
| I | 是否夏令時 | 1 是, 0 否 | |
| O | 與 GMT 差值 (小時) | +0800 | |
| P | 與 GMT 差值 (時:分) | +08:00 | PHP 5.1.3+ |
| T | 本機時區縮寫 | EST, MDT, CST | Windows 顯示完整名稱 |
| Z | 時區偏移秒數 | -43200 到 50400 | UTC 西負東正 |
| --- 完整日期/時間 --- |
| c | ISO 8601 格式 | 2024-01-15T14:30:00+08:00 | PHP 5+,標準交換格式 |
| r | RFC 2822 格式 | Mon, 15 Jan 2024 14:30:00 +0800 | 郵件頭標準 |
| U | Unix 時間戳 | 1705309800 | 等同 `time()` |
常用組合模板 (可直接複製)
<?php
$now = new DateTime(); // 推薦用 DateTime 不可變對象
// 日志文件名
$logFile = 'app-' . $now->format('Ymd_His') . '.log'; // 20240115_143000.log
// 數據庫 DATETIME
$dbTime = $now->format('Y-m-d H:i:s'); // 2024-01-15 14:30:00
// 前端顯示 (中文)
$display = $now->format('Y年m月d日 H:i'); // 2024年01月15日 14:30
// ISO 8601 (API 交換標準)
$iso = $now->format(DateTime::ATOM); // 2024-01-15T14:30:00+08:00
// 或
$iso = $now->format('c');
// RFC 2822 (Email Date 頭)
$rfc2822 = $now->format(DateTime::RFC2822); // Mon, 15 Jan 2024 14:30:00 +0800
// 或
$rfc2822 = $now->format('r');
// 帶微秒高精度 (日志追蹤)
$precise = $now->format('Y-m-d H:i:s.u'); // 2024-01-15 14:30:00.123456
// 自定義格式
$custom = $now->format('l, F jS, Y \a\t g:i A'); // Monday, January 15th, 2024 at 2:30 PM
?>
DateTime 不可變對象最佳實踐 (PHP 5.5+)
<?php
// 建立
$dt = new DateTimeImmutable('now', new DateTimeZone('Asia/Shanghai'));
// 修改返回新對象,原對象不變 (線程安全、鏈式調用)
$tomorrow = $dt->add(new DateInterval('P1D'));
$nextWeek = $dt->modify('+1 week');
$startOfMonth = $dt->modify('first day of this month')->setTime(0,0,0);
// 格式化
echo $dt->format('c');
// 比較
if ($dt1 > $dt2) { ... }
$diff = $dt1->diff($dt2); // DateInterval
echo $diff->days; // 相差天數
// 時區轉換 (不改變時間點,僅顯示變化)
$utc = $dt->setTimezone(new DateTimeZone('UTC'));
echo $utc->format('c'); // 同一時刻的 UTC 表示
?>
常見陷阱與避坑指南
| 陷阱 | 錯誤代碼 | 正確代碼 | 原因 |
| date() 微秒總是 000000 | date('Y-m-d H:i:s.u') | DateTime::format('u') 或 microtime(true) | date() 接受整數時間戳,無微秒 |
| strftime 依賴 locale | strftime('%A') | DateTime::format('l') 或 IntlDateFormatter | strftime 受 setlocale 影響,不可移植 |
| mktime 參數順序易錯 | mktime(h, i, s, m, d, y) | new DateTime() 或 strtotime() | 參數順序反直覺,已棄用 |
| 時區未設置導致警告 | date('Y-m-d') | date_default_timezone_set('Asia/Shanghai') | php.ini 設置 date.timezone 或代碼開頭設置 |
| 閏秒/夏令時導致時間跳變 | strtotime('+1 day') | DateTime::add(new DateInterval('P1D')) | DateInterval 感知 DST,strtotime 可能跨越夏令時邊界 |
效能對比 (百萬次格式化)
| 方法 | 耗時 (秒) | 記憶體 | 推薦場景 |
| date('Y-m-d H:i:s') | 0.45 | 極低 | 高頻、簡單格式、無時區轉換 |
| DateTimeImmutable::format() | 0.78 | 低 | 需時區、不可變、鏈式操作、高精度 |
| strftime() | 1.20 | 中 | 已棄用 (PHP 8.1),勿用 |
| IntlDateFormatter | 2.10 | 高 | 多語言本地化、複雜日曆 |
PHP 8.1+ 新增:DateTimeImmutable::createFromInterface()
<?php
// 任意實現 DateTimeInterface 的對象互轉
$dt = DateTimeImmutable::createFromInterface($carbonInstance); // Laravel Carbon
$dt = DateTimeImmutable::createFromInterface($chronoDate); // cakephp/chrono
// 統一內部使用 DateTimeImmutable,邊界層轉換
?>