У меня есть 2 даты в PHP, как я могу запустить цикл foreach, чтобы пройти все эти дни?

Я начинаю с даты 2010-05-01 и заканчиваю 2010-05-10. Как я могу перебрать все эти даты в PHP?


person Shamoon    schedule 08.07.2010    source источник


Ответы (11)


$begin = new DateTime('2010-05-01');
$end = new DateTime('2010-05-10');

$interval = DateInterval::createFromDateString('1 day');
$period = new DatePeriod($begin, $interval, $end);

foreach ($period as $dt) {
    echo $dt->format("l Y-m-d H:i:s\n");
}

Это выведет все дни в указанном периоде между $start и $end. Если вы хотите включить 10-й, установите $end на 11-й. Вы можете настроить формат по своему вкусу. См. руководство по PHP для DatePeriod. Требуется PHP 5.3.

person Gordon    schedule 08.07.2010
comment
хорошие новости - есть патч для установки флага, чтобы включить дату окончания, которая (скрестим пальцы) превратит его в будущую версию. - person salathe; 09.07.2010
comment
$begin->setTime(0,0); $end->setTime(12,0); или инициализация со временем дня даты начала, так как любое время позже даты окончания будет включать дату окончания в цикле. Не самое стильное исправление, но это лучший вариант, если нет подходящего флага. - person Chris; 30.05.2013
comment
Это также не работает, если даты начала и окончания совпадают (скажем, вы позволяете кому-то выбрать даты начала и окончания в средстве выбора даты), поэтому я добавляю один день к дате окончания. - person Matthew Lock; 24.10.2013
comment
@MatthewLock ммм, из любопытства, чего вы ожидали, когда даты начала и окончания совпадают? Я имею в виду, что тогда не осталось дней, поэтому очевидно, что тогда это не сработает. - person Gordon; 24.10.2013
comment
Я ожидал, что он будет включать последний день (который совпадает с первым), поэтому для каждой даты (первая/последняя дата) - person Matthew Lock; 24.10.2013
comment
это выглядит чистым способом, но он не самый эффективный: создание массива вызывает цикл, а также дополнительное использование памяти. циклы for+computation, предложенные в других ответах, труднее читать, но более эффективно выполнять. - person Quicker; 02.09.2014
comment
Если вы хотите включить дату окончания в свой интервал, вы можете сделать: $end = $end-›modify('+1 day'); - person JulienITARD; 14.11.2014
comment
Можно ли использовать это, но обратить вспять, вернуться в историю? - person Jon; 22.10.2017
comment
@ Гордон Я понял после того, как опубликовал, что это был глупый вопрос. На самом деле вы не можете просто поменять даты, так как это, похоже, приведет к сбою. Но для моего использования я просто использовал дату начала в прошлом, и это сработало. - person Jon; 23.10.2017
comment
@JulienITARD это довольно хорошая идея, но более элегантной будет $end-›add( $interval ), потому что она напрямую реагирует на измененный интервал;) - person GDY; 06.06.2019

Это также включает в себя последнюю дату

$begin = new DateTime( "2015-07-03" );
$end   = new DateTime( "2015-07-09" );

for($i = $begin; $i <= $end; $i->modify('+1 day')){
    echo $i->format("Y-m-d");
}

Если вам не нужна последняя дата, просто удалите = из условия.

person Sabri Aziri    schedule 30.06.2015
comment
Обязательно обратите внимание, что $begin будет другим после цикла. Этот цикл изменяет объект, созданный new DateTime( "2015-07-03" ). Поэтому вы должны использовать версии DateTimeImmutable. Но вам нужны некоторые дополнительные модификации для их использования. - person Henk Poley; 09.09.2019

Преобразование в временные метки unix упрощает математику дат в php:

$startTime = strtotime( '2010-05-01 12:00' );
$endTime = strtotime( '2010-05-10 12:00' );

// Loop between timestamps, 24 hours at a time
for ( $i = $startTime; $i <= $endTime; $i = $i + 86400 ) {
  $thisDate = date( 'Y-m-d', $i ); // 2010-05-01, 2010-05-02, etc
}

При использовании PHP с часовым поясом с переходом на летнее время обязательно добавьте время, отличное от 23:00, 00:00 или 1:00, чтобы предотвратить пропуск или повторение дней.

