public function findOpening($startDay = 0, $endDay = 7, $fileRate = 1, $all = false, $startTime = null)
    {
        $openings = [];
        for ($day = $startDay; $day <= $endDay; ++$day) {
            if (0 === $day) {
                // Current day.
                if ($startTime && $startTime instanceof \DateTime) {
                    $date = clone $startTime;
                } else {
                    $date = new \DateTime();
                }
            } else {
                // Future days.
                // Use noon to begin to evaluate days in the future to avoiding timezones during day checks.
                $date = new \DateTime('noon +'.$day.' day');
            }
            try {
                // Initialize the range (will expand as appropriate later).
                $start = clone $date;
                $end   = clone $date;

                // Evaluate that the client is open this day of the week.
                $hours = $this->evaluateDay(true, $date);

                // Evaluate that the client isn't closed by an excluded date rule.
                $this->evaluateExclusions($date);

                // Push the end time to the correct time for this client's hours.
                $timeTill = !empty($hours->timeTill) ? $hours->timeTill : '23:59';
                $end->setTimezone($this->timezone);
                $end->modify($timeTill.':59');

                // Current day: Evaluate that there is still time today to send.
                if (0 == $day && $this->now > $end) {
                    // Continue to the next day.
                    continue;
                }

                // Pull the start time to the correct time for this day and schedule.
                $timeFrom = !empty($hours->timeFrom) ? $hours->timeFrom : '00:00';
                $start->setTimezone($this->timezone);
                $start->modify($timeFrom);

                if ('file' === $this->contactClient->getType()) {
                    // Evaluate if we have exceeded allowed file count for this day.
                    $fileCount = $this->evaluateFileRate($fileRate, $date);

                    // If we have already built a file in this day and can send more...
                    if ($fileCount > 0 && $fileRate > 1) {
                        // Push the start time to the next available slot in this day.
                        $daySeconds = $end->format('U') - $start->format('U');
                        if ('00:00' === $timeFrom && '23:59' === $timeTill) {
                            // Avoid sending 2 files at midnight.
                            $segmentSeconds = intval($daySeconds / $fileRate);
                        } else {
                            // Send at opening and closing times, spreading the rest of the day evenly.
                            $segmentSeconds = intval($daySeconds / ($fileRate - 1));
                        }
                        // Push start time to the next segment.
                        $start->modify('+'.($segmentSeconds * $fileCount).' seconds');
                    }
                }

                // Start time should not be in the past.
                if (0 === $day && $start < $this->now) {
                    // Must be done after rate has been applied.
                    $start = $this->now;
                }

                $openings[] = [$start, $end];
                if (!$all) {
                    break;
                }
            } catch (\Exception $e) {
                if ($e instanceof ContactClientException) {
                    // Expected.
                } else {
                    throw $e;
                }
            }
        }

        return $openings;
    }
public function findOpening($startDay = 0, $endDay = 7, $fileRate = 1, $all = false, $startTime = null)
    {
        $openings = [];
        for ($day = $startDay; $day <= $endDay; ++$day) {
            if (0 === $day) {
                // Current day.
                if ($startTime && $startTime instanceof \DateTime) {
                    $date = clone $startTime;
                } else {
                    $date = new \DateTime();
                }
            } else {
                // Future days.
                // Use noon to begin to evaluate days in the future to avoiding timezones during day checks.
                $date = new \DateTime('noon +'.$day.' day');
            }
            try {
                // Initialize the range (will expand as appropriate later).
                $start = clone $date;
                $end   = clone $date;

                // Evaluate that the client is open this day of the week.
                $hours = $this->evaluateDay(true, $date);

                // Evaluate that the client isn't closed by an excluded date rule.
                $this->evaluateExclusions($date);

                // Push the end time to the correct time for this client's hours.
                $timeTill = !empty($hours->timeTill) ? $hours->timeTill : '23:59';
                $end->setTimezone($this->timezone);
                $end->modify($timeTill.':59');

                // Current day: Evaluate that there is still time today to send.
                if (0 == $day && $this->now > $end) {
                    // Continue to the next day.
                    continue;
                }

                // Pull the start time to the correct time for this day and schedule.
                $timeFrom = !empty($hours->timeFrom) ? $hours->timeFrom : '00:00';
                $start->setTimezone($this->timezone);
                $start->modify($timeFrom);

                if ('file' === $this->contactClient->getType()) {
                    // Evaluate if we have exceeded allowed file count for this day.
                    $fileCount = $this->evaluateFileRate($fileRate, $date);

                    // If we have already built a file in this day and can send more...
                    if ($fileCount > 0 && $fileRate > 1) {
                        // Push the start time to the next available slot in this day.
                        $daySeconds = $end->format('U') - $start->format('U');
                        if ('00:00' === $timeFrom && '23:59' === $timeTill) {
                            // Avoid sending 2 files at midnight.
                            $segmentSeconds = intval($daySeconds / $fileRate);
                        } else {
                            // Send at opening and closing times, spreading the rest of the day evenly.
                            $segmentSeconds = intval($daySeconds / ($fileRate - 1));
                        }
                        // Push start time to the next segment.
                        $start->modify('+'.($segmentSeconds * $fileCount).' seconds');
                    }
                }

                // Start time should not be in the past.
                if (0 === $day && $start < $this->now) {
                    // Must be done after rate has been applied.
                    $start = $this->now;
                }

                $openings[] = [$start, $end];
                if (!$all) {
                    break;
                }
            } catch (\Exception $e) {
                if ($e instanceof ContactClientException) {
                    // Expected.
                } else {
                    throw $e;
                }
            }
        }

        return $openings;
    }
