PHP:将日期添加到日期,但不超过该月的最后一天

时间:2011-04-22 21:31:06

标签: php date

希望创建一个在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.

我的规范不允许我超越新月。

实现这一目标的最简单方法是什么?

7 个答案:

答案 0 :(得分:40)

您可以比较添加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

答案 1 :(得分:3)

这似乎对我有用,并给出了你期望的结果:

<?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 ?

用文字表示:该方法是否应该添加下个月的天数,这是Crend King的作用,以及2011-02-26和2011-03-03的结果(这似乎不是所希望的)结果给我)或者这应该增加一个月并保持原样,而不是像我的代码那样“太高”的一天?我很困惑......

答案 2 :(得分:3)

据我所知,这个问题的范围非常有限,所以你可能最好通过测试一种类型的错误并修复它。

您要做的就是确保将“一个月”添加到29日,30日或31日等较晚的日期并不会让您前进到下个月的第一,第二或第三。

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 days”,其中X是由此产生的日期。

答案 3 :(得分:0)

对于任何有兴趣的人,我都采取了坚实的方法来处理这个问题

/**
 * @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

答案 4 :(得分:0)

您可以使用此代码。第一行接受任何格式的日期,后几行将月份值分割开,添加一个月份并以Y-M-D格式返回最终值。

$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 );

答案 5 :(得分:0)

您也可以使用此代码。

<?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');

答案 6 :(得分:0)

// 我更新了获取月份最后日期的逻辑

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;
}