這一個問題是在計算年資時,出現了差異,DEBUG 數據如下:
===== DEBUG START =====
person_id = 1273
raw due_date = 2024-03-01
TODAY = 2026-03-12
NYD = 2027-01-01
calculateAnnualLeaveType2(date, TODAY) =
Array
(
[completed_years]=>1
[leave_days] => 7
[source_date] => 2026-03-01
[expired_at] => 2027-02-28
[title] => 115.03.01-116.02.28
)
===== DEBUG END =====
這是特休,理論上年度應該是兩年,計算是這樣:
$hire = new DateTime($hire_date); $last_anniversary = DateTime::createFromFormat('Y-m-d', $year . '-' . $md); $completed_years = $hire->diff($last_anniversary)->y;因為找不出原因,乾脆直接改成手算年資。
$hire_y = (int)$hire->format('Y');
$today_y = (int)$today->format('Y');
$hire_md = $hire->format('m-d');
$today_md = $today->format('m-d');
// 已滿幾年:不用 diff()->y,改手算,避免環境差異
$completed_years = $today_y - $hire_y;
if ($today_md < $hire_md) {
$completed_years--;
}
if ($completed_years < 0) {
$completed_years = 0;
}結果就正常了:
Array ( [completed_years]=>2 [leave_days] => 10 [source_date] => 2026-03-01 [expired_at] => 2027-02-28 [title] => 115.03.01-116.02.28 )詢問了chatgpt 給出的答案是:
$hire->diff($last_anniversary)->y這個內建本身通常是可靠的,PHP 官方把DateTimeInterface::diff()定義成回傳兩個日期時間之間的差異,而且它是依照完整日期時間 來算,不是只看年月日;另外它還會考慮 DST(夏令時間)變化。(php.net)真正容易出問題的,通常不是
diff()壞掉,而是輸入給它的 DateTime 物件不夠乾淨。 PHP 官方文件明講:createFromFormat()若沒有用!或|, 未提供的欄位會先用「目前的日期時間」初始化;所以你只是想解析Y-m-d,卻沒有先歸零, 就可能把當下的時分秒帶進去,導致「看起來同一天,實際差不到完整 1 年 / 2 年」。(php.net)所以實務上我會這樣判斷:
拿來算一般日期差:很穩。(php.net)
拿來算年資、特休、到職週年這種 HR 規則:可以用,但前提是你要先把輸入標準化成純日期 ,否則很容易被時分秒、時區、DST 影響。(php.net)
若規則本質是「比年月日」而不是「比完整 timestamp」,像你的特休這種,我反而會更偏向你現在改的 「手算 completed_years」做法,因為它更貼近制度規則,也更容易除錯。
換句話說,這不是說 diff()->y 很爛,而是:
diff()->y比較像「時間物件差幾個完整年」,
但你的需求其實是 「年資制度上已滿幾年」。
這兩者很多時候相同,但在邊界上不一定完全一樣。
你現在這樣改成手算年資,我會認為是比較保險的做法。尤其在特休、年資、合約年限、
生日到齡這類規則上,手算年月日邊界往往比 diff()->y 更符合商業邏輯。
















