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

PHP 計算相對時間顯示:幾秒前、幾分鐘前、幾小時前、幾天前完整函數與類庫

     編輯:PHP 相關知識

社交動態、評論系統、文章列表常需顯示「3分鐘前」、「2天前」等相對時間。本文整理 4 種函數實作與 1 種類實作,涵蓋基本版、陣列映射版、精確計算版、多語系擴展版,滿足不同精度需求。

實作一:基本條件判斷版 (最直觀)

<?php
function time_tran($the_time) {
    $now_time = strtotime(date('Y-m-d H:i:s'));  // 當前時間戳
    $show_time = strtotime($the_time);           // 目標時間戳
    $diff = $now_time - $show_time;              // 秒差
    
    if ($diff < 0) return $the_time;  // 未來時間直接返回原字串
    
    if ($diff < 60) {
        return $diff . '秒前';
    } elseif ($diff < 3600) {
        return floor($diff / 60) . '分鐘前';
    } elseif ($diff < 86400) {
        return floor($diff / 3600) . '小時前';
    } elseif ($diff < 259200) {  // 3 天內
        return floor($diff / 86400) . '天前';
    } else {
        return $the_time;  // 超過 3 天顯示完整時間
    }
}

// 使用
echo time_tran('2024-01-15 10:30:00');  // 輸出: 2小時前 (視當前時間)

實作二:陣列映射版 (程式碼簡潔、易擴展) ✅ 推薦

<?php
function format_date($timestamp) {
    $diff = time() - $timestamp;
    
    if ($diff < 0) return date('Y-m-d H:i:s', $timestamp);
    
    $intervals = [
        31536000 => '年',
        2592000  => '個月',
        604800   => '星期',
        86400    => '天',
        3600     => '小時',
        60       => '分鐘',
        1        => '秒'
    ];
    
    foreach ($intervals as $seconds => $unit) {
        $count = floor($diff / $seconds);
        if ($count > 0) {
            return $count . $unit . '前';
        }
    }
    
    return '剛剛';
}

// 使用 (傳入時間戳)
echo format_date(strtotime('-2 hours'));  // 2小時前

實作三:精確分級計算版 (邏輯清晰、邊界處理完善)

<?php
function formatTime($date) {
    $timer = strtotime($date);
    $diff = $_SERVER['REQUEST_TIME'] - $timer;  // 使用請求開始時間,效能較佳
    
    if ($diff < 0) return $date;
    
    $day = floor($diff / 86400);
    $remainder = $diff % 86400;
    
    if ($day > 0) {
        return $day . '天前';
    }
    
    $hour = floor($remainder / 3600);
    $remainder %= 3600;
    if ($hour > 0) return $hour . '小時前';
    
    $min = floor($remainder / 60);
    $sec = $remainder % 60;
    if ($min > 0) return $min . '分鐘前';
    if ($sec > 0) return $sec . '秒前';
    
    return '剛剛';
}

實作四:支援未來時間、多語系、可配置閾值的進階版

<?php
class RelativeTime {
    private $locale = 'zh_TW';
    private $thresholds = [
        'just_now' => 5,      // 5 秒內顯示「剛剛」
        'minute'   => 60,
        'hour'     => 3600,
        'day'      => 86400,
        'week'     => 604800,
        'month'    => 2592000,
        'year'     => 31536000,
    ];
    
    private $labels = [
        'zh_TW' => [
            'just_now' => '剛剛',
            'seconds_ago' => '{count}秒前',
            'minutes_ago' => '{count}分鐘前',
            'hours_ago'   => '{count}小時前',
            'days_ago'    => '{count}天前',
            'weeks_ago'   => '{count}週前',
            'months_ago'  => '{count}個月前',
            'years_ago'   => '{count}年前',
            'future'      => '{count}秒後',
        ],
        'en' => [
            'just_now' => 'just now',
            'seconds_ago' => '{count} seconds ago',
            'minutes_ago' => '{count} minutes ago',
            'hours_ago'   => '{count} hours ago',
            'days_ago'    => '{count} days ago',
            'weeks_ago'   => '{count} weeks ago',
            'months_ago'  => '{count} months ago',
            'years_ago'   => '{count} years ago',
            'future'      => 'in {count} seconds',
        ]
    ];
    
    public function __construct($locale = 'zh_TW') {
        $this->locale = $locale;
    }
    
    public function setThreshold($key, $seconds) {
        if (isset($this->thresholds[$key])) {
            $this->thresholds[$key] = $key;
        }
    }
    
