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

PHP strtotime('-1 month') 計算上個月同一天 Bug 完美解決:mktime 精確計算方案

     編輯:PHP 相關知識

Bug 現象

> php -r "echo date('Ymd', strtotime('-1 month', strtotime('20130731')));"
20130701  // 錯誤!應為 20130630

> php -r "echo date('Ymd', strtotime('-1 month', strtotime('20130801')));"
20130701  // 正確

原因:strtotime('-1 month') 簡單減少月份數,不處理月末邊界。

方案一:mktime 手動計算 (精確控制)

<?php
function lastMonthSameDay($timestamp) {
    return mktime(
        date('H', $timestamp),
        date('i', $timestamp),
        date('s', $timestamp),
        date('n', $timestamp) - 1, // 月份 -1
        date('j', $timestamp),     // 保持同一天
        date('Y', $timestamp)
    );
}

// 測試
echo date('Y-m-d', lastMonthSameDay(strtotime('2013-07-31'))); // 2013-07-01 (PHP自動處理溢出)
// 註:PHP mktime 會自動將 6月31日 修正為 7月1日

方案二:獲取上個月最後一天 (業務常見需求)

<?php
function lastMonthLastDay($timestamp) {
    // 當月 0 日 = 上個月最後一天
    return mktime(0, 0, 0, date('n', $timestamp), 0, date('Y', $timestamp));
}

echo date('Y-m-d', lastMonthLastDay(strtotime('2013-07-15'))); // 2013-06-30
echo date('Y-m-d', lastMonthLastDay(strtotime('2013-03-15'))); // 2013-02-28 (非閏年)
echo date('Y-m-d', lastMonthLastDay(strtotime('2012-03-15'))); // 2012-02-29 (閏年)

方案三:完整業務邏輯 - 上個月同一天,不存在則取月末

<?php
/**
 * 計算上個月同一天,若上個月無此日則返回上個月最後一天
 */
function lastMonthToday($timestamp) {
    // 上個月 0 點時間戳
    $lastMonthTs = mktime(
        date('G', $timestamp),
        date('i', $timestamp),
        date('s', $timestamp),
        date('n', $timestamp),
        0,  // 關鍵:第 0 天 = 上個月最後一天
        date('Y', $timestamp)
    );
    
    $lastMonthDays = date('t', $lastMonthTs); // 上個月總天數
    $currentDay = date('j', $timestamp);      // 當前幾號
    
    if ($lastMonthDays < $currentDay) {
        // 上個月沒有這一天,返回月末
        return date('Y-m-t H:i:s', $lastMonthTs);
    }
    
    // 有這一天,返回同一天
    return date('Y-m-' . $currentDay . ' H:i:s', $timestamp);
}

// 測試
echo lastMonthToday(strtotime('2013-07-31 12:00:00')); // 2013-06-30 12:00:00
echo lastMonthToday(strtotime('2013-07-15 12:00:00')); // 2013-06-15 12:00:00
echo lastMonthToday(strtotime('2012-03-31 12:00:00')); // 2012-02-29 12:00:00 (閏年)
echo lastMonthToday(strtotime('2013-03-31 12:00:00')); // 2013-02-28 12:00:00 (非閏年)

DateTime 物件導向方案 (PHP 5.3+)

<?php
$date = new DateTime('2013-07-31');
$date->modify('first day of last month'); // 2013-06-01
$date->modify('last day of last month');  // 2013-06-30

// 或者
$date = new DateTime('2013-07-31');
$interval = new DateInterval('P1M');
$date->sub($interval); // 2013-07-01 (同樣有 Bug)

// 正確做法:先設為 1 號再減月
$date = new DateTime('2013-07-31');
$date->setDate($date->format('Y'), $date->format('m'), 1);
$date->sub(new DateInterval('P1M'));
$date->setDate($date->format('Y'), $date->format('m'), min(31, $date->format('t')));

測試用例表

輸入日期預期上月同天strtotime 結果方案三結果
2013-01-312012-12-312013-01-01 ❌2012-12-31 ✅
2013-03-312013-02-282013-03-03 ❌2013-02-28 ✅
2012-03-312012-02-292012-03-02 ❌2012-02-29 ✅
2013-07-152013-06-152013-06-15 ✅2013-06-15 ✅
2013-08-312013-07-312013-07-31 ✅2013-07-31 ✅
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved