網路上常見的「相減秒數再除 365/30/24」法,無法正確處理閏年、大小月、閏秒等情況。本文提供一個考慮實際行事曆的 diffDateTime 方法,精確輸出年、月、日、時、分、秒差距。
class Utils {
/** 將 MySQL DATETIME 轉為 Unix 時間戳 */
public function fmDatetime($datetime) {
$y = substr($datetime, 0, 4);
$m = substr($datetime, 5, 2);
$d = substr($datetime, 8, 2);
$h = substr($datetime, 11, 2);
$i = substr($datetime, 14, 2);
$s = substr($datetime, 17, 2);
return mktime($h, $i, $s, $m, $d, $y);
}
/** 計算詳細差距 */
private function diffDateTime($start, $end) {
$rs = [];
$sY = (int)substr($start, 0, 4);
$eY = (int)substr($end, 0, 4);
$sM = (int)substr($start, 5, 2);
$eM = (int)substr($end, 5, 2);
$sD = (int)substr($start, 8, 2);
$eD = (int)substr($end, 8, 2);
$startTime = $this->fmDatetime($start);
$endTime = $this->fmDatetime($end);
$diffSec = $endTime - $startTime;
// 秒、分、時、日、週(以秒換算)
$rs['second'] = $diffSec;
$rs['minute'] = ceil($diffSec / 60);
$rs['hour'] = ceil($diffSec / 3600);
$rs['day'] = ceil($diffSec / 86400);
$rs['week'] = ceil($rs['day'] / 7);
// 月、年(依實際月份差計算)
$monthDiff = ($eY - $sY) * 12 + ($eM - $sM);
$yearDiff = $eY - $sY;
// 微調:若結束日 < 開始日,月份 -1
if ($eD < $sD) $monthDiff--;
// 若月份差為負,年份 -1
if ($monthDiff < 0) {
$yearDiff--;
$monthDiff += 12;
}
$rs['month'] = $monthDiff;
$rs['year'] = $yearDiff;
return $rs;
}
}
$utils = new Utils();
$diff = $utils->diffDateTime('2023-01-31 00:00:00', '2023-03-01 00:00:00');
print_r($diff);
/*
Array(
[second] => 2505600
[minute] => 41760
[hour] => 696
[day] => 29
[week] => 5
[month] => 1 // 1月31到3月1 算 1 個月
[year] => 0
)
*/