首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 精確計算「上個月同一天」日期,完美處理月末邊界情況

     編輯:PHP 相關知識

看似簡單的「獲取上個月同一天日期」,實則隱藏著月份長度不一致的陷阱: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";
}
?>

關鍵技術點

  1. mktime(..., 0) 取上個月最後一天:PHP 中月份參數為 0 表示上個月最後一天,極其實用
  2. date("t") 獲取月份總天數:直接判斷上個月是否有「同一天」
  3. 跨年安全拼接:使用 date("Y-m", $lastMonthTime) 獲取上個月的「年-月」,再拼接日期,避免手動拼接年份導致的跨年錯誤
  4. 保留時間部分:方案保留原時間的時分秒,符合大多數業務預期

常見錯誤避坑

<?php
// ❌ 錯誤寫法:手動拼接年份會導致跨年錯誤
$bad = "Y-" . date("m", $lastMonthTime) . "-d"; // 1月時年份仍為當前年

// ✅ 正確寫法:統一用 date 格式化上個月時間戳
$good = date("Y-m", $lastMonthTime) . "-d";     // 自動處理跨年
?>

替代方案:DateTime 物件風格(PHP 5.3+)

<?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 鏈式調用可讀性更強,內部自動處理閏年、跨年,推薦現代項目使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved