PHP 中 date() 與 gmdate() 兩個時間格式化函數常讓開發者困惑,特別是跨平台(Linux/Windows)部署時的行為差異。本文深入解析兩者區別、時區機制,並提供生產環境的最佳實踐方案。
| 函數 | 說明 | 參考基準 |
|---|---|---|
date() | 格式化本地時間 | PHP 內建時區設定(date.timezone) |
gmdate() | 格式化 GMT/UTC 時間 | 格林威治標準時間(不受時區設定影響) |
假設伺服器時區為 Asia/Taipei (UTC+8),當前 UTC 時間 2024-01-15 04:30:00:
<?php
// Linux + Apache 環境
date_default_timezone_set('Asia/Taipei');
echo date('Y-m-d H:i:s'); // 2024-01-15 12:30:00 (本地時間 UTC+8)
echo gmdate('Y-m-d H:i:s'); // 2024-01-15 04:30:00 (UTC 時間)
// Windows 環境(舊版 PHP 行為不同)
// 早期 Windows PHP 預設時區為 UTC,導致 date() 也輸出 UTC 時間
// 現代 PHP 7.1+ 已統一行為,建議明確設定時區
歷史版本中,Windows PHP 預設不讀取系統時區,導致 date() 行為與 Linux 不一致。雖然 PHP 5.4+ 已大幅改善,但生產環境務必顯式設定時區,避免依賴預設值。
<?php
// 寫入資料庫:永遠存 UTC timestamp (INT) 或 UTC datetime
$created_at = time(); // 或 gmdate('Y-m-d H:i:s')
// INSERT INTO posts (created_at) VALUES ($created_at)
<?php
// 使用者偏好時區(註冊時選擇或自動偵測)
$user_timezone = 'America/New_York'; // UTC-5 (冬令時間)
// 讀取資料庫 UTC 時間戳
$utc_timestamp = 1705302600; // 2024-01-15 04:30:00 UTC
// 轉換為使用者本地時間
$dt = new DateTime('@' . $utc_timestamp);
$dt->setTimezone(new DateTimeZone($user_timezone));
echo $dt->format('Y-m-d H:i:s'); // 2024-01-14 23:30:00 (前一天晚上)
// 前端 JavaScript 也可直接處理
// new Date(utc_timestamp * 1000).toLocaleString()
| 方式 | 適用場景 | 優先級 |
|---|---|---|
php.ini: date.timezone = "Asia/Taipei" | 獨立伺服器、容器環境 | 最高(全域生效) |
date_default_timezone_set('Asia/Taipei') | 共享主機、框架啟動檔 | 次高(執行期生效) |
ini_set('date.timezone', 'Asia/Taipei') | 臨時覆蓋、測試用 | 最低(僅當前腳本) |
Asia/Taipei(含夏令時間歷史規則)Asia/Shanghai、PRCAsia/Hong_KongUTC、Etc/GMT(注意:Etc/GMT-8 實際表示 UTC+8,POSIX 標準反向)echo gmdate('Y-m-d H:i:s', time() + 3600 * 8); // 硬編碼 +8 小時
問題:無法處理夏令時間(DST)、時區規則變更、跨地區使用者。
$utc = new DateTime('now', new DateTimeZone('UTC'));
$taipei = clone $utc;
$taipei->setTimezone(new DateTimeZone('Asia/Taipei'));
echo $taipei->format('Y-m-d H:i:s');
<?php
// Laravel config/app.php
'timezone' => 'UTC', // 應用層統一用 UTC
// 模型存取器自動轉換
protected function createdAt(): Attribute {
return Attribute::make(
get: fn ($value) => $this->convertToUserTimezone($value),
);
}
private function convertToUserTimezone($utc) {
if (!$utc) return null;
$userTz = auth()->user()?->timezone ?? 'Asia/Taipei';
return Carbon::parse($utc, 'UTC')->setTimezone($userTz);
}
DateTime + DateTimeZonedate_default_timezone_set('UTC') 統一基準掌握時區處理核心邏輯,能從根源消除「時間對不上」這類難以重現的生產環境 Bug。