核心函數實現
/**
* 將秒數格式化為中文可讀時間
* @param int $seconds 秒數
* @return string 格式化字符串,如 "1天2小時30分5秒"
*/
function formatDuration(int $seconds): string {
if ($seconds < 0) $seconds = 0;
$units = [
86400 => '天',
3600 => '小時',
60 => '分',
1 => '秒'
];
$parts = [];
foreach ($units as $unitSeconds => $unitName) {
if ($seconds >= $unitSeconds) {
$value = (int)($seconds / $unitSeconds);
$parts[] = $value . $unitName;
$seconds %= $unitSeconds;
}
}
return $parts ? implode('', $parts) : '0秒';
}
// 測試
echo formatDuration(86465); // 1天1分5秒
echo formatDuration(3661); // 1小時1分1秒
echo formatDuration(45); // 45秒
echo formatDuration(0); // 0秒
進階版:支援多語言、精度控制、零值顯示
/**
* 進階時間格式化
* @param int $seconds
* @param array $options [
* 'lang' => 'zh|en', // 語言
* 'precision' => 2, // 最大顯示單位數
* 'showZero' => false, // 是否顯示 0 值單位
* 'separator' => '', // 單位分隔符
* 'short' => false // 短格式:1d 2h 3m 4s
* ]
*/
function formatDurationAdvanced(int $seconds, array $options = []): string {
$opts = array_merge([
'lang' => 'zh',
'precision' => 4,
'showZero' => false,
'separator' => '',
'short' => false
], $options);
if ($seconds < 0) $seconds = 0;
$units = $opts['lang'] === 'en' ? [
86400 => $opts['short'] ? 'd' : ' day',
3600 => $opts['short'] ? 'h' : ' hour',
60 => $opts['short'] ? 'm' : ' minute',
1 => $opts['short'] ? 's' : ' second'
] : [
86400 => $opts['short'] ? '天' : '天',
3600 => $opts['short'] ? '時' : '小時',
60 => $opts['short'] ? '分' : '分',
1 => $opts['short'] ? '秒' : '秒'
];
$parts = [];
foreach ($units as $unitSeconds => $unitName) {
if (count($parts) >= $opts['precision']) break;
$value = (int)($seconds / $unitSeconds);
if ($value > 0 || $opts['showZero']) {
$suffix = $opts['short'] ? '' : ($opts['lang'] === 'en' && $value > 1 ? 's' : '');
$parts[] = $value . $opts['separator'] . $unitName . $suffix;
$seconds %= $unitSeconds;
}
}
return $parts ? implode($opts['separator'] ? ' ' : '', $parts) : ($opts['short'] ? '0s' : '0秒');
}
// 使用範例
echo formatDurationAdvanced(90061); // 1天1小時1分1秒
echo formatDurationAdvanced(90061, ['precision' => 2]); // 1天1小時
echo formatDurationAdvanced(90061, ['lang' => 'en']); // 1 day 1 hour 1 minute 1 second
echo formatDurationAdvanced(90061, ['short' => true]); // 1天1時1分1秒
echo formatDurationAdvanced(90061, ['separator' => ' ']); // 1 天 1 小時 1 分 1 秒
Carbon 庫方案(現代 PHP 專案推薦)
// composer require nesbot/carbon
use Carbon\CarbonInterval;
$interval = CarbonInterval::seconds(90061);
// 人類可讀
echo $interval->forHumans(); // "1 day 1 hour 1 minute 1 second"
echo $interval->forHumans(['short' => true]); // "1d 1h 1m 1s"
echo $interval->forHumans(['parts' => 2]); // "1 day 1 hour"
echo $interval->forHumans(['locale' => 'zh']); // "1天 1小時 1分鐘 1秒" (需 Carbon 2.50+)
// 精確格式化
echo $interval->cascade()->format('%d天 %h小時 %i分 %s秒'); // 1天 1小時 1分 1秒
DateInterval 原生方案(無依賴)
function formatDurationNative(int $seconds): string {
$interval = new DateInterval('PT' . $seconds . 'S');
// DateInterval 不自動進位,需手動計算
$days = (int)($seconds / 86400);
$hours = (int)(($seconds % 86400) / 3600);
$minutes = (int)(($seconds % 3600) / 60);
$secs = $seconds % 60;
$parts = [];
if ($days) $parts[] = $days . '天';
if ($hours) $parts[] = $hours . '小時';
if ($minutes) $parts[] = $minutes . '分';
if ($secs || empty($parts)) $parts[] = $secs . '秒';
return implode('', $parts);
}
性能對比
| 方案 | 執行時間 (100萬次) | 依賴 | 功能豐富度 |
| 自定義函數 | ~0.15s | 無 | 基礎 |
| DateInterval | ~0.45s | 無 | 中等 |
| CarbonInterval | ~1.2s | Composer | 極豐富 |
常見應用場景
| 場景 | 推薦方案 | 範例 |
| 視頻/音頻時長顯示 | 自定義函數 / Carbon | 1:23:45 |
| 相對時間(幾分鐘前) | Carbon diffForHumans() | 5分鐘前 |
| 倒計時器 | 自定義函數 + JS 前端 | 剩餘 2天3小時 |
| 日誌/審計持續時間 | Carbon forHumans() | 執行耗時 1.5秒 |
| 高性能批量處理 | 自定義函數 | 百萬級數據轉換 |
總結
- 簡單項目/高性能需求:自定義函數(~10 行代碼,零依賴)
- 現代 Laravel/Symfony 專案:Carbon(功能完整、國際化、鏈式調用)
- 需精確控制格式:DateInterval + 手動計算 或 Carbon
cascade()->format()
- 核心算法:除法取商取模,從大單位到小單位依次處理
秒數格式化看似簡單,但處理邊界條件(0值、負數)、國際化、精度控制時易出錯。封裝通用函數或引入成熟庫,能避免重複造輪子並保證一致性。