* SPDX-License-Identifier: AGPL-3.0-only ************************************/ require_once('include/utils/utils.php'); require_once('modules/Calendar/Date.php'); class RecurringType { var $recur_type; var $startdate; var $enddate; var $recur_freq; var $dayofweek_to_rpt = array(); var $repeat_monthby; var $rptmonth_datevalue; var $rptmonth_daytype; var $rpt_count; // crmv@185576 var $recurringdates = array(); var $reminder; /** * Constructor for class RecurringType * @param array $repeat_arr - array contains recurring info */ function __construct($repeat_arr) { $st_date = explode("-", $repeat_arr["startdate"]); $st_time = explode(":", $repeat_arr["starttime"]); $end_date = explode("-", $repeat_arr["enddate"]); $end_time = explode(":", $repeat_arr['endtime']); $start_date = Array( 'day' => $st_date[2], 'month' => $st_date[1], 'year' => $st_date[0], 'hour' => $st_time[0], 'min' => $st_time[1] ); $end_date = Array( 'day' => $end_date[2], 'month' => $end_date[1], 'year' => $end_date[0], 'hour' => $end_time[0], 'min' => $end_time[1] ); $this->startdate = new vt_DateTime($start_date, true); $this->enddate = new vt_DateTime($end_date, true); $this->rpt_count = $repeat_arr['count']; // crmv@185576 $this->recur_type = $repeat_arr['type']; $this->recur_freq = $repeat_arr['repeat_frequency']; if (empty($this->recur_freq)) { $this->recur_freq = 1; } $this->dayofweek_to_rpt = $repeat_arr['dayofweek_to_repeat']; $this->repeat_monthby = $repeat_arr['repeatmonth_type']; if (isset($repeat_arr['repeatmonth_date'])) $this->rptmonth_datevalue = $repeat_arr['repeatmonth_date']; $this->rptmonth_daytype = $repeat_arr['repeatmonth_daytype']; $this->recurringdates = $this->_getRecurringDates(); } public static function fromUserRequest($requestArray) { // All the information from the user is received in User Time zone // Convert Start date and Time to DB Time zone $startDateObj = DateTimeField::convertToDBTimeZone($requestArray["startdate"] . ' ' . $requestArray['starttime']); $requestArray['startdate'] = $startDate = $startDateObj->format('Y-m-d'); $requestArray['starttime'] = $startTime = $startDateObj->format('H:i'); $endDateObj = DateTimeField::convertToDBTimeZone($requestArray["enddate"] . ' ' . $requestArray['endtime']); $requestArray['enddate'] = $endDate = $endDateObj->format('Y-m-d'); $requestArray['endtime'] = $endTime = $endDateObj->format('H:i'); if ($requestArray['sun_flag']) { $requestArray['dayofweek_to_repeat'][] = 0; } if ($requestArray['mon_flag']) { $requestArray['dayofweek_to_repeat'][] = 1; } if ($requestArray['tue_flag']) { $requestArray['dayofweek_to_repeat'][] = 2; } if ($requestArray['wed_flag']) { $requestArray['dayofweek_to_repeat'][] = 3; } if ($requestArray['thu_flag']) { $requestArray['dayofweek_to_repeat'][] = 4; } if ($requestArray['fri_flag']) { $requestArray['dayofweek_to_repeat'][] = 5; } if ($requestArray['sat_flag']) { $requestArray['dayofweek_to_repeat'][] = 6; } if ($requestArray['type'] == 'Weekly') { if ($requestArray['dayofweek_to_repeat'] != null) { $userStartDateTime = DateTimeField::convertToUserTimeZone($startDate . ' ' . $startTime); $dayOfWeek = $requestArray['dayofweek_to_repeat']; $dbDaysOfWeek = array(); for ($i = 0; $i < count($dayOfWeek); ++$i) { $selectedDayOfWeek = $dayOfWeek[$i]; $currentDayOfWeek = $userStartDateTime->format('w'); $newDate = $userStartDateTime->format('d') + ($selectedDayOfWeek - $currentDayOfWeek); $userStartDateTime->setDate($userStartDateTime->format('Y'), $userStartDateTime->format('m'), $newDate); $dbDaysOfWeek[] = $userStartDateTime->format('w'); } $requestArray['dayofweek_to_repeat'] = $dbDaysOfWeek; } } elseif ($requestArray['type'] == 'Monthly') { $userStartDateTime = DateTimeField::convertToUserTimeZone($startDate . ' ' . $startTime); if ($requestArray['repeatmonth_type'] == 'date') { $dayOfMonth = $requestArray['repeatmonth_date']; $userStartDateTime->setDate($userStartDateTime->format('Y'), $userStartDateTime->format('m'), $dayOfMonth); $userStartDateTime->setTimezone(new DateTimeZone(DateTimeField::getDBTimeZone())); $requestArray['repeatmonth_date'] = $userStartDateTime->format('d'); } else { // crmv@185501 - removed useless code! } } return new RecurringType($requestArray); } public static function fromDBRequest($resultRow) { // All the information from the database is received in DB Time zone $repeatInfo = array(); $repeatInfo['startdate'] = $startDate = $resultRow['date_start']; $repeatInfo['starttime'] = $startTime = $resultRow['time_start']; $repeatInfo['enddate'] = $endDate = $resultRow['due_date']; $repeatInfo['endtime'] = $endTime = $resultRow['time_end']; $repeatInfo['type'] = $resultRow['recurringtype']; $repeatInfo['repeat_frequency'] = $resultRow['recurringfreq']; $recurringInfoString = $resultRow['recurringinfo']; $recurringInfo = explode('::', $recurringInfoString); if ($repeatInfo['type'] == 'Weekly') { $startIndex = 1; // 0 is for Recurring Type $length = count($recurringInfo); $j = 0; for ($i = $startIndex; $i < $length; ++$i) { $repeatInfo['dayofweek_to_repeat'][$j++] = $recurringInfo[$i]; } } elseif ($repeatInfo['type'] == 'Monthly') { $repeatInfo['repeatmonth_type'] = $recurringInfo[1]; if ($repeatInfo['repeatmonth_type'] == 'date') { $repeatInfo['repeatmonth_date'] = $recurringInfo[2]; } else { $repeatInfo['repeatmonth_daytype'] = $recurringInfo[2]; $repeatInfo['dayofweek_to_repeat'][0] = $recurringInfo[3]; } } return new RecurringType($repeatInfo); } // crmv@185576 /** * Create the object from a ical string * If unsupported values are passed, null is returned */ public static function fromRRule($rrule, $activity = array()) { global $default_timezone; // map RFC 5545 frequencies to vte freqs $freqMap = array('DAILY' => 'Daily', 'WEEKLY' => 'Weekly', 'MONTHLY' => 'Monthly', 'YEARLY' => 'Yearly'); $dayMap = array("SU" => 0, "MO" => 1, "TU" => 2, "WE" => 3, "TH" => 4, "FR" => 5, "SA" => 6); $seqMap = array(-1 => 'last', 1 => 'first', 2 => 'second', 3 => 'third', 4 => 'fourth', 5 => 'fifth'); // check for unsupported properties if ($rrule['BYYEARDAY']) return null; if ($rrule['BYMONTH']) return null; if ($rrule['BYWEEKNO']) return null; if ($rrule['BYHOUR']) return null; if ($rrule['BYMINUTE']) return null; if ($rrule['BYSECOND']) return null; if ($rrule['BYSETPOS']) return null; //if ($rrule['WKST']) return null; // this is ignored $repeatInfo = array(); $repeatInfo['type'] = $freqMap[$rrule['FREQ']]; if (empty($repeatInfo['type'])) return null; $repeatInfo['repeat_frequency'] = $rrule['INTERVAL'] ?: 1; $repeatInfo['startdate'] = $activity['date_start'] ?: date('Y-m-d'); $repeatInfo['starttime'] = $activity['time_start'] ?: date('H:i'); if ($rrule['UNTIL']) { $dt = self::icalTS2Date($rrule['UNTIL']); $repeatInfo['enddate'] = $dt->format('Y-m-d'); $repeatInfo['endtime'] = $dt->format('H:i'); } elseif ($rrule['COUNT']) { $repeatInfo['count'] = $rrule['COUNT']; } else { $repeatInfo['enddate'] = $activity['due_date'] ?: date('Y-m-d'); $repeatInfo['endtime'] = $activity['time_end']; } if ($repeatInfo['type'] == 'Weekly') { $repeatInfo['dayofweek_to_repeat'] = array(); if ($rrule['BYDAY']) { // crmv@186753 if (count($rrule['BYDAY']) == 1) { $dayn = $dayMap[$rrule['BYDAY']['DAY']]; $repeatInfo['dayofweek_to_repeat'][] = $dayn; } else { foreach ($rrule['BYDAY'] as $day) { $dayn = $dayMap[$day['DAY']]; $repeatInfo['dayofweek_to_repeat'][] = $dayn; } } // crmv@186753e } else { // use the current date as day $startTs = strtotime($repeatInfo['startdate'].' '.$repeatInfo['starttime'].':00'); $startDow = date('w', $startTs); $repeatInfo['dayofweek_to_repeat'][] = $startDow; } } elseif ($repeatInfo['type'] == 'Monthly') { if ($rrule['BYMONTHDAY']) { $repeatInfo['repeatmonth_type'] = 'date'; $repeatInfo['repeatmonth_date'] = $rrule['BYMONTHDAY']; } else { if ($rrule['BYDAY']) { $repeatInfo['dayofweek_to_repeat'] = array(); $repeatInfo['repeatmonth_type'] = 'day'; /*foreach ($rrule['BYDAY'] as $day) { $seq = $seqMap[] }*/ $seq = $seqMap[$rrule['BYDAY'][0]]; $dayn = $dayMap[$rrule['BYDAY']['DAY']]; $repeatInfo['repeatmonth_daytype'] = $seq; $repeatInfo['dayofweek_to_repeat'][] = $dayn; } else { // use the current date as day $startTs = strtotime($repeatInfo['startdate'].' '.$repeatInfo['starttime'].':00'); $startDom = date('d', $startTs); $repeatInfo['repeatmonth_type'] = 'date'; $repeatInfo['repeatmonth_date'] = $startDom; } } } return new RecurringType($repeatInfo); } protected static function icalTS2Date($datetime){ global $default_timezone; if ($datetime['tz'] == 'Z') { $inputTz = new DateTimeZone('UTC'); } else { $inputTz = new DateTimeZone($default_timezone); } $dt = new DateTime('now', $inputTz); $dt->setDate($datetime['year'], $datetime['month'], $datetime['day']); $dt->setTime(intval($datetime['hour']), intval($datetime['min']), intval($datetime['sec'])); $dt->setTimezone(new DateTimeZone($default_timezone)); return $dt; } // crmv@185576e function getRecurringType() { return $this->recur_type; } function getRecurringFrequency() { return $this->recur_freq; } // crmv@185576 function getRecurringEndDate() { return $this->enddate; } function getRecurringCount() { return $this->rpt_count; } // crmv@185576e function getDBRecurringInfoString() { $recurringType = $this->getRecurringType(); $recurringInfo = ''; if ($recurringType == 'Daily' || $recurringType == 'Yearly') { $recurringInfo = $recurringType; } elseif ($recurringType == 'Weekly') { if ($this->dayofweek_to_rpt != null) { $recurringInfo = $recurringType . '::' . implode('::', $this->dayofweek_to_rpt); } else { $recurringInfo = $recurringType; } } elseif ($recurringType == 'Monthly') { $recurringInfo = $recurringType . '::' . $this->repeat_monthby; if ($this->repeat_monthby == 'date') { $recurringInfo = $recurringInfo . '::' . $this->rptmonth_datevalue; } else { $recurringInfo = $recurringInfo . '::' . $this->rptmonth_daytype . '::' . $this->dayofweek_to_rpt[0]; } } return $recurringInfo; } function getUserRecurringInfo() { $recurringType = $this->getRecurringType(); $recurringInfo = array(); if ($recurringType == 'Weekly') { if ($this->dayofweek_to_rpt != null) { $dbStartDateTime = new DateTime($this->startdate->get_DB_formatted_date() . ' ' . $this->startdate->get_formatted_time()); $dayOfWeek = $this->dayofweek_to_rpt; $userDaysOfWeek = array(); for ($i = 0; $i < count($dayOfWeek); ++$i) { $selectedDayOfWeek = $dayOfWeek[$i]; $currentDayOfWeek = $dbStartDateTime->format('w'); $newDate = $dbStartDateTime->format('d') + ($selectedDayOfWeek - $currentDayOfWeek); $dbStartDateTime->setDate($dbStartDateTime->format('Y'), $dbStartDateTime->format('m'), $newDate); $userStartDateTime = DateTimeField::convertToUserTimeZone($dbStartDateTime->format('Y-m-d') . ' ' . $dbStartDateTime->format('H:i')); $userDaysOfWeek[] = $userStartDateTime->format('w'); } $recurringInfo['dayofweek_to_repeat'] = $userDaysOfWeek; } } elseif ($recurringType == 'Monthly') { $dbStartDateTime = new DateTime($this->startdate->get_DB_formatted_date() . ' ' . $this->startdate->get_formatted_time()); $recurringInfo['repeatmonth_type'] = $this->repeat_monthby; if ($this->repeat_monthby == 'date') { $dayOfMonth = $this->rptmonth_datevalue; $dbStartDateTime->setDate($dbStartDateTime->format('Y'), $dbStartDateTime->format('m'), $dayOfMonth); $userStartDateTime = DateTimeField::convertToUserTimeZone($dbStartDateTime->format('Y-m-d') . ' ' . $dbStartDateTime->format('H:i')); $recurringInfo['repeatmonth_date'] = $userStartDateTime->format('d'); } else { $dayOfWeek = $this->dayofweek_to_rpt[0]; $recurringInfo['repeatmonth_daytype'] = $this->rptmonth_daytype; $recurringInfo['dayofweek_to_repeat'][0] = $dayOfWeek; // crmv@185501 // crmv@185501 - removed useless code } } return $recurringInfo; } // crmv@185576 function getDisplayRecurringInfo() { $displayRecurringData = array(); $recurringInfo = $this->getUserRecurringInfo(); $displayRecurringData['recurringcheck'] = getTranslatedString('LBL_YES', 'Calendar'); $displayRecurringData['repeat_frequency'] = $this->getRecurringFrequency(); $displayRecurringData['recurringtype'] = $this->getRecurringType(); $edate = $this->getRecurringEndDate(); if ($edate) { $displayRecurringData['end_date'] = $edate->get_DB_formatted_date(); $displayRecurringData['end_date_user'] = getDisplayDate($displayRecurringData['end_date']); } $displayRecurringData['count'] = $this->getRecurringCount(); if ($this->getRecurringType() == 'Weekly') { $noOfDays = count($recurringInfo['dayofweek_to_repeat']); $translatedRepeatDays = array(); for ($i = 0; $i < $noOfDays; ++$i) { $translatedRepeatDays[] = getTranslatedString('LBL_DAY' . $recurringInfo['dayofweek_to_repeat'][$i], 'Calendar'); } $displayRecurringData['repeat_str'] = implode(',', $translatedRepeatDays); } elseif ($this->getRecurringType() == 'Monthly') { $translatedRepeatDays = array(); $displayRecurringData['repeatMonth'] = $recurringInfo['repeatmonth_type']; if ($recurringInfo['repeatmonth_type'] == 'date') { $displayRecurringData['repeatMonth_date'] = $recurringInfo['repeatmonth_date']; $displayRecurringData['repeat_str'] = getTranslatedString('on', 'Calendar') . ' ' . $recurringInfo['repeatmonth_date'] . ' ' . getTranslatedString('day of the month', 'Calendar'); } else { $displayRecurringData['repeatMonth_daytype'] = $recurringInfo['repeatmonth_daytype']; $displayRecurringData['repeatMonth_day'] = $recurringInfo['dayofweek_to_repeat'][0]; $translatedRepeatDay = getTranslatedString('LBL_DAY' . $recurringInfo['dayofweek_to_repeat'][0], 'Calendar'); $displayRecurringData['repeat_str'] = getTranslatedString('on', 'Calendar') . ' ' . getTranslatedString($recurringInfo['repeatmonth_daytype'], 'Calendar') . ' ' . $translatedRepeatDay; } } if ($displayRecurringData['count']) { $displayRecurringData['repeat_str'] .= " ".str_replace("%s", $displayRecurringData['count'], strtolower(getTranslatedString('LBL_FORXTIMES', 'Calendar'))); } elseif ($displayRecurringData['end_date']) { $displayRecurringData['repeat_str'] .= " ".strtolower(getTranslatedString('LBL_UNTIL', 'Calendar'))." ".$displayRecurringData['end_date_user']; } $displayRecurringData['full_text'] = getTranslatedString('LBL_REPEATEVENT', 'Calendar') ." ".$displayRecurringData['repeat_frequency'].' '.strtolower(getTranslatedString($displayRecurringData['recurringtype'], 'Calendar')) ." ".$displayRecurringData['repeat_str']; return $displayRecurringData; } // crmv@185576e /** * Function to get recurring dates depending on the recurring type * return array $recurringDates - Recurring Dates in format * Recurring date will be returned in DB Time Zone, as well as DB format */ function _getRecurringDates() { global $app_strings; $startdateObj = $this->startdate; $startdate = $startdateObj->get_DB_formatted_date(); $recurringDates[] = $startdate; $tempdateObj = $startdateObj; $tempdate = $startdate; $enddate = $this->enddate->get_DB_formatted_date(); $count = $this->rpt_count; // crmv@185576 $keepGoing = ($count > 0); // crmv@185576 while ($keepGoing || $tempdate <= $enddate) { // crmv@185576 $date = $tempdateObj->get_Date(); $month = $tempdateObj->getMonth(); $year = $tempdateObj->getYear(); if ($this->recur_type == 'Daily') { if (isset($this->recur_freq)) { $index = $date + $this->recur_freq - 1; } else { $index = $date; } $tempdateObj = $this->startdate->getThismonthDaysbyIndex($index, '', $month, $year); $tempdate = $tempdateObj->get_DB_formatted_date(); if($keepGoing || $tempdate <= $enddate) { // crmv@185576 $recurringDates[] = $tempdate; } } elseif ($this->recur_type == 'Weekly') { if (count($this->dayofweek_to_rpt) == 0) { $this->dayofweek_to_rpt[] = $this->startdate->dayofweek; } for ($i = 0; $i < count($this->dayofweek_to_rpt); $i++) { $repeatDay = $tempdateObj->getThisweekDaysbyIndex($this->dayofweek_to_rpt[$i]); $repeatDate = $repeatDay->get_DB_formatted_date(); if ($repeatDate > $startdate && ($keepGoing || $repeatDate <= $enddate)) { // crmv@185576 $recurringDates[] = $repeatDate; } } if (isset($this->recur_freq)) { $index = $this->recur_freq * 7; } else { $index = 7; } $date_arr = Array( 'day' => $date + $index, 'month' => $month, 'year' => $year ); $tempdateObj = new vt_DateTime($date_arr, true); $tempdate = $tempdateObj->get_DB_formatted_date(); } elseif ($this->recur_type == 'Monthly') { if ($this->repeat_monthby == 'date') { if ($this->rptmonth_datevalue <= $date) { $index = $this->rptmonth_datevalue - 1; $day = $this->rptmonth_datevalue; if (isset($this->recur_freq)) $month = $month + $this->recur_freq; else $month = $month + 1; $tempdateObj = $tempdateObj->getThismonthDaysbyIndex($index, $day, $month, $year); } else { $index = $this->rptmonth_datevalue - 1; $day = $this->rptmonth_datevalue; $tempdateObj = $tempdateObj->getThismonthDaysbyIndex($index, $day, $month, $year); } } elseif ($this->repeat_monthby == 'day') { // crmv@185501 $ns = array('first' => 1, 'second' => 2, 'third' => 3, 'fourth' => 4); if (array_key_exists($this->rptmonth_daytype, $ns)) { $n = $ns[$this->rptmonth_daytype]; $date_arr = Array( 'day' => 1, 'month' => $month, 'year' => $year ); $tempdateObj = new vt_DateTime($date_arr, true); $firstdayofmonthObj = $this->getNthdayofmonthObj($this->dayofweek_to_rpt[0], $n, $month, $year); if ($firstdayofmonthObj->get_DB_formatted_date() <= $tempdate) { if (isset($this->recur_freq)) $month = $firstdayofmonthObj->getMonth() + $this->recur_freq; else $month = $firstdayofmonthObj->getMonth() + 1; $tempdateObj = $this->getNthdayofmonthObj($this->dayofweek_to_rpt[0], $n, $month, $firstdayofmonthObj->getYear()); } else { $tempdateObj = $firstdayofmonthObj; } // crmv@185501e } elseif ($this->rptmonth_daytype == 'last') { $date_arr = Array( 'day' => $tempdateObj->getDaysInMonth(), 'month' => $tempdateObj->getMonth(), 'year' => $tempdateObj->getYear() ); $tempdateObj = new vt_DateTime($date_arr, true); $lastdayofmonthObj = $this->getLastdayofmonth($this->dayofweek_to_rpt[0], $tempdateObj); if ($lastdayofmonthObj->get_DB_formatted_date() <= $tempdate) { if (isset($this->recur_freq)) $month = $lastdayofmonthObj->getMonth() + $this->recur_freq; else $month = $lastdayofmonthObj->getMonth() + 1; $dateObj = $lastdayofmonthObj->getThismonthDaysbyIndex(0, 1, $month, $lastdayofmonthObj->getYear()); $dateObj = $dateObj->getThismonthDaysbyIndex($dateObj->getDaysInMonth() - 1, $dateObj->getDaysInMonth(), $month, $lastdayofmonthObj->getYear()); $nextmonthObj = $this->getLastdayofmonth($this->dayofweek_to_rpt[0], $dateObj); $tempdateObj = $nextmonthObj; } else { $tempdateObj = $lastdayofmonthObj; } } } else { $date_arr = Array( 'day' => $date, 'month' => $month + 1, 'year' => $year ); $tempdateObj = new vt_DateTime($date_arr, true); } $tempdate = $tempdateObj->get_DB_formatted_date(); if ($keepGoing || $tempdate <= $enddate) { // crmv@185576 $recurringDates[] = $tempdate; } } elseif ($this->recur_type == 'Yearly') { if (isset($this->recur_freq)) $index = $year + $this->recur_freq; else $index = $year + 1; if ($index > 2037 || $index < 1970) { print("" . $app_strings['LBL_CAL_LIMIT_MSG'] . ""); exit; } $date_arr = Array( 'day' => $date, 'month' => $month, 'year' => $index ); $tempdateObj = new vt_DateTime($date_arr, true); $tempdate = $tempdateObj->get_DB_formatted_date(); if ($keepGoing || $tempdate <= $enddate) { // crmv@185576 $recurringDates[] = $tempdate; } } else { die("Recurring Type " . $this->recur_type . " is not defined"); } // crmv@185576 if ($keepGoing) { $keepGoing = count($recurringDates) <= $count; } // crmv@185576e } return $recurringDates; } /** Function to get first day of the month(like first Monday or Friday and etc.) * @param $dayofweek -- day of the week to repeat the event :: Type string * @param $dateObj -- date object :: Type vt_DateTime Object * return $dateObj -- the date object on which the event repeats :: Type vt_DateTime Object */ function getFistdayofmonth($dayofweek, & $dateObj) { if ($dayofweek < $dateObj->dayofweek) { $index = (7 - $dateObj->dayofweek) + $dayofweek; $day = 1 + $index; $month = $dateObj->month; $year = $dateObj->year; $dateObj = $dateObj->getThismonthDaysbyIndex($index, $day, $month, $year); } else { $index = $dayofweek - $dateObj->dayofweek; $day = 1 + $index; $month = $dateObj->month; $year = $dateObj->year; $dateObj = $dateObj->getThismonthDaysbyIndex($index, $day, $month, $year); } return $dateObj; } /** Function to get last day of the month(like last Monday or Friday and etc.) * @param $dayofweek -- day of the week to repeat the event :: Type string * @param $dateObj -- date object :: Type vt_DateTime Object * return $dateObj -- the date object on which the event repeats :: Type vt_DateTime Object */ function getLastdayofmonth($dayofweek, & $dateObj) { if ($dayofweek == $dateObj->dayofweek) { return $dateObj; } else { if ($dayofweek > $dateObj->dayofweek) $day = $dateObj->day - 7 + ($dayofweek - $dateObj->dayofweek); else $day = $dateObj->day - ($dateObj->dayofweek - $dayofweek); $index = $day - 1; $month = $dateObj->month; $year = $dateObj->year; $dateObj = $dateObj->getThismonthDaysbyIndex($index, $day, $month, $year); return $dateObj; } } // crmv@185501 /** * Get the nth day of week of the specified month * @param int $dayofweek The day of the week (1-7) * @param int $n The nth day (1-4) * @param int $month The month (1-12) * @param int $year The year * * @return string/null The date or null if the day doesn't exist (ex: 5th day) */ function getNthdayofmonth($dayofweek, $n, $month, $year) { $month = sprintf("%02d", intval($month)); $next ="$year-$month-".sprintf("%02d", (8 - intval($dayofweek))).' 12:00:00'; // limit tz/dst problems $nextTs = strtotime($next); $nextDow = date('N', $nextTs); $nthDay = $n * 7 - $nextDow + 1; // check if out of range if ($nthDay > 28) { $totDays = date('t', $nextTs); if ($nthDay > $totDays) return null; } return "$year-$month-".sprintf("%02d", intval($nthDay)); } /** * Same as getNthdayofmonth, but return a vt_DateTime object instead */ function getNthdayofmonthObj($dayofweek, $n, $month, $year) { $date = $this->getNthdayofmonth($this->dayofweek_to_rpt[0], $n, $month, $year); list($y, $m, $day) = explode('-', $date); $date_arr = Array( 'day' => $day, 'month' => $month, 'year' => $year ); $tempdateObj = new vt_DateTime($date_arr, true); return $tempdateObj; } // crmv@185501e }