문제

I have a CakePHP application that is being used as a center portal for our company's general day to day activities. These consist of various booking systems and information such as employees and their departments. The system runs of CakePHP 2.2

What I am currently implementing is a system where a person doing a tender, can apply for a contract to be drawn up. This contract has various changes that would depend on how long before the deadline (of the tender) the draft contract should be handed over to the legal person, to draw up the formal contract. The system then 'books' the legal person for the few days that this contract is set up. No other bookings can be made on those days.

The system works perfectly, but it does not split a contract over a weekend. It moves all the days to before the weekend. Let's say for arguments sake, I book a draft contract in for completion on the 18th of June. The system determines that the total contract time would be 3 days. That means that the contract should fall over a weekend and it should block out 13, 14 and 17 June (not the 18th, as I need to have the finished doc by then). HOWEVER, the system blocks 12 - 14 June.

Sorry for the long read, but I'd rather have you know the full story, than just bits and pieces. Here is my code for the system:

$tenderDocument = $this->TenderDocument->read(null,$id);
$deadline = $tenderDocument['TenderDocument']['required_date'];
$deadline = $start = date('Y-m-d',strtotime('-1 day',strtotime($deadline)));
$start = date('Y-m-d',strtotime('-' . $tenderDocument['ContractForm']['time'],strtotime($deadline)));
$dates[] = $current = $start;
while($current < $deadline){  
    $current = date('Y-m-d',strtotime('+1 day',strtotime($current)));
    $dates[] = $current;  
}
$is_available = array();
$is_available = $this->TenderDocument->find('first',
    array(
        'conditions' => array(
            '(TenderDocument.start_date >= "' . $start . '" AND 
            TenderDocument.start_date < "' . $deadline . '" OR
            TenderDocument.start_date <= "' . $deadline . '" AND
            TenderDocument.required_date > "' . $deadline . '") AND
            TenderDocument.id != ' . $id
        )
    )
);
$is_weekend = false;
foreach($dates as $date) {
    if(date('l',strtotime($date)) == 'Sunday' || date('l',strtotime($date)) == 'Saturday') {
        $is_weekend = true;
    }
}
while(!empty($is_available) || $is_weekend) {
    $is_weekend = false;
    $dates = array();
    $deadline = date('Y-m-d',strtotime('-1 day',strtotime($deadline)));
    $start = date('Y-m-d',strtotime('-1 day',strtotime($start)));
    $dates[] = $current = $start;
    while($current < $deadline){  
        $current = date('Y-m-d',strtotime('+1 day',strtotime($current)));
        $dates[] = $current;
    }
    foreach($dates as $date) {
        if(date('l',strtotime($date)) == 'Sunday' || date('l',strtotime($date)) == 'Saturday') {
            $is_weekend = true;
        }
    }
    $is_available = $this->TenderDocument->find('first',
        array(
            'conditions' => array(
                '(TenderDocument.start_date >= "' . $start . '" AND 
                TenderDocument.start_date < "' . $deadline . '" OR
                TenderDocument.start_date <= "' . $deadline . '" AND
                TenderDocument.required_date > "' . $deadline . '") AND
                TenderDocument.id != ' . $id
            )
        )
    );
}

On an unrelated note, is there any way I can simplify/minimize this code?

도움이 되었습니까?

해결책 2

I did it in a totally different way.

I received the required by date, created a date range of 2 months, and then removed all Saturdays and Sundays PLUS already booked days from the array. And then I reverse sort, and pick the first required amount of days.

function _createDateRangeArray($strDateFrom,$strDateTo) {
    $aryRange=array();

    $iDateFrom=mktime(1,0,0,substr($strDateFrom,5,2),     substr($strDateFrom,8,2),substr($strDateFrom,0,4));
    $iDateTo=mktime(1,0,0,substr($strDateTo,5,2),     substr($strDateTo,8,2),substr($strDateTo,0,4));

    if ($iDateTo>=$iDateFrom)
    {
        array_push($aryRange,date('Y-m-d',$iDateFrom)); // first entry
        while ($iDateFrom<$iDateTo)
        {
            $iDateFrom+=86400; // add 24 hours
            array_push($aryRange,date('Y-m-d',$iDateFrom));
        }
    }
    return $aryRange;
}

public function confirm($id = null) {
    if($this->request->is('post')) {
        $this->TenderDocument->create();
        $data = array(
            'TenderDocument' => array(
                'id' => $id,
                'book_dates' => implode(',',$this->request->data['TenderDocument']['book_days'])
            )
        );
        if($this->TenderDocument->save($data)) {
            $this->_sendTenderDocument($id);
            $this->Session->setFlash('Tender document request saved.','default',array('class'=>'notification'));
            $this->redirect(array('action'=>'add'));
        } else {
            $this->Session->setFlash('There was an error saving your deadline. Please retry.','default',array('class'=>'error'));
        }
    }
    $this->TenderDocument->id = $id;
    if (!$this->TenderDocument->exists()) {
        throw new NotFoundException(__('Invalid tender document'));
    }
    $tenderDocument = $this->TenderDocument->read(null,$id);
    $deadline = $tenderDocument['TenderDocument']['required_date']; // Deadline date as entered on form
    $deadline = $start = date('Y-m-d',strtotime('-1 day',strtotime($deadline))); // adjusted deadline
    $start = date('Y-m-d',strtotime('-3 months',strtotime($deadline))); // adjusted start
    $period = $this->_createDateRangeArray($start,$deadline);
    $TenderDocuments = $this->TenderDocument->find('list',
        array(
            'fields' => array('TenderDocument.book_dates')
        )
    );
    $dates = array();
    foreach($TenderDocuments as $TenderDocument) {
        $doc_dates = explode(',',$TenderDocument);
        foreach($doc_dates as $doc_date) {
            $dates[] = $doc_date;
        }
    }
    foreach($period as $key=>$value) {
        if(date('l',strtotime($value)) == 'Saturday' || date('l',strtotime($value)) == 'Sunday' || in_array($value,$dates)) {
            unset($period[$key]);
        }
    }
    rsort($period);
    for($k = 0; $k <= $tenderDocument['ContractForm']['time']; $k++) {
        $book_days[] = $period[$k];
    }
    $this->set('TenderDocument',$this->TenderDocument->read(null,$id));
    $this->set('book_days',$book_days);
}

If there is any way I can streamline this code and make it faster, feel free to comment.

다른 팁

I hope I understand your issue correctly, you need to calculate the end date of a contract given the number of days it should run for, without counting weekends as working days.

PHP's DateTime classes can simplify your code considerably. I think this function will help you:-

/**
 * Given the start date of a contract will return the end date, skipping weekends
 *
 * @param String $starDate format 'Y-m-d'
 * @param Int $numberOfDays Number of working days
 * @return Array of date strings
 */
function getDeadline($starDate, $numberOfDays)
{
    $start = \DateTime::createFromFormat('Y-m-d', $starDate);
    $interval = new DateInterval('P1D');
    $result = array();

    $i = 0;
    while($i < $numberOfDays){
        if((int)$start->format('N') < 6){
            $result[] = $start->format('Y-m-d');
            $i++;
        }
        $start->add($interval);
    }
    return $result;
}

$start = '2013-6-12';
var_dump(getDeadline($start, 4));

Output:-

array (size=4)
  0 => string '2013-06-12' (length=10)
  1 => string '2013-06-13' (length=10)
  2 => string '2013-06-14' (length=10)
  3 => string '2013-06-17' (length=10)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top