263

I'm starting with a date 2010-05-01 and ending with 2010-05-10. How can I iterate through all of those dates in PHP?

16 Answers 16

669
$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");
}

This will output all days in the defined period between $start and $end. If you want to include the 10th, set $end to 11th. You can adjust format to your liking. See the PHP Manual for DatePeriod. It requires PHP 5.3.

16
  • 2
    good news - there is a patch for setting a flag to include the end date which (fingers crossed) will make it into a future version.
    – salathe
    Commented Jul 8, 2010 at 20:51
  • 11
    $begin->setTime(0,0); $end->setTime(12,0); or initializing with the time of day of the start date as any time later than that of the end date will include the end date in the loop. Not the most stylish fix, but it's the best option as long as there's not a proper flag.
    – Chris
    Commented May 30, 2013 at 9:00
  • 43
    If you want to include the end date to your interval, you can do : $end = $end->modify( '+1 day' ); Commented Nov 14, 2014 at 13:52
  • 3
    Is it possible to use this but reverse it, to loop back in history?
    – Jon
    Commented Oct 22, 2017 at 18:39
  • 4
    @JulienITARD thats a pretty good idea but more elegant would be $end->add( $interval ) because it responds directly to a changed interval ;)
    – GDY
    Commented Jun 6, 2019 at 8:36
125

This also includes the last date

$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");
}

If you dont need the last date just remove = from the condition.

1
  • 4
    Be sure to note that $begin will be different after the loop. This loop modifies the object created by new DateTime( "2015-07-03" ). Hence why you ought to use the DateTimeImmutable versions. But you need some further modifications for using them.
    – Henk Poley
    Commented Sep 9, 2019 at 9:44
50

Converting to unix timestamps makes doing date math easier in 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
}

When using PHP with a timezone having DST, make sure to add a time that is not 23:00, 00:00 or 1:00 to protect against days skipping or repeating.

8
  • 4
    I don't like the look of that 86400. I understand that it is 60 * 60 * 24, but still... something about it irks me.
    – MikeD
    Commented Jul 8, 2010 at 20:37
  • 14
    in this case, it works, but if there is a switch between normal and sunlight saving time, it will fail because there's a 90000 second-day that you'll have twice in your loop...
    – oezi
    Commented Jul 8, 2010 at 20:46
  • 3
    Mike, the best thing to do is setup a constant and name it "DAY" so it becomes far easier to read. Commented Jul 8, 2010 at 20:50
  • 5
    This will suffer from daylight savings issues. When you cross a daylight savings time point, it will get screwed up. 12:00am isn't 12:00am on both sides of the point in time.
    – Eric Cope
    Commented Feb 19, 2014 at 8:13
  • 1
    This code does (each code with 86400 seconds per day) have problem with daylight saving! With daylight saving some days last only 23 hours, and some 25 hours.
    – sbrbot
    Commented Jun 4, 2015 at 22:35
30

Copy from php.net sample for inclusive range:

$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>";
}
2
  • 2
    this is the best and most complete answer. Only missing some explanation of the DateInterval value P1D, so here are some Period Designator examples Two days : P2D Two seconds : PT2S One week and ten minutes : P1WT10M Y for years M for months D for days W for weeks. These get converted into days, so can not be combined with D. H for hours M for minutes S for seconds
    – Orcra
    Commented Mar 14, 2020 at 2:45
  • @Orcra To further supplement your explanation: We use 'P1D' because the DateInterval constructor's $duration argument takes the letter P (for period), followed by the duration period represented by an integer value (in our case '1') and then by a period designator (in our case 'D'). Thus, a period duration of 1 day. Refer doc link for further information on using this: php.net/manual/en/dateinterval.construct.php
    – joeljpa
    Commented Feb 15, 2023 at 7:33
24

Here is another simple implementation -

