views:

255

answers:

1

I am attempting to implement Martin Fowler's class model for recurring events in PHP. The DateTime and DatePeriod PHP objects look like they might be a good fit for dealing with temporal expressions. The issue is that I need to be able to perform UNION, INTERSECT and DIFFERENCE operations on the DatePeriods so that I can calculate expressions like "every Tuesday except dates that are even". That example would require the difference of an 'every Tuesday' DatePeriod and an 'even date' DatePeriod.

DatePeriod is iterable, but it is not accessible to standard array handling functions like array_intersect() and array_diff(). (I wish!) So I tried changing the DatePeriod to an array with $ap=array_to_iterator($dp). This works mostly as expected, except that the DateTime objects that DatePeriod contains do not have a __toString() method, something that many array functions require.

My most recent attempt comes closest:

function recur_union($p1,$p2){
 $a1=(is_object($p1)) ? iterator_to_array($p1):$p1;
 $d_arr=recur_difference($p2,$a1);
 return array_merge($a1,$d_arr);
}

function recur_difference($p1,$p2){
 $arr=array();
 $a2=(is_object($p2)) ? iterator_to_array($p2):$p2;

 foreach($p1 as $dt){
  if(!in_array($dt,$a2))$arr[]=$dt;
 }
 return $arr;
}

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

$au=recur_union($p1,$p2);
$ad=recur_difference($p1,$p2);
echo $au, $ad;

Unfortunately, it appears that the DatePeriods $p1 and $p2 get clobbered during processing. If I do recur_union($p1,$p2) first, I get a valid result. If done after recur_difference($p1,$p2), recur_union returns an empty array. The same occurs for recur_difference.

Question #1: Can anyone explain why iterator_to_array seems to obliterate the original DatePeriod?

Since my DatePeriods were being clobbered, I thought I'd try cloning them. But the following causes IE8 to display the "cannot display the webpage" message and FF3.5 to do nothing:

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

$a1=clone $p1;
$a2=clone $p2;

$au=recur_union($p1,$p2);
$ad=recur_difference($a1,$a2);

echo $au, $ad;

Question #2: What is going on with the clones?

Thanks All!

+1  A: 

I had a similar question last year, although for me, I was having difficulty with relating it to a persistence layer. For the application layer, one approach is to divide up the job into one set of classes that handles the set operations (intersection, union, difference) and one that handles the "temporal expressions" (every month, last day of month). The temporal classes can then be composed together to generate arbitrarily complex date conditions.

I've written about this topic and provided sample classes here and here.

majelbstoat
That's an interesting take. I ended up extending DatePeriod to a class I called Recurrence, which could be accessed like an array and didn't have any issues with cloning. Then I made Temporal_Expression classes (union, difference, intersect) that know how to do set operations with Recurrence objects. The logic for how operations are grouped and ordered is dealt with at a higher level.
dnagirl