跨時區應用中,服務器統一存儲 GMT/UTC 時間,客戶端需根據用戶所在時區動態顯示本地時間。本文提供一套「前端檢測 → Cookie 傳遞 → 後端轉換」的完整方案。
<script>
window.onload = function() {
// 1. 獲取時區偏移(小時),東區為正,西區為負
var exp = new Date();
var gmtHours = -(exp.getTimezoneOffset() / 60); // getTimezoneOffset 返回分鐘差(UTC - 本地)
setCookie('customer_timezone', gmtHours, 1); // 存儲 1 天
// 2. 判斷夏令時
var dateStr = formatDate(exp, 'yyyy-MM-dd HH:mm:ss');
if (inDaylightTime(dateStr)) {
setCookie('inDaylightTime', 1, 1);
}
};
// 格式化日期輔助
function formatDate(date, fmt) {
var o = {
"M+": date.getMonth() + 1,
"d+": date.getDate(),
"H+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds()
};
for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (o[k] < 10 ? "0" : "") + o[k]);
return fmt.replace(/(y+)/, date.getFullYear());
}
// 設置 Cookie
function setCookie(name, value, days) {
var exp = new Date();
exp.setDate(exp.getDate() + days);
document.cookie = name + "=" + escape(value) + "; expires=" + exp.toGMTString() + "; path=/";
}
// 判斷東/西半球
function isEastEarthTime(date) {
var offset = date.getTimezoneOffset(); // 分鐘
return offset <= 0; // 東區 offset 為負或零
}
// 夏令時判斷邏輯:對比年初與年中時區偏移
function inDaylightTime(dateStr) {
var date = new Date(dateStr);
var jan = new Date(date.getFullYear(), 0, 1);
var jul = new Date(date.getFullYear(), 6, 1);
var janOffset = jan.getTimezoneOffset();
var julOffset = jul.getTimezoneOffset();
if (janOffset === julOffset) return false; // 無夏令時
var stdOffset = isEastEarthTime(date) ? julOffset : janOffset; // 標準時間偏移
return date.getTimezoneOffset() !== stdOffset; // 當前偏移 ≠ 標準偏移即為夏令時
}
</script>
<?php
/**
* GMT 時間戳轉換為用戶本地時間(僅返回 H:i)
* @param int|string $time GMT 時間戳
* @param bool $dst 是否強制夏令時(由 Cookie 決定)
* @return string 格式化後的本地時間
*/
function gmt_to_local($time = '', $dst = false) {
if ($time === '') return date('H:i'); // 默認當前時間
// 1. 讀取時區偏移
$timezone = isset($_COOKIE['customer_timezone']) ? floatval($_COOKIE['customer_timezone']) : 0;
// 2. 基礎偏移轉換
$time += $timezone * 3600;
// 3. 夏令時額外 +1 小時
if (isset($_COOKIE['inDaylightTime']) && $_COOKIE['inDaylightTime'] == '1') {
$dst = true;
}
if ($dst) $time += 3600;
return date('H:i', $time);
}
// 使用示例
$gmtTime = strtotime('2024-01-01 12:00:00'); // 假設數據庫存儲的 GMT 時間
echo "用戶本地時間: " . gmt_to_local($gmtTime);
?>
| 優點 | 缺點/風險 |
|---|---|
| 純前端檢測,無需用戶手動選擇時區 | 依賴 Cookie,用戶禁用 Cookie 則失效 |
| 夏令時自動判斷,無需維護時區數據庫 | JS 夏令時判斷邏較簡略,極少數地區規則特殊可能誤判 |
| 服務端無狀態,易於擴展 | 首次訪問無 Cookie 時默認 GMT,體驗不佳 |
Intl.DateTimeFormat().resolvedOptions().timeZone 獲取如 Asia/Shanghai,傳給後端。new DateTime('@'.$timestamp, new DateTimeZone($tz))->format('Y-m-d H:i'),自動處理夏令時、歷史規則變更。