person Harold1983-    schedule 08.07.2010
comment
Мне не нравится вид этого 86400. Я понимаю, что это 60*60*24, но все же... что-то меня в нем раздражает. - person MikeD; 09.07.2010
comment
в этом случае это работает, но если есть переключение между нормальным временем и временем сохранения солнечного света, это не удастся, потому что в вашем цикле будет 90000 секунд, которые вы будете иметь дважды... - person oezi; 09.07.2010
comment
Майк, лучше всего настроить константу и назвать ее DAY, чтобы ее было намного легче читать. - person The Pixel Developer; 09.07.2010
comment
Это будет страдать от проблем с переходом на летнее время. Когда вы пересекаете точку перехода на летнее время, она испортится. 12:00 — это не 12:00 по обе стороны от точки во времени. - person Eric Cope; 19.02.2014
comment
Этот код (каждый код с 86400 секундами в день) имеет проблемы с переходом на летнее время! С переходом на летнее время некоторые дни длятся всего 23 часа, а некоторые - 25 часов. - person sbrbot; 05.06.2015
comment
Вопреки тому, что было сказано в последних комментариях, поскольку он был отредактирован @Gordon (16 март 2013, в 9:18), он больше не страдает от проблем с дневным светом. Время установлено на 12 часов дня, поэтому даже с летним временем оно может стать только 11 утра или 13 часов, что все еще остается в тот же день. В цикле он проверяет только дату без времени, не будет пропуска или повторения дней. - person Abu Junayd; 02.12.2015
comment
Это все версии совместимы. - person krishna; 22.06.2016
comment
Этот ответ принадлежит программистам, верящим в ложь о времени. Конечно, всякий раз, когда есть дополнительная секунда, это немного неправильно, но все же неправильно. - person Henk Poley; 10.09.2019

Скопируйте из примера php.net для диапазона включительно:

$begin = new DateTime( '2012-08-01' );
$end = new DateTime( '2012-08-31' );
$end = $end->modify( '+1 day' ); 

$interval = new DateInterval('P1D');
$daterange = new DatePeriod($begin, $interval ,$end);

foreach($daterange as $date){
    echo $date->format("Ymd") . "<br>";
}
person Alexander Kharchenko    schedule 02.09.2016
comment
это лучший и самый полный ответ. Отсутствует только некоторое объяснение значения DateInterval P1D, поэтому вот несколько примеров обозначения периода Два дня: P2D Две секунды: PT2S Одна неделя и десять минут: P1WT10M Y для лет M для месяцев D для дней W для недель. Они преобразуются в дни, поэтому их нельзя комбинировать с D. H для часов M для минут S для секунд - person Orcra; 14.03.2020

Вот еще одна простая реализация -

/**
 * Date range
 *
 * @param $first
 * @param $last
 * @param string $step
 * @param string $format
 * @return array
 */
function dateRange( $first, $last, $step = '+1 day', $format = 'Y-m-d' ) {
    $dates = [];
    $current = strtotime( $first );
    $last = strtotime( $last );

    while( $current <= $last ) {

        $dates[] = date( $format, $current );
        $current = strtotime( $step, $current );
    }

    return $dates;
}

Пример:

print_r( dateRange( '2010-07-26', '2010-08-05') );

Array (
    [0] => 2010-07-26
    [1] => 2010-07-27
    [2] => 2010-07-28
    [3] => 2010-07-29
    [4] => 2010-07-30
    [5] => 2010-07-31
    [6] => 2010-08-01
    [7] => 2010-08-02
    [8] => 2010-08-03
    [9] => 2010-08-04
    [10] => 2010-08-05
)
person HADI    schedule 26.04.2017

Используйте эту функцию: -

function dateRange($first, $last, $step = '+1 day', $format = 'Y-m-d' ) {
                $dates = array();
                $current = strtotime($first);
                $last = strtotime($last);

                while( $current <= $last ) {    
                    $dates[] = date($format, $current);
                    $current = strtotime($step, $current);
                }
                return $dates;
        }

Использование/вызов функции: -

Увеличение на один день: -

dateRange($start, $end); //increment is set to 1 day.

Увеличение по месяцам: -

dateRange($start, $end, "+1 month");//increase by one month

