_fields_values = self::_parseRecurringDataString($recurringType); $this->_recurring_start_date_stamp = $recurringStartDateStamp; $this->_recurring_end_date_stamp = $recurringEndDateStamp; } public static function getInstance($recurringTypeString, $recurringStartDateStamp, $recurringEndDateStamp) { return new self($recurringTypeString, $recurringStartDateStamp, $recurringEndDateStamp); } /** * Get field value. * @param $fieldName * @return mixed * @throws Exception */ private function _getFieldValue($fieldName) { if(!isset($this->_fields_values[$fieldName])) throw new Exception("Field '{$fieldName}' not found."); return $this->_fields_values[$fieldName]; } /** * Parse recurring data from array to string. * @param $dataArray * @return string * @throws Exception */ static function parseRecurringDataArrayToString($dataArray) { $dataFields = array( self::FLD_REC_TYPE => "each", self::FLD_REC_TYPE_STEP => "step", self::FLD_WEEK_NUMBER => "week_number", self::FLD_WEEK_DAYS_LIST => "days_of_week", self::FLD_REPEAT => "repeat" ); $recurringTypes = array(self::REC_TYPE_DAY, self::REC_TYPE_WEEK, self::REC_TYPE_MONTH, self::REC_TYPE_YEAR); $daysOfWeek = array("sunday" => 0, "monday" => 1, "tuesday" => 2, "wednesday" => 3, "thursday" => 4, "friday" => 5, "saturday" => 6); $dataFieldsValues = array(); foreach($dataArray as $field => $value) { switch($field) { case $dataFields[self::FLD_REC_TYPE_STEP]: case $dataFields[self::FLD_REPEAT]: $value = str_replace(" ", "", $value); $dataFieldsValues[$field] = $value; break; case $dataFields[self::FLD_REC_TYPE]: $value = str_replace(" ", "", $value); if(!in_array($value, $recurringTypes)) throw new Exception("Field '{$field}' will contains value of ".join(", ", $recurringTypes)); $dataFieldsValues[$field] = $value; break; case $dataFields[self::FLD_WEEK_NUMBER]: $value = str_replace(" ", "", $value); if(count(explode(",", $dataArray[$dataFields[self::FLD_WEEK_DAYS_LIST]])) > 1) throw new Exception("If field {$field} not null, then field ".$dataFields[self::FLD_WEEK_DAYS_LIST]." will contains only one value of ".join(", ", array_keys($daysOfWeek))); if(!in_array($value, $daysOfWeek)) throw new Exception("Field {$field} will contains value of ".join(",", array_keys($daysOfWeek))); $dataFieldsValues[$field] = $value; break; case $dataFields[self::FLD_WEEK_DAYS_LIST]: $weekDaysToRecurring = explode(",", $value); $days = array(); foreach($weekDaysToRecurring as $day) { $day = str_replace(" ", "", $day); if(!in_array($day, $daysOfWeek)) throw new Exception("Field {$field} will contains data like 'monday,tuesday,wednesday'."); array_push($days, $daysOfWeek[$day]); } $dataFieldsValues[$field] = join("," ,$days); break; default: $dataFieldsValues[$field] = ""; break; } } //Check required data and fill gaps of data. $requiredFields = array( self::FLD_REC_TYPE, self::FLD_REC_TYPE_STEP ); foreach($dataFields as $fieldKey => $fieldName) { if(isset($dataFieldsValues[$fieldName])) continue; if(in_array($fieldKey, $requiredFields)) throw new Exception("Field '{$fieldName}' is required"); $dataFieldsValues[$fieldName] = ""; } $recurringFormat = "%s_%s_%s_%s_%s#%s"; return sprintf( $recurringFormat, $dataFieldsValues[$dataFields[self::FLD_REC_TYPE]], $dataFieldsValues[$dataFields[self::FLD_REC_TYPE_STEP]], (!empty($dataFieldsValues[$dataFields[self::FLD_WEEK_NUMBER]])) ? $dataFieldsValues[$dataFields[self::FLD_WEEK_DAYS_LIST]] : "", $dataFieldsValues[$dataFields[self::FLD_WEEK_NUMBER]], (empty($dataFieldsValues[$dataFields[self::FLD_WEEK_NUMBER]])) ? $dataFieldsValues[$dataFields[self::FLD_WEEK_DAYS_LIST]] : "", $dataFieldsValues[$dataFields[self::FLD_REPEAT]] ); } /** * Parse recurring data from string. * @param $dataStr * @return array */ static private function _parseRecurringDataString($dataStr) { $formatPartsReg = "/(_|#)/"; $formatDaysListPartReg = "/,/"; $parsedData = array(); list( $parsedData[self::FLD_REC_TYPE], $parsedData[self::FLD_REC_TYPE_STEP], $parsedData[self::FLD_WEEK_DAY], $parsedData[self::FLD_WEEK_NUMBER], $parsedData[self::FLD_WEEK_DAYS_LIST], $parsedData[self::FLD_REPEAT] ) = preg_split($formatPartsReg, $dataStr); $parsedData[self::FLD_WEEK_DAYS_LIST] = ($parsedData[self::FLD_WEEK_DAYS_LIST]) ? preg_split($formatDaysListPartReg, $parsedData[self::FLD_WEEK_DAYS_LIST]) : array(); return $parsedData; } public function getRecurringTypeValue() { return $this->_getFieldValue(self::FLD_REC_TYPE); } public function getRecurringTypeStepValue() { return $this->_getFieldValue(self::FLD_REC_TYPE_STEP); } public function getWeekNumberValue() { return $this->_getFieldValue(self::FLD_WEEK_NUMBER); } public function getWeekDayValue() { return $this->_getFieldValue(self::FLD_WEEK_DAY); } public function getWeekDaysListValue() { return $this->_getFieldValue(self::FLD_WEEK_DAYS_LIST); } public function getRepeatValue() { return $this->_getFieldValue(self::FLD_REPEAT); } /** * Correcting interval by recurring start($this->_recurring_start_date_stamp) * and end($this->_recurring_end_date_stamp) dates. * @param $intervalStartDateStamp * @param $intervalEndDateStamp * @return array */ private function _getCorrectedRecurringInterval($intervalStartDateStamp, $intervalEndDateStamp) { $recurringStartDateStamp = $this->_recurring_start_date_stamp; $recurringEndDateStamp = $this->_recurring_end_date_stamp; $recurringInterval = array( "start_date_stamp" => $intervalStartDateStamp, "end_date_stamp" => $intervalEndDateStamp ); //Return recurring interval without correcting if it not belongs to assigned interval. if(($intervalStartDateStamp >= $recurringEndDateStamp) || ($intervalEndDateStamp <= $recurringStartDateStamp)) return $recurringInterval; //Correct start date interval if it smaller then recurring start date. if($intervalStartDateStamp < $recurringStartDateStamp) $intervalStartDateStamp = $recurringStartDateStamp; //Correct end date interval if it smaller then recurring end date. if($intervalEndDateStamp > $recurringEndDateStamp) $intervalEndDateStamp = $recurringEndDateStamp; $differenceStartDates = SchedulerHelperDate::differenceBetweenDates($intervalStartDateStamp, $recurringStartDateStamp); $differenceEndDates = SchedulerHelperDate::differenceBetweenDates($intervalEndDateStamp, $recurringEndDateStamp); $dateUnits = SchedulerHelperDate::$DATE_UNITS; //Add years. $recurringInterval["start_date_stamp"] = SchedulerHelperDate::addYears($recurringStartDateStamp, $differenceStartDates[$dateUnits["year"]]); $recurringInterval["end_date_stamp"] = SchedulerHelperDate::addYears($recurringEndDateStamp, -$differenceEndDates[$dateUnits["year"]]); //If recurring type is "year" then exit, else add months. if($this->getRecurringTypeValue() == self::REC_TYPE_YEAR) return $recurringInterval; //Add months. $recurringInterval["start_date_stamp"] = SchedulerHelperDate::addMonths($recurringInterval["start_date_stamp"], $differenceStartDates[$dateUnits["month"]]); $recurringInterval["end_date_stamp"] = SchedulerHelperDate::addMonths($recurringInterval["end_date_stamp"], -$differenceEndDates[$dateUnits["month"]]); return $recurringInterval; } /** * Get step to recurring day from current day of week in date. * @param $dateStamp * @param $recurringWeekDay * @return int */ private function _getRecurringDayStep($dateStamp, $recurringWeekDay) { $weekDay = SchedulerHelperDate::getDayOfWeek($dateStamp); $dayStep = $recurringWeekDay - $weekDay; $dayStep = ($dayStep < 0) ? (SchedulerHelperDate::DAYS_IN_WEEK - (-$dayStep)) : $dayStep; return $dayStep; } /** * Get recurring days for date. * @param $dateStamp * @return array */ private function _getRecurringDays($dateStamp) { $recurringDays = array(); //If recurring type has list of days, then get those days. $recurringWeekDays = $this->getWeekDaysListValue(); if($recurringWeekDays) { for($i = 0; $i < count($recurringWeekDays); $i++) { $dayStep = $this->_getRecurringDayStep($dateStamp, $recurringWeekDays[$i]); array_push($recurringDays, SchedulerHelperDate::addDays($dateStamp, $dayStep)); } } //Else if recurring type has day of week and step for it, then get this day. elseif($this->getWeekDayValue() && $this->getWeekNumberValue()) { $dayStep = $this->_getRecurringDayStep($dateStamp, $this->getWeekDayValue()); $dayStep += (SchedulerHelperDate::DAYS_IN_WEEK * ($this->getWeekNumberValue() - 1)); array_push($recurringDays, SchedulerHelperDate::addDays($dateStamp, $dayStep)); } //Else return recurring date without change. else array_push($recurringDays, $dateStamp); return $recurringDays; } /** * Get recurring dates by interval or $intervalStartDateStamp and $countDates. * @param $intervalStartDateStamp * @param $intervalEndDateStamp * @param null $countDates * @return array|bool */ public function getRecurringDates($intervalStartDateStamp, $intervalEndDateStamp, $countDates = NULL) { if(!($this->getRecurringTypeStepValue() && $this->getRecurringTypeValue())) return false; //Correct interval by recurring interval. $correctedInterval = $this->_getCorrectedRecurringInterval($intervalStartDateStamp, $intervalEndDateStamp); $intervalStartDateStamp = $correctedInterval["start_date_stamp"]; $intervalEndDateStamp = $correctedInterval["end_date_stamp"]; $currentRecurringStartDateStamp = $intervalStartDateStamp; $recurringDates = array(); //Generate dates wile next recurring date belongs to interval. $countRecurringCycles = 0; while( (!is_null($countDates) && ($countRecurringCycles <= $countDates)) || ( ($intervalStartDateStamp <= $currentRecurringStartDateStamp) && ($currentRecurringStartDateStamp <= $intervalEndDateStamp) ) ) { $countRecurringCycles++; $recurringDays = $this->_getRecurringDays($currentRecurringStartDateStamp); $recurringDates = array_merge($recurringDates, $recurringDays); $recurringTypeStep = $this->getRecurringTypeStepValue(); switch($this->getRecurringTypeValue()) { case self::REC_TYPE_DAY: $currentRecurringStartDateStamp = SchedulerHelperDate::addDays($currentRecurringStartDateStamp, $recurringTypeStep); break; case self::REC_TYPE_WEEK: $currentRecurringStartDateStamp = SchedulerHelperDate::addWeeks($currentRecurringStartDateStamp, $recurringTypeStep); break; case self::REC_TYPE_MONTH: $currentRecurringStartDateStamp = SchedulerHelperDate::addMonths($currentRecurringStartDateStamp, $recurringTypeStep); break; case self::REC_TYPE_YEAR: $currentRecurringStartDateStamp = SchedulerHelperDate::addYears($currentRecurringStartDateStamp, $recurringTypeStep); break; } } return (!is_null($countDates)) ? array_splice($recurringDates, (count($recurringDates) - $countDates)) : $recurringDates; } /** * @param $recurringType * @param $startDateStamp * @param $eventLength * @return int|NULL */ public static function getRecurringEndDate($recurringType, $startDateStamp, $eventLength) { $recurringTypeObj = self::getInstance($recurringType, $startDateStamp, NULL); $repeatValue = $recurringTypeObj->getRepeatValue(); if(empty($repeatValue)) return ($startDateStamp + $eventLength); $recurringStartDatesStamps = $recurringTypeObj->getRecurringDates($startDateStamp, NULL, $repeatValue); $maxEndDateStamp = NULL; foreach($recurringStartDatesStamps as $startDateStamp) { $endDateStamp = $startDateStamp + $eventLength; $maxEndDateStamp = ($endDateStamp > $maxEndDateStamp) ? $endDateStamp : $maxEndDateStamp; } return $maxEndDateStamp; } }