Что может пойти не так при добавлении месяцев с DateInterval и DateTime::add?

Я не смог найти правильного решения этой проблемы. Как вы видите в Примере № 3 в документации PHP, они утверждают, что нужно соблюдать осторожность при добавлении месяцев с использованием DateInterval в DateTime::add.

На самом деле нет никакого объяснения, почему поведение метода таково и что я могу сделать, чтобы избежать этого, что я считаю ошибкой с первого взгляда.

У кого-нибудь есть понимание этого?

2 ответа

Решение

Проблема в том, что в каждом месяце может быть разное количество дней. Вопрос в том, что вы делаете, когда хотите увеличить дату на 1 месяц. Согласно документации PHP, если вы используете 31 января (или 30 января) и добавляете 1 месяц, каково ожидаемое поведение?

В феврале только 29 дней. Вы хотите установить последний день месяца? Как правило, безопаснее увеличиваться на определенное количество дней, если это то, что вы ищете, или статическую дату, основанную на текущей дате. Не зная, чего вы пытаетесь достичь, увеличивая свой месяц, трудно сказать, как следить за ошибкой.

РЕДАКТИРОВАТЬ:
Как кто-то упоминает в аналогичном посте, прокомментированном Майком Б выше, вы, вероятно, захотите сделать что-то там, где вы (в псевдокоде):

 1) Use cal_days_in_month() for the next month and save that number to a variable x
 2) If x >= current billing DOB, increment and be done
 3) DateTime::modify('last day')  (havent used this before but something along these lines) to set the date to the last date of the next month (set it to the 1st of the next month, then last day?)

Стоит отметить, что если вы используете переменную здесь в качестве нового значения выставления счета, вы уничтожите свое первоначальное значение. Я бы сохранил дополнительное значение БД, которое является "первой датой выставления счета" или просто "billing_day_of_month" или чем-то подобным, и использую это для определения дня месяца, на который вы должны смотреть

Если ваша цель состоит в строгом увеличении на удобные для пользователя месяцы (таким образом, 3 месяца с 21 января должны быть 21 апреля), за исключением того, что более короткие месяцы членства сокращаются (таким образом, 1 месяц с 31 января является 28-29 февраля), тогда вам нужно вернуться только на несколько дней, если вы перешли на следующий месяц:

function addMonths($date,$months) {
  $orig_day = $date->format("d");
  $date->modify("+".$months." months");
  while ($date->format("d")<$orig_day && $date->format("d")<5)
    $date->modify("-1 day");
}

$d = new DateTime("2000-01-31");
addMonths($d,1);
echo $d->format("Y-m-d"); // 2000-02-29
Другие вопросы по тегам