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;
    }
}
hakre
  • 193,403
  • 52
  • 435
  • 836
user3450844
  • 83
  • 2
  • 3
  • 7
  • Is it possible for the first and last element to not be the lowest/highest of the date range? Or will this be guaranteed? – Brad Christie Nov 15 '10 at 17:56
  • It will be guaranteed, there is a function (ksort()) before this that will sort it anyway, just in case! – user3450844 Nov 15 '10 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: http://us3.php.net/manual/en/control-structures.foreach.php – Brad Christie Nov 15 '10 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. – erisco Nov 15 '10 at 18:40

5 Answers5

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"; 
}
dnagirl
  • 20,196
  • 13
  • 80
  • 123
  • 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); – user3450844 Nov 15 '10 at 18:13
  • @SHutch: SO ate my original reply. Looks like I missed a bit of text when recreating it. Fixed now. – dnagirl Nov 15 '10 at 18:18
  • Unfortunately host is running PHP v5.2.14 so this won't work :( – user3450844 Nov 15 '10 at 18:24
  • 2
    @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. – dnagirl Nov 15 '10 at 18:36
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);
Anti Veeranna
  • 11,485
  • 4
  • 42
  • 63
  • Unfortunately host is running PHP v5.2.14 :( – user3450844 Nov 15 '10 at 18:23
  • 1
    +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. – philipp Dec 06 '11 at 01:45
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.

Jakub
  • 20,418
  • 8
  • 65
  • 92
  • strtotime is sort of expensive, but as long as we're not worried about that, this solution seems great (for PHP < 5.3). – erisco Nov 15 '10 at 18:39
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.

erisco
  • 14,154
  • 2
  • 40
  • 45
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);
user3450844
  • 83
  • 2
  • 3
  • 7