I have 2 dates in PHP, how can I run a foreach loop to go through all of those days?
Asked Answered
H

16

261

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?

Heavenward answered 8/7, 2010 at 20:32 Comment(0)
H
667
$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.

Halda answered 8/7, 2010 at 20:43 Comment(15)
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.Burlesque
$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.Jemy
This also doesn't work if the start and end dates are the same (say you are letting someone pick the start and end dates in a date picker), so I add one day to the end dateValadez
@MatthewLock umm, out of curiosity, what you did you expect it to do when the start and end date are on the same day? I mean, there is no days to go through then, so it's kinda obvious that it doesn't work then.Halda
I expected it to include the last day (which is the same as the first) so to each one date (the first/last date)Valadez
it looks to be a clean way, but it is not the most efficient: creating the array causes a loop and and also extra mem utilization. for+computation loops as proposed in other answers are harder to read but more efficient in execution.Monda
If you want to include the end date to your interval, you can do : $end = $end->modify( '+1 day' );Nannienanning
Is it possible to use this but reverse it, to loop back in history?Pup
@Halda I realized after I posted that it was a dumb question. You can’t actually just reverse the dates, as that seems to make it fail though. But for my use, I ended up just using the start date in the past and it worked.Pup
@Nannienanning thats a pretty good idea but more elegant would be $end->add( $interval ) because it responds directly to a changed interval ;)Expressivity
This is wrong! You aint get all the datesTharp
@user1713785 how so? 3v4l.org/u63FH does exactly what my answer describes.Halda
@Halda you lost 2010-05-10Tharp
@user see my answer "If you want to include the 10th, set $end to 11th." Or use what JulienITARD suggested in the comments above. In any case: works as described.Halda
I created a LiveDemo for this beautiful answer with leap year example and the trick of issuing the setTime() to include end date into results set. Gordon thanks for your post and @Jemy for the time trick!Flowering
R
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.

Rotunda answered 30/6, 2015 at 13:26 Comment(1)
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.Ably
A
49

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.

Alfrediaalfredo answered 8/7, 2010 at 20:35 Comment(8)
I don't like the look of that 86400. I understand that it is 60 * 60 * 24, but still... something about it irks me.Smilacaceous
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...Metamer
Mike, the best thing to do is setup a constant and name it "DAY" so it becomes far easier to read.Marionmarionette
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.Ravenravening
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.Reachmedown
Contrarily to what has been said in the latest comments, since it was edited by @Halda (Mar 16 '13 at 9:18) this doesn't suffer from daylight issues anymore. The time is set to 12pm, so even with DST it could only become 11am or 1pm - which still remans in the same day. In the loop, he's only checking for the date without the time, there will be no days skipping or repeating.Neuron
This is all version compatible.Taritariff
This answer belongs to "Falsehoods programmers believe about time".. Certainly whenever there's a leapsecond it's "slightly" wrong, but still wrong.Ably
M
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>";
}
Mozellemozes answered 2/9, 2016 at 14:37 Comment(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 secondsMonatomic
@Monatomic 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.phpJoyann
Y
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
)
Yasmineyasu answered 26/4, 2017 at 7:46 Comment(0)
S
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);
Snob answered 8/7, 2010 at 20:42 Comment(1)
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.Loony
L
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
Lungi answered 27/11, 2014 at 9:10 Comment(0)
C
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.

Clyde answered 18/7, 2021 at 8:16 Comment(1)
that was a simplest wayMckale
F
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!

Fanti answered 16/8, 2016 at 13:42 Comment(0)
E
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;
    }
Enormous answered 13/12, 2022 at 19:20 Comment(0)
A
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);
}
Acerate answered 11/4, 2023 at 7:9 Comment(0)
D
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.

Dr answered 31/12, 2019 at 6:23 Comment(0)
J
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)));
    }

?>
Jara answered 15/4, 2020 at 22:42 Comment(0)
T
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.

Towering answered 3/7, 2023 at 21:16 Comment(0)
C
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;
Cristal answered 10/3, 2020 at 13:58 Comment(0)
R
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;
}

Regalia answered 13/2 at 18:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.