    public function format($datetime, $now = null) {
        $now = $now ?: $_SERVER['REQUEST_TIME'];
        $target = is_numeric($datetime) ? $datetime : strtotime($datetime);
        $diff = $now - $target;
        
        $labels = $this->labels[$this->locale] ?? $this->labels['zh_TW'];
        $t = $this->thresholds;
        
        if (abs($diff) < $t['just_now']) {
            return $labels['just_now'];
        }
        
        $is_future = $diff < 0;
        $diff = abs($diff);
        
        if ($diff < $t['minute']) {
            $count = $diff;
            $template = $is_future ? $labels['future'] : $labels['seconds_ago'];
        } elseif ($diff < $t['hour']) {
            $count = floor($diff / 60);
            $template = $is_future ? $labels['future'] : $labels['minutes_ago'];
        } elseif ($diff < $t['day']) {
            $count = floor($diff / 3600);
            $template = $is_future ? $labels['future'] : $labels['hours_ago'];
        } elseif ($diff < $t['week']) {
            $count = floor($diff / 86400);
            $template = $is_future ? $labels['future'] : $labels['days_ago'];
        } elseif ($diff < $t['month']) {
            $count = floor($diff / 604800);
            $template = $is_future ? $labels['future'] : $labels['weeks_ago'];
        } elseif ($diff < $t['year']) {
            $count = floor($diff / 2592000);
            $template = $is_future ? $labels['future'] : $labels['months_ago'];
        } else {
            $count = floor($diff / 31536000);
            $template = $is_future ? $labels['future'] : $labels['years_ago'];
        }
        
        return str_replace('{count}', $count, $template);
    }
}

// 使用範例
$rt = new RelativeTime('zh_TW');
echo $rt->format('-30 minutes');      // 30分鐘前
echo $rt->format('+2 hours');         // 2小時後
echo $rt->format('2024-01-01');       // 幾天前/幾個月前
echo $rt->format(time());             // 剛剛

$rt_en = new RelativeTime('en');
echo $rt_en->format('-1 day');        // 1 day ago

類實作:MyGetTime (原文提供的 OOP 版本)

<?php
/**
 * 發布時間計算類
 * @author Solon Ring
 */
class MyGetTime {
    private $createtime;
    private $gettime;
    
    public function __construct($createtime, $gettime) {
        $this->createtime = $createtime;
        $this->gettime = $gettime;
    }
    
    private function diff() { return $this->createtime - $this->gettime; }
    
    public function getYears()   { return $this->diff() / 31536000; }
    public function getMonths()  { return $this->diff() / 2592000; }
    public function getWeeks()   { return $this->diff() / 604800; }
    public function getDays()    { return $this->diff() / 86400; }
    public function getHours()   { return $this->diff() / 3600; }
    public function getMinutes() { return $this->diff() / 60; }
    public function getSeconds() { return $this->diff(); }
    
    public function index() {
        if ($this->getYears() > 1) {
            return $this->getYears() > 2 
                ? date('Y-m-d', $this->gettime)  // 超過 2 年顯示日期
                : intval($this->getYears()) . ' 年前';
        }
        if ($this->getMonths() > 1) return intval($this->getMonths()) . ' 月前';
        if ($this->getDays() > 1)     return intval($this->getDays()) . ' 天前';
        if ($this->getHours() > 1)    return intval($this->getHours()) . ' 小時前';
        if ($this->getMinutes() > 1)  return intval($this->getMinutes()) . ' 分鐘前';
        if ($this->getSeconds() > 1)  return intval($this->getSeconds()) . ' 秒前';
        return '剛剛';
    }
}

// 使用
$a = new MyGetTime(time(), strtotime('-25 month'));
echo $a->index();  // 2 年前 (或顯示日期)

效能與選擇建議

實作版本程式碼行數擴展性多語系適用場景
基本判斷版~20低否簡單專案、快速原型
陣列映射版~15高易擴展一般專案、推薦
精確分級版~25中否需精確邊界控制
RelativeTime 類~60最高內建支援國際化專案、長期維護
MyGetTime 類~35中否舊專案相容

前端整合建議:時間戳標準化

<!-- HTML -->


<script>
// 前端即時更新 (每分鐘更新一次)
document.querySelectorAll('.relative-time').forEach(el => {
    const ts = parseInt(el.dataset.timestamp);
    const update = () => {
        const diff = Math.floor(Date.now()/1000) - ts;
        // 這裡可複用 PHP 同邏輯或使用 moment.js/dayjs
        el.textContent = formatRelative(diff);
        el.title = el.dateTime;  // 滑鼠懸停顯示完整時間
    };
    update();
    setInterval(update, 60000);
});
</script>

最佳實踐:資料庫儲存 TIMESTAMP 或 DATETIME (UTC),前端傳遞 ISO 8601 格式,PHP/JS 統一使用時間戳運算,避免時區混淆。

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