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

PHP 計算兩個日期相差天數的標準與高精度方案

     編輯:PHP 相關知識

跨時區、跨夏令時、閏秒:日期差值計算的避坑指南

看似簡單的「兩個日期相差天數」,實際涉及時區偏移、夏令時切換、閏年閏秒等邊界條件。本文從時間戳減法進階到 DateTime/DateInterval 標準庫方案,給出生產環境可靠的計算代碼。

方案一:時間戳減法(簡單但有陷阱)

<?php
/**
 * 簡易版:基於 UTC 時間戳相減
 * ⚠️ 缺陷:忽略時區、夏令時導致邊界日誤差 ±1 天
 */
function diffDaysSimple(string $day1, string $day2): float
{
    $ts1 = strtotime($day1);
    $ts2 = strtotime($day2);
    if ($ts1 === false || $ts2 === false) {
        throw new InvalidArgumentException('無效日期格式');
    }
    // 絕對值天數,不考慮方向
    return abs($ts1 - $ts2) / 86400;
}

// 測試
echo diffDaysSimple('2013-07-27', '2013-08-04'); // 8

方案二:DateTime + DateInterval(推薦標準做法)

<?php
/**
 * 標準版:支援時區、夏令時、閏年,返回整數天數差
 * @param string $date1 格式如 '2023-03-15' 或 '2023-03-15 14:30:00'
 * @param string $date2
 * @param string $timezone 時區標識,如 'Asia/Shanghai'、'UTC'
 * @return int 日期差($date1 - $date2),可為負
 */
function diffDaysStandard(string $date1, string $date2, string $timezone = 'UTC'): int
{
    $tz = new DateTimeZone($timezone);
    $dt1 = new DateTime($date1, $tz);
    $dt2 = new DateTime($date2, $tz);

    // 將時間歸零到 00:00:00,純比較「日曆日期」
    $dt1->setTime(0, 0, 0);
    $dt2->setTime(0, 0, 0);

    $interval = $dt1->diff($dt2);
    // invert=1 表示 $dt1 < $dt2,結果為負
    return $interval->invert ? -$interval->days : $interval->days;
}

// 夏令時邊界測試(美東時間 2024-03-10 2:00 跳轉 3:00)
echo diffDaysStandard('2024-03-09', '2024-03-11', 'America/New_York'); // 2 天(而非 1.958...)
echo diffDaysStandard('2023-02-28', '2024-02-28', 'UTC'); // 366 天(閏年)

方案三:Carbon 庫(現代 PHP 專案首選)

<?php
// composer require nesbot/carbon
use Carbon\Carbon;

$days = Carbon::parse('2023-01-01')->diffInDays('2023-12-31'); // 364
$daysAbs = Carbon::parse('2023-01-01')->diffInDays('2023-12-31', true); // 絕對值 364

// 極其豐富的語義化方法
$dt = Carbon::parse('2023-05-20');
echo $dt->diffForHumans(); // "5 months ago"
echo $dt->addDays(10)->isWeekend(); // false

核心差異對比表

維度strtotime 減法DateTime::diffCarbon
時區感知❌ 依賴 php.ini date.timezone✅ 顯式傳入 DateTimeZone✅ 全鏈路不可變時區
夏令時正確❌ 23/25 小時日誤差✅ 內部用 UTC 計算再轉換✅ 同 DateTime
閏秒處理❌ 忽略❌ 忽略(同 Unix 時間戳)❌ 忽略
返回類型float(含小數天)int(整數天)int/float 可選
依賴零依賴零依賴(PHP 5.3+)Composer 包

業務場景選型建議

  • 簡單報表/日志分析:UTC 環境下用 diffDaysSimple 足夠,性能最高。
  • 用戶面向功能(會員天數、訂單超時):必須用 diffDaysStandard 指定用戶時區,避免「跨日 0 點」邏輯錯誤。
  • 新項目/長期維護:直接引入 Carbon,統一時間處理風格,減少自造輪子 Bug。
  • 金融級結算:需引入 leap-seconds.list 或使用專業時間庫(如 PHP-ICU),標準庫不支援閏秒。

常見錯誤與修正

錯誤:floor((strtotime($d1) - strtotime($d2)) / 86400)
原因:夏令時切換日實際秒數 ≠ 86400,導致跨越切換日時差 ±1。
修正:統一用 DateTime::setTime(0,0,0) 歸零後再 diff()。

日期計算無小事,選對工具、鎖定時區、測試邊界,才能讓「相差天數」在所有業務場景下精準可信。

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