7

DatePeriod is a PHP class for handling recurring dates. It has a very limited number of methods. So when I want to do basic array functions with the recurring dates, I have to copy it to an array with iterator_to_array. Strangely, copying it seems to clobber it. Any ideas why?

$p=new DatePeriod(date_create('2008-01-01'),
                  DateInterval::createFromDateString( "+2 days" ),
                  date_create('2008-12-31')); 

echo count(iterator_to_array($p)); //183 

$a=iterator_to_array($p); 
echo count($a); //0 
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
dnagirl
  • 20,196
  • 13
  • 80
  • 123

3 Answers3

3

I wonder if maybe the iterator isn't being re-wound by iterator_to_array(), so the second call starts iterating with the cursor at the end. You could try this:

$p->rewind()
$a=iterator_to_array($p); 

If the iterator is not rewindable, you could try cloning the object before you traverse it, e.g.

$p2 = clone $p;
echo count(iterator_to_array($p2));

$array = iterator_to_array($p);
Tom Haigh
  • 57,217
  • 21
  • 114
  • 142
  • It appears that DatePeriod does not have a rewind method. :( The documentation says that DatePeriod implements Traversable. But Traversable is an abstract interface that requires Iterator or IteratorAggregate to be implemented (neither of which are mentioned in the DatePeriod docs). IteratorAggregate does not appear to have a public rewind method, so perhaps DatePeriod is a child of it rather than Iterator? – dnagirl Sep 15 '09 at 13:26
  • Sadly, clone is not an option. It appears to cause a fatal error, but of what type I don't know since it doesn't show up in the web logs. – dnagirl Sep 15 '09 at 13:37
  • 1
    As far as I'm concerned the new date and time implementations in PHP 5.3 suck big time. – Ionuț G. Stan Sep 15 '09 at 13:46
3

Here's what I'd do. I'd extend DatePeriod and implement a toArray method:

class MyDatePeriod extends DatePeriod
{
    public $dates;

    public function toArray()
    {
        if ($this->dates === null) {
            $this->dates = iterator_to_array($this);
        }

        return $this->dates;
    }
}

$p = new MyDatePeriod(date_create('2008-01-01'),
                      DateInterval::createFromDateString( "+2 days" ),
                      date_create('2008-12-31'));

echo count($p->toArray()) . "\n"; // 183

echo count($p->toArray()) . "\n"; // 183
Ionuț G. Stan
  • 176,118
  • 18
  • 189
  • 202
  • I don't understand why iterator_to_array($this) from inside the class doesn't do the same as iterator_to_array($p) from outside. But I'm sure glad it does! Thanks very much. – dnagirl Sep 15 '09 at 14:10
  • 1
    It does the same, but I'm caching the result into an instance variable, `$dates`, so on subsequent calls to `toArray` there are no more calls to `iterator_to_array`. I've simply returned the cached result. – Ionuț G. Stan Sep 15 '09 at 14:18
2

Presumably, the first call traverses all the elements in the iterator (i.e. calls next() until valid() is false). The sensible behaviour is for iterator_to_array to begin the conversion from the current position in the iterator - having it silently rewind would be inflexible, and possibly bug inducing.

Try rewinding the iterator before using it again.

$p=new DatePeriod(date_create('2008-01-01'),
                  DateInterval::createFromDateString( "+2 days" ),
                  date_create('2008-12-31')); 

echo count(iterator_to_array($p)); //183 

$p->rewind(); // Newly added!

$a=iterator_to_array($p); 
echo count($a); //0
Adam Wright
  • 48,938
  • 12
  • 131
  • 152