簡體   English   中英

PHP:向日期添加月份,但不超過該月的最后一天

[英]PHP: Adding months to a date, while not exceeding the last day of the month

希望創建一個可以在 PHP 中執行此操作的函數。

我需要在日期中添加幾個月,但不能超過該月的最后一天。

例如:

Add 1 month to January (1-28th), 2011, should produce February (1-28th), 2011.
Add 1 month to January 30th, 2011, should produce February 28th, 2011.
Add 3 months to January 31st, 2011, should produce April 30th, 2011.
Add 13 months to January 30th, 2011, should produce February 29th, 2012.
Add 1 month to October 31st, 2011, should produce November 30th, 2011.

如果我在 PHP 中使用日期添加,我會超支:

Adding 1 month to January 30th, 2011, results in March 2nd, 2011.

我的規范不允許我進入新的一個月。

完成此操作的最簡單方法是什么?

您可以比較添加 1 個月之前和之后的一個月中的哪一天。 如果不一樣,你就超過了下個月。

function add($date_str, $months)
{
    $date = new DateTime($date_str);

    // We extract the day of the month as $start_day
    $start_day = $date->format('j');

    // We add 1 month to the given date
    $date->modify("+{$months} month");

    // We extract the day of the month again so we can compare
    $end_day = $date->format('j');

    if ($start_day != $end_day)
    {
        // The day of the month isn't the same anymore, so we correct the date
        $date->modify('last day of last month');
    }

    return $date;
}

$result = add('2011-01-28', 1);   // 2011-02-28
$result = add('2011-01-31', 3);   // 2011-04-30
$result = add('2011-01-30', 13);  // 2012-02-29
$result = add('2011-10-31', 1);   // 2011-11-30
$result = add('2011-12-30', 1);   // 2011-02-28

據我所知,這個問題的范圍非常有限,因此您可能最好通過測試一種類型的錯誤並修復它。

您要做的就是確保在 29 日、30 日或 31 日等較晚日期添加“一個月”不會將您推到下個月的 1 日、2 日或 3 日。

date_modify() 的工作方式(在示例日期“2012-01-31”上使用類似“+1 個月”的字符串)是它首先將月份數增加 1,然后從開始處找到第 31 天那個月的。 這就是它溢出到 3 月 3 日的原因。

如果這不是您想要的,您所要做的就是再次使用 date_modify(),現在告訴它返回幾天(在本例中為 3 天)。 由於您只想返回到上個月的最后一天,因此您想要返回的天數始終與錯誤日期的月份中的某天相同。

剩下的就是確保在不需要時不要應用此更正,例如 PHP 將來要改進時。 這相對容易,因為可能出現的問題情況的范圍非常有限。

  • (1) 僅在日期 29、30 或 31添加月份時出現問題
  • (2) 出現問題時,產生的日期始終為 1、2 或 3。

我下面的代碼添加了“+1 個月”,檢查這是否導致月份中的某天從高到低的劇烈變化,如果是這種情況,則調整日期。

//Create the date, store its day-of-month, and add X months
$myDateTimeISO = "2012-01-31";
$addThese = 1;
$myDateTime = new DateTime($myDateTimeISO);
$myDayOfMonth = date_format($myDateTime,'j');
date_modify($myDateTime,"+$addThese months");

//Find out if the day-of-month has dropped
$myNewDayOfMonth = date_format($myDateTime,'j');
if ($myDayOfMonth > 28 && $myNewDayOfMonth < 4){
//If so, fix by going back the number of days that have spilled over
    date_modify($myDateTime,"-$myNewDayOfMonth days");
}
echo date_format($myDateTime,"Y-m-d");

結果:2012-02-29(是的,這是閏年)。

PS:如果要加年數,問題和症狀幾乎是一樣的。 同樣,您只需要檢查結果中的月份日期是否為 1/2/3,月份中的日期是否為 29/30/31。 如果是這樣,您需要使用 date_modify 返回“-X 天”,其中 X 是生成的月份日期。

