作者 bjbs_270 分享一個月報表統計常用的功能:給定起始日期,向前推算 N 個月份 (格式 Y-m)。文中包含完整程式碼與執行結果。
<?php
/**
* 從指定時間向下推 N 個月
* @param string $ymd 起始日期 (格式 Y-m-d),預設為今天
* @param int $len 推算月數,預設 12
* @return array 月份陣列 (格式 Y-m)
*/
function month($ymd = '', $len = 12) {
$month = array();
$lang = '<meta http-equiv="Content-Type" content="text/html; charset=gb2312">';
// 參數驗證
if ($ymd) {
if (!ereg("([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})", $ymd)) {
echo "日期格式錯誤"; exit;
}
}
if ($len) {
if (!ereg("[0-9]+", $len)) {
echo $lang . "長度參數錯誤"; exit;
}
}
for ($i = 0; $i < $len; $i++) {
if ($i == 0) {
$mktime = $ymd ? strtotime($ymd) : time();
}
$month[] = date("Y-m", $mktime); // 可依需求調整格式
$day = date("t", $mktime); // 當月總天數
if ($i == 0)
$reday = date("d", $mktime); // 第一個月用實際日期
else
$reday = $day; // 後續月用該月最後一天
$t1 = $mktime;
// 關鍵邏輯:減去天數到上個月同一天 (或最後一天)
$mktime = date("Y-m-d", mktime(
date("H", $t1), date("i", $t1), date("s", $t1),
date("m", $t1), date("d", $t1) - $reday, date("Y", $t1)
));
$mktime = strtotime($mktime);
}
return $month;
}
// 測試
$month = month();
foreach ($month as $key => $v) {
echo $v . "<br>";
}
?>
2005-01 2004-12 2004-11 2004-10 2004-09 2004-08 2004-07 2004-06 2004-05 2004-04 2004-03 2004-02
利用 mktime() 的「日期溢位」特性:將日部分減去當月天數 ($reday),自動推算到上個月。第一次迴圈用實際日期,後續用各月最後一天 (date("t")) 確保落在月末。
ereg() 函數 (PHP 5.3+ 移除),建議改用 preg_match()。mktime() 會自動調整 (如 3/31 - 31 天 → 2/28 或 2/29),邏輯正確。$mktime 與函數名衝突,易造成混淆。function getPreviousMonths(\DateTimeInterface $start = null, int $count = 12): array {
$start = $start ?? new DateTime();
$months = [];
$current = clone $start;
for ($i = 0; $i < $count; $i++) {
$months[] = $current->format('Y-m');
$current->modify('first day of last month'); // 直接跳到上個月 1 號
}
return $months;
}
原函數展示了早期 PHP 日期運算的巧思,現代開發強烈建議使用 DateTime 類別,語意清晰且避免時區與閏年陷阱。