2

I have the following array:

Array ( [2010-10-30] => 1 [2010-11-11] => 1 [2010-11-13] => 11 )

I am trying to fill in the array with all the missing dates between the first and last elements. I was attempting using the following but got nowhere:

foreach($users_by_date as $key => $value){
    $real_next_day = date($key, time()+86400);
    $array_next_day = key(next($users_by_date));
    if($real_next_day != $array_next_day){
      $users_by_date[$real_next_day] = $value;
    }
}
4
  • Is it possible for the first and last element to not be the lowest/highest of the date range? Or will this be guaranteed? Commented Nov 15, 2010 at 17:56
  • It will be guaranteed, there is a function (ksort()) before this that will sort it anyway, just in case! Commented Nov 15, 2010 at 17:58
  • Alright. Give me a sec, but just while I'm doing this and as a general rule, don't use a foreach when you're modifying the keys/indexes of an array. The foreach is just to enumerate the elements and "locks" element changes while iterating through (not so much lock as it's a copy, but you can think of it in these terms). For more information see PHP's foreach manual: us3.php.net/manual/en/control-structures.foreach.php Commented Nov 15, 2010 at 18:04
  • Is it practical to index by timestamp instead of formatted date string? A solution might be a bit cleaner if you can. Commented Nov 15, 2010 at 18:40

5 Answers 5

8

The DateTime, DateInterval and DatePeriod classes can really help out here.

$begin=date_create('2010-10-30');
$end=date_create('2010-11-13');
$i = new DateInterval('P1D');
$period=new DatePeriod($begin,$i,$end);

foreach ($period as $d){
  $day=$d->format('Y-m-d');
  $usercount= isset($users_by_date[$day]) ? $users_by_date[$day] :0;
  echo "$day $usercount"; 
}
Sign up to request clarification or add additional context in comments.

4 Comments

Not sure what, but something is wrong with this (besides the incomplete new DatePeriod. $begin = date_create('2010-10-30'); $end = date_create('2010-11-13'); $i = new DateInterval('P1D'); $period = new DatePeriod($begin, $i, $end); foreach($period as $d){ $day = $d->format('Y-m-d'); $usercount = isset($users_by_date[$day]) ? $users_by_date[$day] :0; echo "$day $usercount"; } print_r($users_by_date);
@SHutch: SO ate my original reply. Looks like I missed a bit of text when recreating it. Fixed now.
Unfortunately host is running PHP v5.2.14 so this won't work :(
@SHutch: that is very sad. I suggest pie (or cake). If your sysadmin isn't susceptible to bribery, then you can eat it yourself. Either way, you feel better.
5

I have been waiting for a chance to try out DateTime and DateInterval objects in PHP 5.3, your question was the perfect opportunity to do just that. Note that this code will not work with PHP versions earlier than 5.3

<?php
$dates = array('2010-10-30' => 1, '2010-11-01' => 1, '2010-11-13' => 1);

// get start and end out of array
reset($dates);
$start = new DateTime(key($dates));

end($dates);
$end   = new DateTime(key($dates));

foreach (new DatePeriod($start, new DateInterval('P1D'), $end) as $date) {
    $dateKey = $date->format('Y-m-d'); // get properly formatted date out of DateTime object
    if (!isset($dates[$dateKey])) {
        $dates[$dateKey] = 1;
    }
}

print_r($dates);

2 Comments

Unfortunately host is running PHP v5.2.14 :(
+1 for the 'foreach' loop over the date range and adding the values to the same array. I still have to sort the array after to keep the order of the dates as they are added to the end. I had a solution where I copied them over, but liked yours more.
1

The functions you are looking for (but not using in your example) are strtotime & diff

You would get the day range between your two dates $numdiff, and simply do something in a loop that would do:

for ($i=1; $i<=$numdiff; $i++) { 
   echo date("Y-m-d", strtotime("2010-10-30 +".$i." day"));
}

Result should be something like:

2010-10-31
2010-11-01
2010-11-02...

You could then pop that into your array as needed. Hope that gets you started in the right direction.

1 Comment

strtotime is sort of expensive, but as long as we're not worried about that, this solution seems great (for PHP < 5.3).
0

For indexes using timestamps, you can generate your array easily using the range function.

$startStamp = ...
$endStamp = ...
$oneDay = 60*60*24;

$timeIndexes = range($startStamp, $endStamp, $oneDay);
$filler = array_fill(0, count($timeIndexes), null);
$timeArray = array_combine($timeIndexes, $filler);

I am not sure what values you want in the array, but hopefully it is relatively straight-forward from here.

If you are going to be converting every timestamp to a formatted date string anyhow and would just prefer to use date strings in the first place, consider this modification.

$dateStringIndexes = array_map(
    function ($t) {
        return date('Y-m-d', $t);
    },
    $timeIndexes
);

Of course, since you are on PHP 5.2, you will likely have to compromise with a foreach loop instead of the closure.

Comments

0

This is the PHP 5.2 capable function I came up with that works.

Thanks guys

reset($users_by_date);
    $date = key($users_by_date);

    end($users_by_date);
    $end =  key($users_by_date);

    while(strtotime($date) <= strtotime($end)){
        $datearray[] = date("Y-m-d", strtotime($date));
        $date = date("Y-m-d", strtotime("+1 day", strtotime($date)));
    }

    foreach($datearray as $key => $value){
        if(!isset($users_by_date[$value])){
            $users_by_date[$value] = 0;
        }
    }
    ksort($users_by_date);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.