/** * Move the internal "pointer" to the next recurrence in the set * and return it. * @return qCal_DateTime The next date/time recurrence in the set * @access public */ public function next() { // make a copy of the start date/time to work with $startDate = $this->current->getDate(); $startTime = $this->current->getTime(); $month = $startDate->getMonth(); $year = $startDate->getYear(); // if there is no "next" recurrence in the timeArray, we need to // regenerate it with new times for the next day in the recurrence list if (!($current = next($this->timeArray))) { // if there is no "next" day in the monthArray, we need to regenerate // the monthArray. It is possible that it hasn't been generated in // the first place, so we need to determine if it should be // regenerated for the current year, or for the next year interval if (!($currentDay = next($this->monthArray))) { // determine if we need to generate the monthArray with the current // year or with the next year interval if (!empty($this->monthArray)) { $nextmonth = $month + $this->getInterval(); if ($nextmonth > 12) { $nextmonth = $nextmonth - 12; $year++; } $month = $nextmonth; } // $this->currentDay will be assigned when the monthArray is created // we will now need to regenerate the monthArray with the data above $this->regenerateMonthArray = true; } else { $this->currentDay = $currentDay; } // regenerate the timeArray once the yearArray is regenerated $this->regenerateTimeArray = true; } else { $this->current = $current; } // create a multi-dimensional array of dates that will be looped over // when the object is looped over. Each date will have one or more // times which will result in even more recurrences. We do not build // the time recurrences for the entire month because it takes too long. // @todo If this is not the first time we are building this array, we // need to skip ahead by $this->interval months. if ($this->regenerateMonthArray) { $monthArray = array(); // this date object is used to find out how many days are in the current month $ml = new qCal_Date($year, $month, 1); for ($d = 1; $d <= $ml->getNumDaysInMonth(); $d++) { $day = new qCal_Date($year, $month, $d); if ($this->checkDateAgainstRules($day)) { // if this day is equal to or greater than the start // date, add it to the yearArray if ($day->getUnixTimestamp() >= $startDate->getUnixTimestamp()) { $monthArray[$day->format("Ymd")] = $day; } } } $this->monthArray = $monthArray; $this->currentDay = current($this->monthArray); // now that we have cached the monthArray, we don't need to // regenerate it until we have gotten to the next year increment $this->regenerateMonthArray = false; } // if the time recurrences for the current date haven't been created // yet, then create them and assign the "current" value to the first // time recurrence in the set. If the time recurrences are already // available, move the "current" position ahead one recurrence. if ($this->regenerateTimeArray) { $this->timeArray = $this->findTimeRecurrences($this->currentDay); // now that we have cached the timeArray, we don't need to // regenerate it until we have gotten to the next day $this->regenerateTimeArray = false; $this->current = current($this->timeArray); } // now find the "next" recurrence, advance the "current" date and // return the recurrence // @TODO When I come back to this tomorrow, I need to take what I have // right now, which is an array of dates that are within the recurrence // rule set, along with an array of times on each day, and I need to // make this object capable of properly looping over these things. return $this->current; /** * This is the old code. It was used to create an array containing twelve arrays (one * for each month) of 28-31 days (one for each day of the month) if (empty($this->yearArray) || $regenerateYearArray) { $yearArray = array(); for($m = 1; $m <= 12; $m++) { $month = new qCal_Date($startDate->getYear(), $m, 1); $monthArray = array(); for ($d = 1; $d <= $month->getNumDaysInMonth(); $d++) { $day = new qCal_Date($startDate->getYear(), $m, $d); $monthArray[$d] = $day; } $yearArray[$m] = $monthArray; } $this->yearArray = $yearArray; } */ }
/** * The date object has many getters which allow for you to determine things like day of the week, * day of the year, etc. The following tests those getters. */ public function testGetters() { $date = new qCal_Date(2009, 4, 23); /** * Month */ $this->assertEqual($date->getMonth(), 4); $this->assertEqual($date->getMonthName(), "April"); $this->assertEqual($date->getNumDaysInMonth(), 30); /** * Day */ $this->assertEqual($date->getDay(), 23); $this->assertEqual($date->getYearDay(), 112); $this->assertEqual($date->getFirstDayOfMonth()->__toString(), "04/01/2009"); $this->assertEqual($date->getFirstDayOfMonth()->format("l"), "Wednesday"); $this->assertEqual($date->getLastDayOfMonth()->__toString(), "04/30/2009"); $this->assertEqual($date->getLastDayOfMonth()->format("l"), "Thursday"); // find the xth weekday (mon-sun) of the month $this->assertEqual($date->getXthWeekdayOfMonth(2)->__toString(), "04/09/2009"); // find the second Thursday of the month (Because 4/23/2009 was on a Thursday, the weekday defaults to that. The year defaults to 2009 for basically the same reason) $this->assertEqual($date->getXthWeekdayOfMonth(2, "Monday")->__toString(), "04/13/2009"); // find the second monday of the month (month defaults to april because that's what $date is currently set to) $this->assertEqual($date->getXthWeekdayOfMonth(2, "Monday", "January")->__toString(), "01/12/2009"); // find the second monday in January (year defaults to 2009) $this->assertEqual($date->getXthWeekdayOfMonth(2, "Monday", "January", 2008)->__toString(), "01/14/2008"); // find the second Monday in January, 2008 // now try negatives and positives $this->assertEqual($date->getXthWeekdayOfMonth(-2)->__toString(), "04/23/2009"); // get the second to last Thursday of the month $this->assertEqual($date->getXthWeekdayOfMonth("-2")->__toString(), "04/23/2009"); // get the second to last Thursday of the month $this->assertEqual($date->getXthWeekdayOfMonth(+2)->__toString(), "04/09/2009"); // surprisingly, this works... interesting... $this->assertEqual($date->getXthWeekdayOfMonth("+2")->__toString(), "04/09/2009"); // we can also use numbers instead of spelling out the names of weekdays and months. For the weekday part, use 0 for Sunday through 6 for Saturday (the same as PHP's date function's "w" metacharacter) $this->assertEqual($date->getXthWeekdayOfMonth(2, 1)->__toString(), "04/13/2009"); // second monday $this->assertEqual($date->getXthWeekdayOfMonth(2, 1, 1)->__toString(), "01/12/2009"); // second monday in january $this->assertEqual($date->getXthWeekdayOfMonth(-2, 1)->__toString(), "04/20/2009"); // second to last monday in april /** * Year */ $this->assertEqual($date->getYear(), 2009); /** * Week */ $this->assertEqual($date->getWeekDay(), 4); $this->assertEqual($date->getWeekDayName(), "Thursday"); $this->assertEqual($date->getWeekOfYear(), 17); /** * Unix Timestamp */ $this->assertEqual($date->getUnixTimestamp(), gmmktime(0, 0, 0, 4, 23, 2009)); }
/** * Determine the number or Tuesdays (or whatever day of the week this date is) since the * beginning or end of the month. * @param integer $xth A positive or negative number that determines which weekday of the month we want * @param string|integer $weekday Either Sunday-Saturday or 0-6 to specify the weekday we want * @param string|integer $month Either January-December or 1-12 to specify the month we want * @param integer $year A valid year to specify which year we want */ public function getXthWeekdayOfMonth($xth, $weekday = null, $month = null, $year = null) { $negpos = substr($xth, 0, 1); if ($negpos == "+" || $negpos == "-") { $xth = (int) substr($xth, 1); } else { $negpos = "+"; } if (is_null($weekday)) { $weekday = $this->getWeekday(); } if (ctype_digit((string) $weekday)) { if (!array_key_exists($weekday, $this->weekdays)) { throw new qCal_Date_Exception_InvalidWeekday("\"{$weekday}\" is not a valid weekday."); } } else { $weekday = strtolower($weekday); if (!in_array($weekday, $this->weekdays)) { throw new qCal_Date_Exception_InvalidWeekday("\"{$weekday}\" is not a valid weekday."); } $wdays = array_flip($this->weekdays); $weekday = $wdays[$weekday]; } if (is_null($month)) { $month = $this->getMonth(); } if (ctype_digit((string) $month)) { if (!array_key_exists($month, $this->months)) { throw new qCal_Date_Exception_InvalidMonth("\"{$month}\" is not a valid month."); } } else { $month = strtolower($month); if (!in_array($month, $this->months)) { throw new qCal_Date_Exception_InvalidMonth("\"{$month}\" is not a valid month."); } $mons = array_flip($this->months); $month = $mons[$month]; } if (is_null($year)) { $year = $this->getYear(); } if (!ctype_digit((string) $year) || strlen($year) != 4) { throw new qCal_Date_Exception_InvalidYear("\"{$year}\" is not a valid year."); } // now, using the year, month and numbered weekday, we need to find the actual day of the month... $firstofmonth = new qCal_Date($year, $month, 1); $numdaysinmonth = $firstofmonth->getNumDaysInMonth(); $numweekdays = 0; // the number of weekdays that have occurred (in the loop) $foundday = false; if ($negpos == "+") { $day = 1; $wday = $firstofmonth->getWeekday(); // while we are in the current month, loop while ($day <= $numdaysinmonth) { // if the specified weekday == the current week day in the loop if ($weekday == $wday) { $numweekdays++; if ($numweekdays == $xth) { // break out of the loop, we've found the right day! yay! $foundday = $day; break; } } if ($wday == 6) { $wday = 0; } else { $wday++; } $day++; } } else { $day = $numdaysinmonth; $lastofmonth = $firstofmonth->getLastDayOfMonth(); $wday = $lastofmonth->getWeekday(); while ($day >= 1) { if ($weekday == $wday) { $numweekdays++; if ($numweekdays == $xth) { // break out of the loop, we've found the right day! yay! $foundday = $day; break; } } if ($wday == 0) { $wday = 6; } else { $wday--; } $day--; } } if ($foundday && checkdate($month, $day, $year)) { $date = new qCal_Date($year, $month, $day); } else { if ($day == 32) { throw new qCal_DateTime_Exception_InvalidDate("You have specified an incorrect number of days for qCal_Date::getXthWeekdayOfMonth()"); } else { throw new qCal_DateTime_Exception_InvalidDate("You have entered an invalid date."); } } return $date; }