Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP - Is there a simple way to loop between two dates and fill in missing values?

Tags:

php

I have 2 dates. Lets say they look like this.

$start = 2010/12/24;
$end = 2012/01/05;

I query the database to look for visits between these two dates. I find some. I then populate an array called stats.

$stats['2010/12/25'] = 50;
$stats['2010/12/31'] = 25;
...

As you can see, there are days missing. I need to fill the missing dates with a value of zero. I was thinking something like this. (I have pulled day / month / year from start and end dates.

for($y=$start_year; $y <= $end_year; $y++) {
    for($m=$start_month; $m <=$end_month; $m++) {
        for($d=$start_day; $d <= $end_day; $d++) {

This would work fine for the year however the months and days wouldn't work. If the start day is the 15th. Days 1-14 of each subsequent month would be missed. I could have a solution like this then...

for($y=$start_year; $y <= $end_year; $y++) {
    for($m=1; $m <13; $m++) {
         $total_days = cal_days_in_month(CAL_GREGORIAN, $m, $y) + 1;
         for($d=1; $d <= $total_days; $d++) {

I would then need a bunch of if statements making sure starting and end months and days are valid.

Is there a better way of doing this? Or could this even be done in my mysql query?

like image 961
JasonS Avatar asked Nov 28 '22 09:11

JasonS


2 Answers

Just to demonstrate the power of some of PHP's newer interval handling method (mentioned by pgl in his answer):

$startDate = DateTime::createFromFormat("Y/m/d","2010/12/24",new DateTimeZone("Europe/London"));
$endDate = DateTime::createFromFormat("Y/m/d","2012/01/05",new DateTimeZone("Europe/London"));

$periodInterval = new DateInterval( "P1D" ); // 1-day, though can be more sophisticated rule
$period = new DatePeriod( $startDate, $periodInterval, $endDate );

foreach($period as $date){
   echo $date->format("Y-m-d") , PHP_EOL;
}

Does require PHP >= 5.3.0

EDIT

If you need to include the actual end date, then you need to add a day to $endDate immediately before the foreach() loop:

$endDate->add( $periodInterval );

EDIT #2

$startDate = new DateTime("2010/12/24",new DateTimeZone("Europe/London"));
$endDate = new DateTime("2012/01/05",new DateTimeZone("Europe/London"));

do {
   echo $startDate->format("Y-m-d") , PHP_EOL;
   $startDate->modify("+1 day");
} while ($startDate <= $endDate);

For PHP 5.2.0 (or earlier if dateTime objects are enabled)

like image 107
Mark Baker Avatar answered Dec 20 '22 09:12

Mark Baker


If you're using PHP5.3 then Mark Baker's answer is the one to use. If (as you say in your comment) you're still on PHP5.2 something like this should help you:

$startdate = strtotime( '2010/12/24' );
$enddate = strtotime( '2012/01/05' );
$loopdate = $startdate;
$datesArray = array();
while( $loopdate <= $enddate ) {
   $datesArray[$loopdate] = 0;
   $loopdate = strtotime( '+1 day', $loopdate );
}

It will create an array of the unix timestamp of every date between the start and end dates as the index and each value set to zero. You can then overwrite any actual results you have with the correct values.

like image 29
liquorvicar Avatar answered Dec 20 '22 09:12

liquorvicar