public function findOpening ( $ startDay = 0 , $ endDay = 7 , $ fileRate = 1 , $ all = false , $ startTime = null ) { $ openings = [ ] ; for ( $ day = $ startDay ; $ day <= $ endDay ; ++ $ day ) { if ( 0 === $ day ) { // Current day. if ( $ startTime && $ startTime instanceof \ DateTime ) { $ date = clone $ startTime ; } else { $ date = new \ DateTime ( ) ; } } else { // Future days. // Use noon to begin to evaluate days in the future to avoiding timezones during day checks. $ date = new \ DateTime ( 'noon +' . $ day . ' day' ) ; } try { // Initialize the range (will expand as appropriate later). $ start = clone $ date ; $ end = clone $ date ; // Evaluate that the client is open this day of the week. $ hours = $ this -> evaluateDay ( true , $ date ) ; // Evaluate that the client isn't closed by an excluded date rule. $ this -> evaluateExclusions ( $ date ) ; // Push the end time to the correct time for this client's hours. $ timeTill = ! empty ( $ hours -> timeTill ) ? $ hours -> timeTill : '23:59' ; $ end -> setTimezone ( $ this -> timezone ) ; $ end -> modify ( $ timeTill . ':59' ) ; // Current day: Evaluate that there is still time today to send. if ( 0 == $ day && $ this -> now > $ end ) { // Continue to the next day. continue ; } // Pull the start time to the correct time for this day and schedule. $ timeFrom = ! empty ( $ hours -> timeFrom ) ? $ hours -> timeFrom : '00:00' ; $ start -> setTimezone ( $ this -> timezone ) ; $ start -> modify ( $ timeFrom ) ; if ( 'file' === $ this -> contactClient -> getType ( ) ) { // Evaluate if we have exceeded allowed file count for this day. $ fileCount = $ this -> evaluateFileRate ( $ fileRate , $ date ) ; // If we have already built a file in this day and can send more... if ( $ fileCount > 0 && $ fileRate > 1 ) { // Push the start time to the next available slot in this day. $ daySeconds = $ end -> format ( 'U' ) - $ start -> format ( 'U' ) ; if ( '00:00' === $ timeFrom && '23:59' === $ timeTill ) { // Avoid sending 2 files at midnight. $ segmentSeconds = intval ( $ daySeconds / $ fileRate ) ; } else { // Send at opening and closing times, spreading the rest of the day evenly. $ segmentSeconds = intval ( $ daySeconds / ( $ fileRate - 1 ) ) ; } // Push start time to the next segment. $ start -> modify ( '+' . ( $ segmentSeconds * $ fileCount ) . ' seconds' ) ; } } // Start time should not be in the past. if ( 0 === $ day && $ start < $ this -> now ) { // Must be done after rate has been applied. $ start = $ this -> now ; } $ openings [ ] = [ $ start , $ end ] ; if ( ! $ all ) { break ; } } catch ( \ Exception $ e ) { if ( $ e instanceof ContactClientException ) { // Expected. } else { throw $ e ; } } } return $ openings ; }
Given the hours of operation, timezone and excluded dates of the client...
Find the next appropriate time to send them contacts.

@param int    $startDay  Set to 1 to start the seek tomorrow, 0 for today
@param int    $endDay    Maximum number of days forward to seek for an opening
@param int    $fileRate  Maximum number of files to build per day (applies to file seek only)
@param bool   $all       Indicates we want all the openings in the range of days
@param string $startTime Optional start time to begin forward search on the current day as a string

@return array

@throws \Exception
Given the hours of operation timezone and excluded dates of the client ... Find the next appropriate time to send them contacts .