104

How can I get all dates between two dates in PHP? Prefer using Carbon for dates.

$from = Carbon::now();
$to = Carbon::createFromDate(2017, 5, 21);

I wanna have all dates between those two dates.. But how? Can only found solutions using strtotime function.

Nasreddine
  • 36,610
  • 17
  • 75
  • 94
user1469734
  • 851
  • 14
  • 50
  • 81

11 Answers11

208

As of Carbon 1.29 it is possible to do:

$period = CarbonPeriod::create('2018-06-14', '2018-06-20');

// Iterate over the period
foreach ($period as $date) {
    echo $date->format('Y-m-d');
}

// Convert the period to an array of dates
$dates = $period->toArray();

See documentation for more details: https://carbon.nesbot.com/docs/#api-period.

Paul
  • 3,186
  • 1
  • 19
  • 22
  • 3
    Just a friendly remember, you can also use `$date->toDateString()` to get date as format `Y-m-d`. [Docs](https://carbon.nesbot.com/docs/#api-formatting). – ibnɘꟻ Feb 17 '21 at 04:14
  • @ibnɘꟻ I'm aware of it, but the `->format()` gives you the ability to globally store the string format in config. So, whenever there is a request to change the format everywhere, you don't need to change the code everywhere. – Dendi Handian Feb 28 '22 at 07:27
76

Here's how I did it with Carbon

private function generateDateRange(Carbon $start_date, Carbon $end_date)
{
    $dates = [];

    for($date = $start_date->copy(); $date->lte($end_date); $date->addDay()) {
        $dates[] = $date->format('Y-m-d');
    }

    return $dates;
}
igaster
  • 12,983
  • 6
  • 26
  • 27
Sebastian Sulinski
  • 5,815
  • 7
  • 39
  • 61
  • 2
    Won't you have the problem of `$start_date` being equal to `$end_date` in the end? If you want to continue using the original `$start_date` after this function is called, you should either pass a copy of the original `$start_date` to this function or set `$date = $start_date -> copy()` in the definition of the `for`-loop, which I would prefer. – molerat Jul 09 '18 at 09:12
22

As Carbon is an extension of PHP's built-in DateTime, you should be able to use DatePeriod and DateInterval, exactly as you would with a DateTime object

$interval = new DateInterval('P1D');
$to->add($interval);
$daterange = new DatePeriod($from, $interval ,$to);

foreach($daterange as $date){
    echo $date->format("Ymd"), PHP_EOL;
}

EDIT

If you need to include the final date of the period, then you need to modify it slightly, and adjust $to before generating the DatePeriod

$interval = new DateInterval('P1D');
$daterange = new DatePeriod($from, $interval ,$to);

foreach($daterange as $date){
    echo $date->format("Ymd"), PHP_EOL;
}
Mark Baker
  • 209,507
  • 32
  • 346
  • 385
  • This does not print last date from the interval if $to time is not bigger. I mean at least on second has to be bigger, maybe milisecond, did not try that – Dariux Sep 10 '15 at 11:19
  • Added relevant line of code to include the $to date in the period – Mark Baker Sep 10 '15 at 11:37
11

Based on Mark Baker's answer, I wrote this function:

/**
 * Compute a range between two dates, and generate
 * a plain array of Carbon objects of each day in it.
 *
 * @param  \Carbon\Carbon  $from
 * @param  \Carbon\Carbon  $to
 * @param  bool  $inclusive
 * @return array|null
 *
 * @author Tristan Jahier
 */
function date_range(Carbon\Carbon $from, Carbon\Carbon $to, $inclusive = true)
{
    if ($from->gt($to)) {
        return null;
    }

    // Clone the date objects to avoid issues, then reset their time
    $from = $from->copy()->startOfDay();
    $to = $to->copy()->startOfDay();

    // Include the end date in the range
    if ($inclusive) {
        $to->addDay();
    }

    $step = Carbon\CarbonInterval::day();
    $period = new DatePeriod($from, $step, $to);

    // Convert the DatePeriod into a plain array of Carbon objects
    $range = [];

    foreach ($period as $day) {
        $range[] = new Carbon\Carbon($day);
    }

    return ! empty($range) ? $range : null;
}

Usage:

>>> date_range(Carbon::parse('2016-07-21'), Carbon::parse('2016-07-23'));
=> [
     Carbon\Carbon {#760
       +"date": "2016-07-21 00:00:00.000000",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
     Carbon\Carbon {#759
       +"date": "2016-07-22 00:00:00.000000",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
     Carbon\Carbon {#761
       +"date": "2016-07-23 00:00:00.000000",
       +"timezone_type": 3,
       +"timezone": "UTC",
     },
   ]

You can also pass a boolean (false) as third argument to exclude the end date.

Tristan Jahier
  • 1,127
  • 1
  • 12
  • 27
6

This can also be done like this:

new DatePeriod($startDate, new DateInterval('P1D'), $endDate)

Just keep in mind that DatePeriod is an iterator, so if you want an actual array:

iterator_to_array(new DatePeriod($startDate, new DateInterval('P1D'), $endDate))

If you're using Laravel, you could always create a Carbon macro:

Carbon::macro('range', function ($start, $end) {
    return new Collection(new DatePeriod($start, new DateInterval('P1D'), $end));
});

Now you can do this:

foreach (Carbon::range($start, $end) as $date) {
   // ...
}
Jonathan
  • 18,229
  • 10
  • 57
  • 56
4

Here is what I have:

private function getDatesFromRange($date_time_from, $date_time_to)
    {

        // cut hours, because not getting last day when hours of time to is less than hours of time_from
        // see while loop
        $start = Carbon::createFromFormat('Y-m-d', substr($date_time_from, 0, 10));
        $end = Carbon::createFromFormat('Y-m-d', substr($date_time_to, 0, 10));

        $dates = [];

        while ($start->lte($end)) {

            $dates[] = $start->copy()->format('Y-m-d');

            $start->addDay();
        }

        return $dates;
    }

Example:

$this->getDatesFromRange('2015-03-15 10:10:10', '2015-03-19 09:10:10');
Dariux
  • 3,953
  • 9
  • 43
  • 69
2

You can't use loop control variable directly, the next must be work fine

$start = Carbon::today()->startOfWeek();
$end = Carbon::today()->endOfWeek();

$stack = [];

$date = $start;
while ($date <= $end) {

    if (! $date->isWeekend()) {
        $stack[] = $date->copy();
    }
    $date->addDays(1);
}

return $stack;
2

You can directly using Carbon

    $start = Carbon::createFromDate(2017, 5, 21);
    $end = Carbon::now();

    while($start < $end){
        echo $start->format("M");
        $start->addMonth();
    }
latecoder
  • 329
  • 1
  • 3
  • 11
-1

I think it's will be better if you use this function

 public static function check_continuous_dates(array $dates): bool
    {
        $startDate = Carbon::create($dates[0]);
        $endDate = Carbon::create(end($dates));
        if ($endDate->diffInDays($startDate)+1 == count($dates)) {
            return true;
        }
        return false;
    } 
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 03 '22 at 06:21
-2
//To get just an array of dates, follow this.


        $period = CarbonPeriod::create('2018-06-14', '2018-06-20');

        $p = array();
    // If you want just dates
    // Iterate over the period and create push to array
        foreach ($period as $date) {
            $p[] = $date->format('Y-m-d');
        }

    // Return an array of dates

        return $p;
  • Please add description to your answer, so anyone can understand what's your code algorithm. Don't spoonfeeding. – ibnɘꟻ Jan 30 '20 at 06:01
-3

Very simple solution (it works with old "<1.29" carbon ) :

// set $start and $end to any date
$start = Carbon::now()->addDays(-10);
$end = Carbon::now();

$dates = [];
for($i = 0; $i < $end->diffInDays($start); $i++){
    $dates[] = (clone $start)->addDays($i)->format('Y-m-d');
}
dd($dates);
fico7489
  • 7,931
  • 7
  • 55
  • 89
  • Amazingly slow solution – user1469734 May 02 '20 at 07:27
  • fastest solution is obviously with CarbonPeriod but it is a relative new Caarbon feature and many app can not use that, my solutions covers older carbon version and is the same fast as solution from @Sebastien ...I measured it just now...... – fico7489 May 02 '20 at 08:22