希望创建一个在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.
我的规范不允许我超越新月。
实现这一目标的最简单方法是什么?
答案 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月”,检查是否导致日期从高位变为低位,并调整日期(如果是这种情况)。
//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;
}