используйте третий параметр, если вы хотите установить формат даты: -

   dateRange($start, $end, "+1 month", "Y-m-d H:i:s");//increase by one month and format is mysql datetime
person user2182143    schedule 27.11.2014

вот способ:

 $date = new Carbon();
 $dtStart = $date->startOfMonth();
 $dtEnd = $dtStart->copy()->endOfMonth();

 $weekendsInMoth = [];
 while ($dtStart->diffInDays($dtEnd)) {

     if($dtStart->isWeekend()) {
            $weekendsInMoth[] = $dtStart->copy();
     }

     $dtStart->addDay();
 }

Результатом $weekendsInMoth является массив выходных дней!

person Jean Souza    schedule 16.08.2016

$date = new DateTime($_POST['date']);
$endDate = date_add(new DateTime($_POST['date']),date_interval_create_from_date_string("7 days"));

while ($date <= $endDate) {
    print date_format($date,'d-m-Y')." AND END DATE IS : ".date_format($endDate,'d-m-Y')."\n";
    date_add($date,date_interval_create_from_date_string("1 days"));
}

Вы также можете повторить итерацию таким образом. $_POST['date'] может быть удалено из вашего приложения или веб-сайта. Вместо $_POST['date'] вы также можете разместить свою строку здесь "21-12-2019". Оба будут работать.

person zukayu    schedule 31.12.2019

Если вы используете Laravel и хотите использовать Carbon, правильным решением будет следующее:

$start_date = Carbon::createFromFormat('Y-m-d', '2020-01-01');
$end_date = Carbon::createFromFormat('Y-m-d', '2020-01-31');

$period = new CarbonPeriod($start_date, '1 day', $end_date);

foreach ($period as $dt) {
 echo $dt->format("l Y-m-d H:i:s\n");
}

Не забудьте добавить:

  • используйте Углерод\Углерод;
  • используйте Carbon\CarbonPeriod;
person Victor Nuñez    schedule 10.03.2020

For Carbon users

$startDay = Carbon::parse("2021-08-01");
$endDay= Carbon::parse("2021-08-05");
$period = $startDay->range($endDay, 1, 'day');

Когда я печатаю данные

array:5 [▼
  0 => Carbon\Carbon @1627776000 {#1290 ▼
    #endOfTime: false
    #startOfTime: false
    #constructedObjectId: "0000000045f68b6a0000000030c488c5"
    #localMonthsOverflow: null
    #localYearsOverflow: null
    #localStrictModeEnabled: null
    #localHumanDiffOptions: null
    #localToStringFormat: null
    #localSerializer: null
    #localMacros: null
    #localGenericMacros: null
    #localFormatFunction: null
    #localTranslator: null
    #dumpProperties: array:3 [▶]
    #dumpLocale: null
    date: 2021-08-01 00:00:00.0 UTC (+00:00)
  }
  1 => Carbon\Carbon @1627862400 {#1291 ▼
    #endOfTime: false
    #startOfTime: false
    #constructedObjectId: "0000000045f68b6b0000000030c488c5"
    #localMonthsOverflow: null
    #localYearsOverflow: null
    #localStrictModeEnabled: null
    #localHumanDiffOptions: null
    #localToStringFormat: null
    #localSerializer: null
    #localMacros: null
    #localGenericMacros: null
    #localFormatFunction: null
    #localTranslator: null
    #dumpProperties: array:3 [▶]
    #dumpLocale: null
    date: 2021-08-02 00:00:00.0 UTC (+00:00)
  }
  2 => Carbon\Carbon @1627948800 {#1292 ▶}
  3 => Carbon\Carbon @1628035200 {#1293 ▶}
  4 => Carbon\Carbon @1628121600 {#1294 ▶}
]

Это дамп данных Laravel с использованием dd($period->toArray());. Теперь вы можете перебирать $period, если хотите, с оператором foreach.

Одно важное примечание: он включает в себя обе крайние даты, предоставленные методу.

Чтобы узнать больше о датах, ознакомьтесь с документами Carbon.

person rule_it_subir    schedule 18.07.2021

person    schedule
comment
Похоже, что это решение медленнее, чем принятый ответ (не запускал некоторые тесты: 100% медленнее для 60 итераций). Но я выбираю этот из-за ретро-совместимости со старыми хостинговыми платформами. - person Ifnot; 24.06.2013