看似簡單的「獲取上個月同一天日期」,實則隱藏著月份長度不一致的陷阱:strtotime("-1 month") 在 3月31日 會返回 3月3日(因 2月無 31 日自動順延)。本文提供健壯的解決方案:若上個月無同一天,則返回上個月最後一天,並修正跨年邊界 Bug。
<?php
$time = strtotime("2011-03-31");
echo date("Y-m-d", strtotime("-1 month", $time)); // 輸出: 2011-03-03 (錯誤!預期 2011-02-28)
?>
<?php
/**
* 計算上個月同一天;若上個月無該日期,返回上個月最後一天
* @param int $time 時間戳
* @return string 格式化日期 Y-m-d H:i:s
*/
function lastMonthToday($time) {
// 關鍵:第 0 天 = 上個月最後一天
$lastMonthTime = mktime(
date("G", $time), date("i", $time), date("s", $time),
date("n", $time), 0, date("Y", $time)
);
$lastMonthDays = date("t", $lastMonthTime); // 上個月總天數
$currentDay = date("j", $time); // 當前月幾號
if ($lastMonthDays < $currentDay) {
// 上個月天數不足(如 31號 -> 2月),返回上個月最後一天
return date("Y-m-t H:i:s", $lastMonthTime);
}
// 正常情況:拼接年月 + 當前日期
return date("Y-m-d H:i:s", mktime(
date("G", $time), date("i", $time), date("s", $time),
date("n", $time) - 1, $currentDay, date("Y", $time)
));
}
// 測試用例
$cases = [
"2011-03-31" => "2011-02-28", // 31號 -> 2月最後一天
"2012-03-31" => "2012-02-29", // 閏年
"2011-05-15" => "2011-04-15", // 正常情況
"2011-01-15" => "2010-12-15", // 跨年
"2011-01-31" => "2010-12-31", // 跨年+月末
];
foreach ($cases as $input => $expect) {
$ts = strtotime($input);
$out = lastMonthToday($ts);
echo "$input => $out " . ($out === date("Y-m-d H:i:s", strtotime($expect)) ? "✓" : "✗") . "\n";
}
?>
mktime(..., 0) 取上個月最後一天:PHP 中月份參數為 0 表示上個月最後一天,極其實用date("t") 獲取月份總天數:直接判斷上個月是否有「同一天」date("Y-m", $lastMonthTime) 獲取上個月的「年-月」,再拼接日期,避免手動拼接年份導致的跨年錯誤<?php
// ❌ 錯誤寫法:手動拼接年份會導致跨年錯誤
$bad = "Y-" . date("m", $lastMonthTime) . "-d"; // 1月時年份仍為當前年
// ✅ 正確寫法:統一用 date 格式化上個月時間戳
$good = date("Y-m", $lastMonthTime) . "-d"; // 自動處理跨年
?>
<?php
function lastMonthTodayOOP($time) {
$dt = new DateTime("@$time");
$dt->modify("first day of this month"); // 本月1號
$dt->modify("-1 day"); // 上月最後一天
$lastDay = (int)$dt->format("j");
$day = min((int)$dt->format("j"), $lastDay); // 取較小值
$dt->modify("first day of last month")->setDate($dt->format("Y"), $dt->format("m"), $day);
return $dt->format("Y-m-d H:i:s");
}
?>
DateTime 鏈式調用可讀性更強,內部自動處理閏年、跨年,推薦現代項目使用。