[英]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 個月”,檢查這是否導致月份中的某天從高到低的劇烈變化,如果是這種情況,則調整日期。
//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.