這似乎對我有用並給出了你想要的結果:

<?php
$date = "2011-01-30";

list($year,$month,$day) = explode("-",$date);

// add month here
$month++;
// to avoid a month-wrap, set the day to the number of days of the new month if it's too high
$day = min($day,date("t",strtotime($year."-".$month."-01"))); 

$date = $year."-".$month."-".$day;

// 2011-02-28
echo $date;
?>

編輯:
閱讀Crend Kings 評論后,我認為我們需要更多信息。 在以下情況下所需的結果是什么:

2011-01-30 > 2011-02-28 
2011-01-28 > 2011-02-28 or 2011-02-26 ?
2011-02-01 > 2011-03-01 or 2011-03-03 ?

換句話說:該方法是否應該添加下個月的天數,這就是 Cred King 所做的以及給出 2011-02-26 和 2011-03-03 之類的結果(這對我來說似乎不是想要的結果) 還是應該增加一個月並保持原樣,而不是像我的代碼那樣“太高”的一天? 我糊塗了...

您可以使用此代碼。 第一行接受任何格式的日期,后面的行將月份值分開,添加一個月份並以YMD格式返回最終值。

$year_month = Date("Y-m", strtotime($date));
$year_month_incremented = Date("Y-m", strtotime($year_month . " +1 Month "));
$month_end_dt =strtotime('last day of this month', strtotime($year_month_incremented));
$date = date('Y-m-d', $month_end_dt );

對於任何感興趣的人,我采取了可靠的方法來處理這個問題

/**
 * @var \DateInterval
 */
private $remainder;

/**
 * @param \DateTimeImmutable $date
 * @param string $modifier
 * @return \DateTimeImmutable
 */
private function nextInterval(\DateTimeImmutable $date, $modifier)
{
    $dayNumber = (int)$date->format('j');
    $next = $date->modify($modifier);
    if (!is_null($this->remainder)) {
        $next = $next->add($this->remainder);
        $dayNumber += $this->remainder->days;
        $this->remainder = null;
    }

    // This should in general only apply to months which do not have the same daynumber in that month after adding
    if ($dayNumber !== (int)$next->format('j')) {
        $n = $next->modify('last day of last month');
        $this->remainder = $n->diff($next);
        $next = $n;
    }

    return $next;
}

結果:

2014-11-30
2014-12-30
2015-01-30
2015-02-28
2015-03-30
2015-04-30
2015-05-30

2015-12-30
2016-01-30
2016-02-29
2016-03-30
2016-04-30

您也可以使用此代碼。

<?php
$monthToAdd = 1;

$d1 = DateTime::createFromFormat('Y-m-d H:i:s', '2011-01-30 15:57:57');

$year = $d1->format('Y');
$month = $d1->format('n');
$day = $d1->format('d');

$year += floor($monthToAdd/12);
$monthToAdd = $monthToAdd%12;
$month += $monthToAdd;
if($month > 12) {
    $year ++;
    $month = $month % 12;
    if($month === 0)
        $month = 12;
}

if(!checkdate($month, $day, $year)) {
    $d2 = DateTime::createFromFormat('Y-n-j', $year.'-'.$month.'-1');
    $d2->modify('last day of');
}else {
    $d2 = DateTime::createFromFormat('Y-n-d', $year.'-'.$month.'-'.$day);
}
$d2->setTime($d1->format('H'), $d1->format('i'), $d1->format('s'));
echo $d2->format('Y-m-d H:i:s');

// 我已經更新了獲取月份最后一天的邏輯

function addMonthGetLastDate($date_str, $months)
{
    
   
    list($year,$month,$day) = explode("-",$date_str);
    // add month here
    $month += ($months-1);
    if($month>12)
    {
        $month = $month - 12;
        $year += 1;
    }
    // to avoid a month-wrap, set the day to the number of days of the new month if it's too high
    $day = min($day,date("t",strtotime($year."-".$month.".01."))); 
    $date = date('Y-m-t', strtotime($year."-".$month."-".$day));
    
    return $date;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM