Get the next, or previous day of week from the current date

Given a date, get the next or previous Monday, or Tuesday etc from the given date. This is useful when working with ical recurring dates.  If the date given is already that day of week, it is returned as the result.

For example to get the following

Every 20th Monday of the year, forever:
DTSTART;TZID=US-Eastern:19970519T090000
RRULE:FREQ=YEARLY;BYDAY=20MO

==> (1997 9:00 AM EDT)May 19
(1998 9:00 AM EDT)May 18
(1999 9:00 AM EDT)May 17

One could get the first Monday of the year, the it is fairly easy to get the 20th !

<?php

global $amr_day_of_week_no;
$amr_day_of_week_no = array (
 'MO' => 1,
 'TU' => 2,
 'WE' => 3,
 'TH' => 4,
 'FR' => 5,
 'SA' => 6,
 'SU' => 0
 );    

 function amr_goto_byday ($dateobj, $byday, $sign)    {
 global $amr_day_of_week_no;    
 $dayofweek = $dateobj->format('w'); /* 0=sunday, 6 = saturday */
 if ($dayofweek == '-1') $dayofweek = get_oldweekdays($dateobj); /* php seems to break around 1760   */
$target     = $amr_day_of_week_no[$byday]; /*  mo=1 ,su=7  */
 $adjustment = $target - $dayofweek;
 if ($sign === '+') {
 if ($adjustment < 0) $adjustment = $adjustment + 7;        
 }
 else if ($adjustment > 0) $adjustment = $adjustment-7;    
 $d2 = new DateTime();
 $d2 = clone ($dateobj);         
 date_modify ($d2,$adjustment.' days');    
 return ($d2);
 }    

 /* --------Test data ------------------------- */
$d[] = new DateTime('2009-12-25');
$d[] = new DateTime('2009-12-28');
$d[] = new DateTime('2009-12-29');
$d[] = new DateTime('2009-12-30');
$d[] = new DateTime('2009-12-31');
$d[] = new DateTime('2010-01-01');
$d[] = new DateTime('2010-01-02');
$d[] = new DateTime('2010-01-03');
$d[] = new DateTime('2010-01-04');
$d[] = new DateTime('2010-01-05');
$d[] = new DateTime('2010-01-06');
$d[] = new DateTime('2010-01-07');
$d[] = new DateTime('2010-01-08');
$d[] = new DateTime('2010-01-09');
$d[] = new DateTime('2010-01-10');
$d[] = new DateTime('2010-01-11');

echo '<table>';    
foreach (array ('TU','WE','TH','FR','SA','SU','MO') as $day) {
 echo '<tr><td> Aiming for '.$day.'</td><td>If not this, then next</td><td>If not this, then prev</td></tr>';                
 foreach ($d as $i => $d2)    {
 $d3 = amr_goto_byday ($d2, $day, '+');
 $d4 = amr_goto_byday ($d2, $day, '-');
 $s = $d2->format('Y m d l');
 echo '<tr><td> '.$s.'</td><td>'.$d3->format('l,Y m d').'</td><td>'.$d4->format('l,Y m d').'</td></tr>';
 }    
}
echo '</table>';   
  
?>

Day of week for historical dates earlier than 1760

One of the ics files that I encountered in testing my events list plugin came from Google and had a “Zero Year” date in it.  There were probably reasons for this and I have to be thankful for the happening –   In looking into it I foundthat the php date format function for day of week $dateobj->format(‘w’);  “breaks” around the year 1760 and starts returning a “-1”.  Since I do have people using my plugin for anniversaries with earlier start dates (It’s not all musical gigs and sporting events!), I felt I needed to cope with it.

I now use the following code, which appears to work when comparing the weekdays using http://www.searchforancestors.com/utility/dayofweek.html

$w = $dateobj->format('w');
if ($w == '-1') $w = get_oldweekdays($dateobj);

function get_oldweekdays ($d) {
   $dummy = new DateTime();
   $dummy = clone ($d);
   date_modify($dummy,'+91500 weeks'); 
   /* add weeks to get us back to a "working" date - 
   a guess from when the date started breaking, plus some extra to be safe */
   $w = $dummy->format('w');
 return($w);
}