/**
 * 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;
}

Example:

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
)
0
17
$startTime = strtotime('2010-05-01'); 
$endTime = strtotime('2010-05-10'); 

// Loop between timestamps, 1 day at a time 
$i = 1;
do {
   $newTime = strtotime('+'.$i++.' days',$startTime); 
   echo $newTime;
} while ($newTime < $endTime);

or

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

// Loop between timestamps, 1 day at a time 
do {
   $startTime = strtotime('+1 day',$startTime); 
   echo $startTime;
} while ($startTime < $endTime);
1
  • 2
    It appear that this solution is slower than accepted answer (un ran some benchs : 100% slower for 60 iterations). But i choose this one for retro compatibility for old hosting plateforms.
    – Ifnot
    Commented Jun 24, 2013 at 12:55
5

User this function:-

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;
        }

Usage / function call:-

Increase by one day:-

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

Increase by Month:-

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

use third parameter if you like to set date format:-

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

For Carbon users

use Carbon\Carbon;

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

When I print the data

[
     Carbon\Carbon @1627790400 {#4970
       date: 2021-08-01 00:00:00.0 America/Toronto (-04:00),
     },
     Carbon\Carbon @1627876800 {#4974
       date: 2021-08-02 00:00:00.0 America/Toronto (-04:00),
     },
     Carbon\Carbon @1627963200 {#4978
       date: 2021-08-03 00:00:00.0 America/Toronto (-04:00),
     },
     Carbon\Carbon @1628049600 {#5007
       date: 2021-08-04 00:00:00.0 America/Toronto (-04:00),
     },
     Carbon\Carbon @1628136000 {#5009
       date: 2021-08-05 00:00:00.0 America/Toronto (-04:00),
     },
]

This is Laravel data dump using dd($period->toArray());. You can now iterate through $period if you want with a foreach statement.

One important note - it includes both the edge dates provided to method.

For more cool date related stuff, do check out the Carbon docs.

1
  • that was a simplest way Commented Dec 7, 2021 at 3:32
2

here's a way:

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

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

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

     $dtStart->addDay();
 }

The result of $weekendsInMoth is array of weekend days!

2

If you're using php version less than 8.2 and don't have the DatePeriod::INCLUDE_END_DATE const. I wrote a method that returns an array of \DateTimeImmutable.

This works with a start date before, the same or after the end date.

    /**
     * @param DateTimeImmutable $start
     * @param DateTimeImmutable $end
     * @return array<\DateTimeImmutable>
     */
    public static function getRangeDays(\DateTimeImmutable $start, \DateTimeImmutable $end): array
    {
        $startDate = $start;
        $endDate = $end;
        $forwards = $endDate >= $startDate;
        $carryDate = $startDate;
        $days = [];
        while (true) {
            if (($forwards && $carryDate > $end) || (!$forwards && $carryDate < $end)) {
                break;
            }
            $days[] = $carryDate;

            if ($forwards) {
                $carryDate = $carryDate->modify('+1 day');
            } else {
                $carryDate = $carryDate->modify('- 1 day');
            }
        }

        return $days;
    }
2

Just a thought with the while loop

$startDate = '2023-03-01';
$endDate = '2023-04-01';

$currentDate = strtotime($startDate);
$endDate = strtotime($endDate);

while ($currentDate <= $endDate) {
  echo date('Y-m-d', $currentDate) . "\n"; 
  $currentDate = strtotime('+1 day', $currentDate);
}
1
$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"));
}

You can iterate like this also, The $_POST['date'] can be dent from your app or website Instead of $_POST['date'] you can also place your string here "21-12-2019". Both will work.

1
<?php

    $start_date = '2015-01-01';
    $end_date = '2015-06-30';

    while (strtotime($start_date) <= strtotime($end_date)) {
        echo "$start_daten";
        $start_date = date ("Y-m-d", strtotime("+1 days", strtotime($start_date)));
    }

?>
1

I like using simple, clean and library-less methods like this:

function datesBetween($startDate, $endDate)
{
    $dates = [];

    $start = new DateTime($startDate);
    $end = new DateTime($endDate);

    while ($start <= $end) {
        $dates[] = $start->format('Y-m-d');
        $start->modify('+1 day');
    }

    return $dates;
}

Hope it helps someone.

0

If you use Laravel and want to use Carbon the correct solution would be the following:

$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");
}

Remember to add:

  • use Carbon\Carbon;
  • use Carbon\CarbonPeriod;
0

The more elastic example



//dilo surucu

class Day
{
    private DateTimeInterface $dateTime;

    public function __construct(DateTimeInterface $dateTime)
    {
        $this->dateTime = $dateTime;
    }

    public function today(string $format='Y-m-d'): string
    {
        return $this->dateTime->format($format);
    }

    public function yesterday(string $format='Y-m-d'): string
    {
        $today = $this->today();
        return date($format, strtotime("$today -1 days"));
    }

    public function tomorrow(string $format='Y-m-d'): string
    {
        $today = $this->today();
        return date($format, strtotime("$today +1 days"));
    }
}

class DayIterator implements Iterator
{
    private DateTimeInterface $currentDate;
    private DateTimeInterface $endDate;

    /**
     * @throws Exception
     */
    public function __construct(string $startDate, string $endDate)
    {
        $this->currentDate = new DateTime($startDate);
        $this->endDate = new DateTime($endDate);
    }

    public function current(): Day
    {
        return new Day($this->currentDate);
    }

    public function key(): string
    {
        return $this->currentDate->format('Y-m-d');
    }

    public function next(): void
    {
        $this->currentDate = $this->currentDate->add(new DateInterval('P1D'));
    }

    public function rewind(): void
    {
    }

    public function valid(): bool
    {
        return $this->currentDate <= $this->endDate;
    }
}


// Usage
$dayIterator = new DayIterator(
    '2024-01-01',
    '2024-01-12'
);

foreach ($dayIterator as $day) {

    echo 'Yesterday: ' . $day->yesterday('D Y-m-d') . PHP_EOL;
    echo 'Date: ' . $day->today('D Y-m-d D') . PHP_EOL;
    echo 'Tomorrow: ' . $day->tomorrow('D Y-m-d') . PHP_EOL;
    echo PHP_EOL;
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.