В следующем месяце в тот же день в PHP - PullRequest
12 голосов
/ 06 июня 2009

У меня есть «событие», которое должно быть запланировано на один и тот же день каждого месяца.

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

Учитывая, что есть месяцы с разным количеством дней (28, 30, 31) в зависимости от самого месяца и года ... что было бы простым способом установить это?

Рассмотрим следующую (и ошибочную) функцию nextmonth:

$events = array()

function nextmonth($date) {
   return $date+(60*60*24*30);
}
$curr = $start;
while($curr < $end) {
    $events[ = $curr;
    $curr = nextmonth($curr);
}

Отредактировано, чтобы добавить: Для меня проблема, достаточно просто, как решить, каково количество дней в любом данном месяце и таким образом получить следующую соответствующую дату.

Ответы [ 16 ]

9 голосов
/ 06 июня 2009

Обновление:

Это даст вам количество дней в данном месяце:

echo date('t', $timestamp);

См .: дата ()

Старый ответ:

Я не уверен в алгоритме, о котором вы думаете, но я верю, что это поможет вам:

echo date('d-M-y', strtotime('next month'));
5 голосов
/ 06 июня 2009

Это вернет правильную временную метку на $ X месяцев вперед: mktime(0,0,0,date("m")+$X,date('d'),date("Y"));

3 голосов
/ 22 октября 2012

Попробуй это. reday - день пополнения (int - день месяца). Если меньше, чем сегодня, это даст reday этого месяца, но не намного, чем нет дней этого месяца. Если больше, чем сегодня, укажите reday следующего месяца в таком же состоянии. Я использую это для расчета «Следующего пополнения» ежемесячной точки пополнения. возврат dd/mm/yyyy

function nextrefill($reday) {
    if (date("j") < $reday) {
        $numd = date("t");
        if ($reday > $numd) {
            return str_pad($numd, 2, "0", STR_PAD_LEFT).date("/m/Y");
        } else {
            return str_pad($reday, 2, "0", STR_PAD_LEFT).date("/m/Y");
        }
    } else {
        $nextm = date('m', strtotime('first day of next month'));
        $nextmy = date('Y', strtotime('first day of next month'));
        $numd = cal_days_in_month(CAL_GREGORIAN, $nextm, $nextmy);
        if ($reday > $numd) {
            return str_pad($numd, 2, "0", STR_PAD_LEFT)."/".$nextm."/".$nextmy;
        } else {
            return str_pad($reday, 2, "0", STR_PAD_LEFT)."/".$nextm."/".$nextmy;
        }
    }
}

для более прямого указания (Правка)

function nextmonthday($day) {
        $next_month = date('m', strtotime('first day of next month'));
        $year_of_next_month = date('Y', strtotime('first day of next month'));
        $no_of_day_in_next_month = cal_days_in_month(CAL_GREGORIAN, $nextm, $nextmy);
        if ($day > $no_of_day_in_next_month){
            return str_pad($no_of_day_in_next_month, 2, "0", STR_PAD_LEFT)."/".$next_month."/".$year_of_next_month;
        } else {
            return str_pad($day, 2, "0", STR_PAD_LEFT)."/".$next_month."/".$year_of_next_month;
        }
}
2 голосов
/ 06 июня 2009

Я рекомендую прочитать следующий комментарий на php.net. strtotime () (php.net)

Редактировать: следующий ответ дает «сводку» ссылки, которую я разместил для тех, кто не может расшифровать показанный там контент.

1 голос
/ 18 апреля 2018

Прошло много времени, но в случае, если кто-то ищет это, среди всех приведенных выше ответов я обнаружил, что только ответ @ling действительно отвечает на этот вопрос, но, тем не менее, его ответ не был на 100% точным, он неверно выводит часть года как вы можете видеть в его результате выше.

Вот модифицированная версия его кода, которая исправляет проблему года (я также изменил ее, чтобы вместо нее использовать класс DateTime)

  /**
   * Adding month in PHP using DateTime
   * class will render the date in a way that
   * we do not desire, for example adding one month
   * to 2018-01-31 will result in 2018-03-03 but
   * we want it to be 2018-02-28 instead.
   *
   * This method ensure that adding months
   * to a date get caculated properly.
   *
   *
   * @param DateTime $startDate
   * @param int $numberOfMonthsToAdd
   *
   * @return DateTime
   */
  function getSameDayNextMonth(DateTime $startDate, $numberOfMonthsToAdd = 1) {
    $startDateDay = (int) $startDate->format('j');
    $startDateMonth = (int) $startDate->format('n');
    $startDateYear = (int) $startDate->format('Y');

    $numberOfYearsToAdd = floor(($startDateMonth + $numberOfMonthsToAdd) / 12);
    if ((($startDateMonth + $numberOfMonthsToAdd) % 12) === 0) {
      $numberOfYearsToAdd--;
    }
    $year = $startDateYear + $numberOfYearsToAdd;

    $month = ($startDateMonth + $numberOfMonthsToAdd) % 12;
    if ($month === 0) {
      $month = 12;
    }
    $month = sprintf('%02s', $month);

    $numberOfDaysInMonth = (new DateTime("$year-$month-01"))->format('t');
    $day = $startDateDay;
    if ($startDateDay > $numberOfDaysInMonth) {
      $day = $numberOfDaysInMonth;
    }
    $day = sprintf('%02s', $day);

    return new DateTime("$year-$month-$day");
  }


  // Quick Test
  $startDate = new DateTime('2018-01-31');
  for($i=0; $i <= 40; $i++) {
    echo getSameDayNextMonth($startDate, $i)->format('Y-m-d') . "\n";
  }

Выход:

2018-01-31
2018-02-28
2018-03-31
2018-04-30
2018-05-31
2018-06-30
2018-07-31
2018-08-31
2018-09-30
2018-10-31
2018-11-30
2018-12-31
2019-01-31
2019-02-28
2019-03-31
2019-04-30
2019-05-31
2019-06-30
2019-07-31
2019-08-31
2019-09-30
2019-10-31
2019-11-30
2019-12-31
2020-01-31
2020-02-29
2020-03-31
2020-04-30
2020-05-31
2020-06-30
2020-07-31
2020-08-31
2020-09-30
2020-10-31
2020-11-30
2020-12-31
2021-01-31
2021-02-28
2021-03-31
2021-04-30
2021-05-31
1 голос
/ 29 ноября 2017

Ничто из вышеперечисленного не учитывало несуществующие дни (например, 30 февраля или 31 апреля), поэтому моя функция такова:

<?php
function getSameDayNextMonth($time, $numberMonthsToAdd = 1)
{
    list($year, $month, $day) = explode('-', date("Y-m-d", $time));

    // replace the day by one temporarily (just to make sure it exists for any month
    $numberOfYearsToAdd = floor($numberMonthsToAdd / 12);
    $year += $numberOfYearsToAdd;
    $month = ($month + $numberMonthsToAdd) % 12;
    if (0 === $month) {
        $month = 12;
    }
    $monthFormatted = sprintf('%02s', $month);
    $nbDaysInThisMonth = date("t", strtotime("$year-$monthFormatted-01"));

    if ((int)$day > $nbDaysInThisMonth) {
        $day = $nbDaysInThisMonth;
    }
    $day = sprintf('%02s', $day);
    return strtotime("$year-$monthFormatted-$day");
}


$time = strtotime("2017-10-31");
for ($i = 0; $i <= 15; $i++) {
    $_time = getSameDayNextMonth($time, $i);
    echo date("Y-m-d", $_time) . '<br>';
}

/**
 * 2017-10-31
 * 2017-11-30
 * 2017-12-31
 * 2017-01-31
 * 2017-02-28
 * 2017-03-31
 * 2017-04-30
 * 2017-05-31
 * 2017-06-30
 * 2017-07-31
 * 2017-08-31
 * 2017-09-30
 * 2018-10-31
 * 2018-11-30
 * 2018-12-31
 * 2018-01-31
 */
1 голос
/ 30 августа 2013

Простой

public function adicionarMesMantendoDia($date, $months, $format = "Y-m-d"){
        $date = \DateTime::createFromFormat($format, $date);

        for($i=0;$i < $months; $i++){
            $date->modify('+ ' . date("t", $date->getTimestamp())  . ' day');
        }
        return $date;
}
1 голос
/ 01 апреля 2010

попробовал это как жаворонок, и на самом деле работает

strtotime('last day next month')

Итак:

$today_next_month = strtotime('this day next month');
$last_day_next_month = strtotime('last day next month');
if (date('d', $today_next_month) < date('d', $last_day_next_month)){
    $date = date('m-d-Y', $last_day_next_month); 
} else {
    $date = date('m-d-Y', $today_next_month);
}
echo "And the winner is : $date<br/>";
1 голос
/ 06 июня 2009

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

Пример:

June 5, 2009, next month would be July 5, 2009
August 31, 2009, next month would be September 30, 2009

или просто strtotime("+1 month")

0 голосов
/ 14 мая 2018

Я создал функцию my_strtotime, которая будет обрабатывать сложение и вычитание месяцев. это в основном обертка вокруг strtotime.

Он проверяет, будет ли сложение или вычитание месяца безопасно доставить вам желаемый месяц с тем же днем. Если нет, то он делает это безопасно.

При добавлении месяцев добавляется 15 для безопасного перехода в следующий месяц, а затем возвращается последний день месяца. Вычитая месяцы, он берет первый день этого месяца и вычитает 1.

примечание: как написано, это работает в течение +1 месяца или -1 месяца


    function _my_strtotime($offset, $reference_date){

           //$offset is a string such as (+1 month)

       if(strpos($offset, 'month') === false){
         //exit if not addin/subtracing months
         return strtotime($offset, $reference_date);
       }

       $other_months_date = strtotime ( $offset, $reference_date );
       $day_of_other_month = date( 'd', $other_months_date );
       $day_of_reference_month = date('d', $reference_date );

       //check if numerical day is different. If so manipulate.
       if($day_of_reference_month != $day_of_other_month){
         if(strpos($offset, '-') === false){
           // adding months
           return strtotime('last day of this month', strtotime('+15 days', $reference_date));
         } else {
           // subtracing months
           return strtotime('-1 day', strtotime('first day of this month', $reference_date));
         }

       }

       return strtotime($offset, $reference_date);

     }

Результаты испытаний:


    input=2016-01-31  (+1 month)  output=2016-02-29 //leap year

    input=2017-01-31  (+1 month) output=2017-02-28

    input=2018-05-31 (+1 month) output=2018-06-30

    input=2018-07-31 (-1 month) output=2018-06-30

    input=2016-07-22 (-1 month) output=2016-06-22

    input=2016-07-22 (+1 month) output=2016-08-22

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...