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

PHP 计算两个时间的差值:天/时/分/秒精确计算与 DateTime 最佳实践

     編輯:PHP 綜合知識

现代推荐:DateTime + DateInterval (PHP 5.3+)

摒弃 `strtotime` + 手工除以 86400/3600 的旧写法。DateTime 原生支持时区、闰年、夏令时、微秒,代码更健壮、可读性强。

核心函数封装

<?php
/**
 * 计算两个时间点的差值
 * @param string|DateTime $start 开始时间
 * @param string|DateTime $end   结束时间 (默认现在)
 * @param string $timezone       时区 (默认 PRC)
 * @return array ['days'=>, 'hours'=>, 'minutes'=>, 'seconds'=>, 'total_seconds'=>, 'human'=>]
 */
function timeDiff($start, $end = 'now', string $timezone = 'PRC'): array
{
    $tz = new DateTimeZone($timezone);
    $dtStart = $start instanceof DateTime ? $start : new DateTime($start, $tz);
    $dtEnd   = $end   instanceof DateTime ? $end   : new DateTime($end, $tz);

    if ($dtStart > $dtEnd) {
        // 交换并标记负数
        [$dtStart, $dtEnd] = [$dtEnd, $dtStart];
        $negative = true;
    } else {
        $negative = false;
    }

    /** @var DateInterval $diff */
    $diff = $dtStart->diff($dtEnd);

    $totalSeconds = $dtEnd->getTimestamp() - $dtStart->getTimestamp();

    $result = [
        'years'         => $diff->y,
        'months'        => $diff->m,
        'days'          => $diff->d,
        'hours'         => $diff->h,
        'minutes'       => $diff->i,
        'seconds'       => $diff->s,
        'total_days'    => (int)$diff->format('%a'), // 总天数 (含年月折算)
        'total_seconds' => $totalSeconds,
        'negative'      => $negative,
        'human'         => $diff->format('%y年 %m月 %d天 %h小时 %i分 %s秒'),
    ];

    return $result;
}

使用示例

// 1. 字符串时间
$diff = timeDiff('2023-01-01 00:00:00', '2023-12-31 23:59:59');
print_r($diff);
/*
Array
(
    [years] => 0
    [months] => 11
    [days] => 30
    [hours] => 23
    [minutes] => 59
    [seconds] => 59
    [total_days] => 364
    [total_seconds] => 31535999
    [negative] =>
    [human] => 0年 11月 30天 23小时 59分 59秒
)
*/

// 2. 仅想要总天数/总小时/总分钟
$start = new DateTime('2023-06-01 10:00:00');
$end   = new DateTime('2023-06-03 14:30:00');
$diff  = $start->diff($end);

echo "总天数: " . $diff->format('%a') . "\n";       // 2
echo "总小时: " . ($diff->days * 24 + $diff->h) . "\n"; // 52
echo "总分钟: " . (($diff->days * 24 + $diff->h) * 60 + $diff->i) . "\n"; // 3150

// 3. 倒计时场景:距离目标时间还剩多久
$target = new DateTime('2024-01-01 00:00:00');
$now    = new DateTime();
$diff   = $now->diff($target);
if ($now < $target) {
    echo "距离新年: {$diff->format('%a天 %h小时 %i分 %s秒')}";
}

旧写法对比 (仅作了解)

// 不推荐:手工计算易错、不支持时区、无闰秒处理
$start = strtotime('2023-01-01 00:00:00');
$end   = strtotime('2023-12-31 23:59:59');
$diff  = $end - $start;

$days    = intval($diff / 86400);
$hours   = intval(($diff % 86400) / 3600);
$minutes = intval(($diff % 3600) / 60);
$seconds = $diff % 60;

常见陷阱与避坑指南

陷阱原因正确做法
$diff->days 与 $diff->format('%a') 不同days 仅为日期部分差值,不含年月折算总天数用 format('%a') 或 $diff->days + $diff->m*30 + $diff->y*365 (近似)
跨夏令时边界计算错误strtotime 使用系统时区,可能跳变 1 小时显式指定 new DateTimeZone('Asia/Shanghai') (无夏令时)
微秒丢失DateTime::diff 仅精确到秒用 $dtEnd->getTimestamp() - $dtStart->getTimestamp() 获取总秒数,或用 microtime(true)
负数差值开始时间 > 结束时间先比较大小再 diff,或检查 $diff->invert 属性

高性能场景:仅需总秒数

function secondsBetween(string $start, string $end): int
{
    // 直接用时间戳差值,最快、最简单
    return strtotime($end) - strtotime($start);
}

// 或 DateTime (可指定时区)
function secondsBetweenDt(DateTime $start, DateTime $end): int
{
    return $end->getTimestamp() - $start->getTimestamp();
}

结语

永远使用 DateTime/diff 处理时间差。它封装了复杂的日历逻辑,支持时区、格式化输出、倒计时、年龄计算等所有场景。手工除以 86400 的代码已属技术债,重构时请全部替换。

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