首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 日期時間處理大全:從時間戳到 DateTime 物件的現代最佳實踐

     編輯:PHP 綜合知識

核心概念:時間戳 vs DateTime 物件

PHP 有兩套並行的日期時間 API:

  • 過程式風格:`time()`、`date()`、`strtotime()`、`mktime()`、`strftime()` —— 返回整數時間戳或格式化字串,不處理時區物件,易在跨時區、DST 切換時出錯。
  • 物件導向風格:`DateTime`、`DateTimeImmutable`、`DateTimeZone`、`DateInterval`、`DatePeriod` —— 內建時區感知、不可變模式、鏈式操作,現代開發首選。

建議:全面遷移至 DateTime 系列,棄用 `date()`/`strtotime()` 等舊函數。

DateTime 基礎操作速查

任務代碼範例
建立當前時間(預設時區)`$dt = new DateTime();` 或 `new DateTimeImmutable();`
指定時區建立`$dt = new DateTime('now', new DateTimeZone('Asia/Taipei'));`
解析字串(支援相對時間)`$dt = new DateTime('2024-01-15 14:30:00');`
`$dt = new DateTime('+1 week');`
`$dt = new DateTime('first day of next month');`
格式化輸出`$dt->format('Y-m-d H:i:s');`
`$dt->format('U'); // 時間戳`
修改時區(不改變瞬間)`$dt->setTimezone(new DateTimeZone('UTC'));`
加減時間(可變)`$dt->add(new DateInterval('P1D'));`
`$dt->sub(new DateInterval('PT30M'));`
加減時間(不可變,推薦)`$newDt = $dt->add(new DateInterval('P1D'));` // 返回新實例
計算差值`$diff = $dt1->diff($dt2);` // 返回 DateInterval
`$diff->days, $diff->h, $diff->i, $diff->format('%a 天 %h 小時')`
比較大小`$dt1 > $dt2`、`$dt1 == $dt2`、`$dt1 < $dt2` 直接支援

DateInterval 週期表示法(ISO 8601)

P[n]Y[n]M[n]DT[n]H[n]M[n]S
// 範例
P1Y       // 1 年
P3M       // 3 個月
P2W       // 2 週
P10D      // 10 天
PT5H      // 5 小時
PT30M     // 30 分鐘
P1DT6H    // 1 天 6 小時

DatePeriod:生成規律性日期序列

$start = new DateTime('2024-01-01');
$interval = new DateInterval('P1D'); // 每天
$end = (new DateTime('2024-01-10'))->modify('+1 day'); // 包含結束日需 +1
$period = new DatePeriod($start, $interval, $end);

foreach ($period as $dt) {
    echo $dt->format('Y-m-d') . "\n";
}
// 輸出 2024-01-01 ~ 2024-01-10

時區處理最佳實踐

  1. 統一儲存 UTC:資料庫 `DATETIME`/`TIMESTAMP` 欄位皆存 UTC(或 `TIMESTAMP` 自動轉換),應用層統一 `new DateTimeZone('UTC')`。
  2. 顯示時轉換:根據用戶偏好時區(`Asia/Taipei`、`America/New_York`)在 View 層 `setTimezone()` 輸出。
  3. PHP.ini 設定:`date.timezone = UTC`,避免 `date()` 產生 E_WARNING。
  4. 夏令時(DST)安全:`DateTime` 內建處理 DST 切換,`strtotime('+1 day')` 在 DST 邊界可能出現 23/25 小時bug,改用 `$dt->add(new DateInterval('P1D'))`。

常見場景實戰範例

1. 計算年齡

function getAge(DateTime $birth): int {
    $today = new DateTime('today');
    $diff = $birth->diff($today);
    return $diff->y;
}

2. 本週一 00:00:00 與本週日 23:59:59

$monday = (new DateTime('monday this week'))->setTime(0,0,0);
$sunday = (new DateTime('sunday this week'))->setTime(23,59,59);

3. 判斷是否為今天/本月/今年

function isToday(DateTime $dt): bool {
    return $dt->format('Y-m-d') === (new DateTime())->format('Y-m-d');
}

4. 友善相對時間(如「3 分鐘前」、「昨天 14:30」)

function humanDiff(DateTime $dt): string {
    $now = new DateTime();
    $diff = $dt->diff($now);
    if ($diff->y) return $diff->format('%y 年前');
    if ($diff->m) return $diff->format('%m 個月前');
    if ($diff->d) {
        if ($diff->d == 1) return '昨天 ' . $dt->format('H:i');
        if ($diff->d < 7) return $diff->format('%d 天前');
        return $dt->format('Y-m-d');
    }
    if ($diff->h) return $diff->format('%h 小時前');
    if ($diff->i) return $diff->format('%i 分鐘前');
    return '剛剛';
}

5. 解析多種輸入格式(表單、API、CSV)

function parseFlexible(string $input): ?DateTime {
    $formats = [
        'Y-m-d H:i:s', 'Y-m-d', 'Y/m/d H:i:s', 'Y/m/d',
        'd-m-Y H:i:s', 'd.m.Y', 'U', // 時間戳
    ];
    foreach ($formats as $fmt) {
        $dt = DateTime::createFromFormat($fmt, $input, new DateTimeZone('UTC'));
        if ($dt !== false) return $dt;
    }
    // 最後嘗試 strtotime 相對格式
    $ts = strtotime($input);
    return $ts !== false ? (new DateTime('@'.$ts))->setTimezone(new DateTimeZone('UTC')) : null;
}

資料庫互動建議

  • MySQL `DATETIME`:無時區資訊,應用層負責 UTC ⇄ 用戶時區轉換。
  • MySQL `TIMESTAMP`:儲存 UTC,讀取自動轉換為 Session 時區(`SET time_zone = '+08:00'`),注意 2038 年問題。
  • PDO 綁定:`$stmt->bindValue(':dt', $dt->format('Y-m-d H:i:s'));` 或直接綁定 `DateTime` 物件(PHP 8.1+ 支援)。

效能與陷阱提醒

陷阱正確做法
在循環中重複 `new DateTimeZone()`建立一次 `$tz = new DateTimeZone('Asia/Taipei');` 重複使用
用 `date('Y-m-d', strtotime('+1 month'))` 處理月底用 `$dt->modify('first day of +1 month')` 或 `DateInterval`
直接比較格式化字串 `$dt1->format('Ymd') > $dt2->format('Ymd')`直接比較物件 `$dt1 > $dt2`
忘記 `DateTimeImmutable` 導致副作用函數參數/返回值優先用 `DateTimeImmutable`,除非需就地修改

結語

PHP 的 `DateTime` 系列功能強大、符合國際標準、時區安全。掌握 物件導向 API、ISO 8601 週期、DatePeriod 序列、UTC 儲存策略,即可從容應對 99% 的日期時間業務。拋棄舊式 `date()`/`strtotime()`,擁抱現代、可測試、可維護的時間處理方式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved