32 位元系統 `time_t` 為有號 32 位元整數,最大值 `2147483647` 對應 **2038-01-19 03:14:07 UTC**。超過此時間戳會溢位回繞至 1901 年。PHP 在 64 位元建置上已原生支援 > 2038 年,但 32 位元建置、Windows 早期版本、或舊函式 `strtotime()`/`date()` 仍受限。
PHP 5.2+ `DateTime` 內部使用 64 位元時間戳,**不受 2038 年限制**(需 64 位元 PHP 或 PHP 7.1+ 修正)。
<?php
// 正確處理 2038 年後日期
$dt = new DateTime('@2147483648'); // 2038-01-19 03:14:08 UTC
echo $dt->format('Y-m-d H:i:s'); // 正常輸出
// 甚至支援極遠未來
$dt = new DateTime('@9999999999'); // 2286-11-20
echo $dt->format('Y-m-d');
若專案大量使用 `strtotime()`、`date()`,可用以下 Drop-in 替換函式:
<?php
/**
* 相容 strtotime(),支援 2038 年後
* @param string $time 相對時間字串或時間戳
* @param int|null $now 基準時間戳
* @return int|false 時間戳,失敗回傳 false
*/
function safeStrtotime(string $time, int $now = null): int|false
{
try {
$dt = new DateTime($time);
if ($now !== null) {
$dt->setTimestamp($now);
$dt->modify($time);
}
return $dt->getTimestamp();
} catch (Exception) {
return false;
}
}
/**
* 相容 date(),支援 2038 年後
* @param string $format 格式字串
* @param int|string $timestamp 時間戳或 '@timestamp'
* @return string 格式化日期
*/
function safeDate(string $format, int|string $timestamp = 'now'): string
{
try {
if (is_numeric($timestamp)) {
$dt = new DateTime('@' . $timestamp);
} else {
$dt = new DateTime($timestamp);
}
$dt->setTimezone(new DateTimeZone(date_default_timezone_get()));
return $dt->format($format);
} catch (Exception) {
return '';
}
}
/**
* 相容 mktime()/gmmktime()
*/
function safeMktime(int $hour=0, int $min=0, int $sec=0, int $month=1, int $day=1, int $year=1970): int
{
try {
$dt = new DateTime();
$dt->setDate($year, $month, $day);
$dt->setTime($hour, $min, $sec);
$dt->setTimezone(new DateTimeZone('UTC'));
return $dt->getTimestamp();
} catch (Exception) {
return 0;
}
}
// 使用範例
echo safeDate('Y-m-d H:i:s', 2147483648) . "\n"; // 2038-01-19 11:14:08 (UTC+8)
echo safeStrtotime('+100 years') . "\n"; // 遠未來時間戳
<?php
// 檢測是否為 64 位元建置
echo 'PHP_INT_SIZE: ' . PHP_INT_SIZE . ' bytes (' . (PHP_INT_SIZE * 8) . ' bits)' . "\n";
echo 'PHP_INT_MAX: ' . PHP_INT_MAX . "\n";
// 測試 strtotime 極限
$max32 = 2147483647;
echo 'strtotime 2038-01-19: ' . strtotime('2038-01-19') . "\n";
echo 'DateTime 2038-01-19: ' . (new DateTime('2038-01-19'))->getTimestamp() . "\n";
// 若 strtotime 回傳 false 或負數,表示 32 位元限制
if (strtotime('2050-01-01') === false) {
echo "⚠️ 當前環境 strtotime 受 2038 年限制,請改用 DateTime 或安全封裝函式\n";
}
**最佳實務**:全專案統一使用 `DateTime` / `DateTimeImmutable` 物件操作時間,僅在與外部系統交互(API、資料庫、序列化)時轉為 ISO 8601 字串或 64 位元整數時間戳。棄用 `strtotime()`、`date()`、`mktime()` 等舊式函式。