0

I am doing resource allocation for an event.

My event table for one resource is looks like this:

(Int)---(nvarchar(100)---(datetime)--(datetime)

EventId --- Event ---    StartTime ---              EndTime

1    /       test    /    2013-02-20 13:00:00   /   2013-02-20 15:00:00

2    /       test2   /     2013-02-20 09:30:00  /   2013-02-20 11:00:00

3    /       test3   /     2013-02-25 11:30:00  /   2013-02-25 14:30:00

Now I want to find the total availablity for this resource on one day.

Like on 20th Feb 2013 I want to remove the busy hours from this resource and want to show only available hours for new event.

I am using php and sql server 2008 r2.

It works fine with only one record of a single day. Right now I am using a foreach loop with calculation.

My Code is:

$id = 6;    
$cdata = $test->getResourceEvents($id);

$h = "";
$final= array();

foreach($cdata as $c)
   {

   $sh = $c['starttime']->Format('H'); // Starting hour
   $eh = $c['endtime']->Format('H'); // End hour

   $hh = $sh;
   $final = array();
   $sdate = $c['starttime']->Format('Y-m-d');
   $edate = $c['endtime']->Format('Y-m-d');
   if($edate == $sdate)
   {

   $dh = $eh-$sh; // Duration
       for($i=1;$i<=$dh;$i++)
       {
           $hh = $hh.",".($sh+$i); // Busy hours
       }    
   $busyhrs[$sdate] = explode(",",$hh);
       $final[$sdate] = $busyhrs;
   }
   else
   {
       echo "false";
   }
}

print_r($final);

and my result is :

Array
(
    [2013-02-20] => Array
        (
            [2013-02-20] => Array
                (
                    [0] => 9
                    [1] => 10
                    [2] => 11
                )

        )

    [2013-02-26] => Array
        (
            [2013-02-26] => Array
                (
                    [0] => 11
                    [1] => 12
                    [2] => 13
                    [3] => 14
                )

        )

)

First two records have the same dates. but this only calculates 2nd row's hours. Not calculating first row's hours that is 13,14,15.

Can anyone please tell me how to match the dates and how to get total busy hours of one date?

4

3 回答 3

0

我在 Java 中做了类似的事情。

我有一个包含日期范围的表格,还有一个日期范围,我必须从已经存在的日期范围插入到覆盖范围的失效中。

我基本上采用了我感兴趣的日期范围,并“减去”了所有现有的日期范围。您将在下面的代码中找到我的减法。

从 DateRange_A 中减去 DateRange_B 会导致 DateRange_A 被原地修改,并且如果 DateRange_A 完全被 DateRange_B 分割,则该方法返回一个新的 DateRange。

当然还有其他方法可以解决这个问题,比如 SQL SERVER 中的迭代,但我已经处于 Java 思维模式,这个解决方案就这样发生了。


/*
 * Copyright (c) 2009, Ben Fortuna
 * (Modified by Alex Marunowski)
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *  o Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 *  o Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * 
 *  o Neither the name of Ben Fortuna nor the names of any other contributors
 * may be used to endorse or promote products derived from this software
 * without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.io.Serializable;
import java.util.Date;

/**
 * @author fortuna
 *
 */
public class DateRange implements Serializable {

    private static final long serialVersionUID = -7303846680559287286L;

    /**
     * A flag indicating whether to include the start of the period in test functions.
     */
    public static final int INCLUSIVE_START = 1;

    /**
     * A flag indicating whether to include the end of the period in test functions.
     */
    public static final int INCLUSIVE_END = 2;

    private Date rangeStart;

    private Date rangeEnd;

    /**
     * @param start the start of the range
     * @param end the end of the range
     */
    public DateRange(Date start, Date end) {
        if (start == null) {
            throw new IllegalArgumentException("Range start is null");
        }
        if (end == null) {
            throw new IllegalArgumentException("Range end is null");
        }
        if (end.before(start)) {
            throw new IllegalArgumentException("Range start must be before range end");
        }
        this.rangeStart = start;
        this.rangeEnd = end;
    }

    /**
     * @return the rangeStart
     */
    public Date getStartDate() {
        return rangeStart;
    }

    /**
     * @return the rangeEnd
     */
    public Date getEndDate() {
        return rangeEnd;
    }

    public void setRangeStart(Date rangeStart) {
        if(rangeStart.after(getEndDate()))
            throw new IllegalArgumentException("The start of a date range cannot be after its end!");

        this.rangeStart = rangeStart;
    }

    public void setRangeEnd(Date rangeEnd) {
        if(rangeStart.after(getEndDate()))
            throw new IllegalArgumentException("The end of a date range cannot be after its start!");

        this.rangeEnd = rangeEnd;
    }

    /**
     * Determines if the specified date occurs within this period (inclusive of
     * period start and end).
     * @param date a date to test for inclusion
     * @return true if the specified date occurs within the current period
     * 
     */
    public final boolean includes(final Date date) {
        return includes(date, INCLUSIVE_START | INCLUSIVE_END);
    }

    /**
     * Decides whether a date falls within this period.
     * @param date the date to be tested
     * @param inclusiveMask specifies whether period start and end are included
     * in the calculation
     * @return true if the date is in the period, false otherwise
     * @see Period#INCLUSIVE_START
     * @see Period#INCLUSIVE_END
     */
    public final boolean includes(final Date date, final int inclusiveMask) {
        boolean includes = true;
        if ((inclusiveMask & INCLUSIVE_START) > 0) {
            includes = includes && !rangeStart.after(date);
        }
        else {
            includes = includes && rangeStart.before(date);
        }
        if ((inclusiveMask & INCLUSIVE_END) > 0) {
            includes = includes && !rangeEnd.before(date);
        }
        else {
            includes = includes && rangeEnd.after(date);
        }
        return includes;
    }

    /**
     * Decides whether this period is completed before the given period starts.
     * 
     * @param range
     *            a period that may or may not start after this period ends
     * @return true if the specified period starts after this periods ends,
     *         otherwise false
     */
    public final boolean before(final DateRange range) {
        return (rangeEnd.before(range.getStartDate()));
    }

    /**
     * Decides whether this period starts after the given period ends.
     * 
     * @param range
     *            a period that may or may not end before this period starts
     * @return true if the specified period end before this periods starts,
     *         otherwise false
     */
    public final boolean after(final DateRange range) {
        return (rangeStart.after(range.getEndDate()));
    }

    /**
     * Decides whether this period intersects with another one.
     * 
     * @param range
     *            a possible intersecting period
     * @return true if the specified period intersects this one, false
     *         otherwise.
     */
    public final boolean intersects(final DateRange range) {
        boolean intersects = false;
        // Test for our start date in period
        // (Exclude if it is the end date of test range)
        if (range.includes(rangeStart) && !range.getEndDate().equals(rangeStart)) {
            intersects = true;
        }
        // Test for test range's start date in our range
        // (Exclude if it is the end date of our range)
        else if (includes(range.getStartDate())
                && !rangeEnd.equals(range.getStartDate())) {
            intersects = true;
        }
        return intersects;
    }

    /**
     * Decides whether these periods are serial without a gap.
     * @param range a period to test for adjacency
     * @return true if one period immediately follows the other, false otherwise
     */
    public final boolean adjacent(final DateRange range) {
        boolean adjacent = false;
        if (rangeStart.equals(range.getEndDate())) {
            adjacent = true;
        } else if (rangeEnd.equals(range.getStartDate())) {
            adjacent = true;
        }
        return adjacent;
    }

    /**
     * Decides whether the given period is completely contained within this one.
     * 
     * @param range
     *            the period that may be contained by this one
     * @return true if this period covers all the dates of the specified period,
     *         otherwise false
     */
    public final boolean contains(final DateRange range) {
        // Test for period's start and end dates in our range
        return (includes(range.getStartDate()) && includes(range.getEndDate()));
    }

    /**
     * Decides whether the given period is completely contained within this one, taking into consideration whether date ranges with matching start or end dates 
     * are counted as being contained
     * 
     * @param range
     *            the period that may be contained by this one
     * @param inclusiveMask
     *              if this is set to 0, the start and end dates cannot be the same date and have it be considered to be contained within this date range.
     *              this.contains(this, 1) returns true 
     *              this.contains(this, 0) returns false 
     * @return true if this period covers all the dates of the specified period,
     *         otherwise false
     */
    public final boolean contains(final DateRange range, int inclusiveMask) {
        // Test for period's start and end dates in our range
        return (includes(range.getStartDate(), inclusiveMask) && includes(range.getEndDate(), inclusiveMask));
    }


    /**
     * Exclude otherRange from the dates covered by this DateRange. Note: This will put the specified buffer around the range being subtracted from this date range. 
     * @param otherRange
     * @return an additional date range if subtracting otherRange from this DateRange results in a part of this objects DateRange being separated from the rest of the range. 
     * i.e. if this.includes(otherRange, 0), then there will be two remaining portions of this daterange.
     * If no dangling date range remains, then the method returns null.
     * @author Alex Marunowski. 2012.10.31
     */
    public DateRange subtract(DateRange otherRange, long buffer) throws DateRangeObliteratedException{

        Date bufferedStart = new Date(otherRange.getStartDate().getTime()-buffer);
        Date bufferedEnd= new Date(otherRange.getEndDate().getTime()+buffer);
        otherRange = new DateRange(bufferedStart, bufferedEnd);

        // If the other range is entirely after this range, nothing happens
        if(getEndDate().before(otherRange.getStartDate()))
            return null;

        // If the other range is entirely before this range, nothing happens
        if(getStartDate().after(otherRange.getEndDate()))
            return null;

        if(otherRange.contains(this))
            throw new DateRangeObliteratedException();

        DateRange separatedTimeInterval = null;
        if(this.contains(otherRange, 0)){

            // The trailing daterange is the time between the end date of the inner daterange, and the end date of the outer date range
            separatedTimeInterval = new DateRange(otherRange.getEndDate(), getEndDate());

            // This date range now ends at the start time of the inner date range
            this.setRangeEnd(otherRange.getStartDate());

            return separatedTimeInterval;
        }




        if(otherRange.getEndDate().before(getEndDate())){
            // This date range is now the time between the end of the otherRange plus the buffer time, and the end of this date range
            long newRangeStart = otherRange.getEndDate().getTime();
            this.setRangeStart(new Date(newRangeStart));
            return null;
        }

        if(otherRange.getStartDate().after(getStartDate())){
            // This date range is now the time between this date range's start, and the other date ranges start minus the buffer time
            long newRangeEnd = otherRange.getStartDate().getTime();
            this.setRangeEnd(new Date(newRangeEnd));
            return null;
        }

        // This will never happen, but the compiler doesn't know that
        System.out.println("This should never have happened. No comparisons between the date ranges was discovered");
        return null;



    }

    public static class DateRangeObliteratedException extends Exception {
        /**
         * 
         */
        private static final long serialVersionUID = -5642891561498447972L;

        public DateRangeObliteratedException() {
            super("This date range no longer exists. It was entirely contained within the range you subtracted from it.");
        }
    }
}



                        for(int rangeIndex = 0; rangeIndex less than rangesBusy.size(); rangeIndex++) {
                            DateRange busyRange = rangesBusy.get(rangeIndex);
                            try {
                                DateRange trailingRange =  freeTimeBlock.subtract(busyRange, 0);

                                if(trailingRange != null) {
                                    freeTimeRanges.add(trailingRange);
                                }

                            } catch (DateRangeObliteratedException e) {
                                freeTimeRanges.remove(rangeIndex);
                                rangeIndex--;
                            }
                        }
于 2013-02-14T02:12:21.263 回答
0

我认为这符合您的预期。关键是那些涉及 $all_finals 和 $final 的移动。我通常用python编写并且不知道在php中附加到数组的最佳方法所以我使用了这个http://php.net/manual/en/function.array-push.php

$id = 6;    
$cdata = $test->getResourceEvents($id);

$h = "";
$all_finals = array();


foreach($cdata as $c)
   {
   $final= array();
   $sh = $c['starttime']->Format('H'); // Starting hour
   $eh = $c['endtime']->Format('H'); // End hour

   $hh = $sh;
   $final = array();
   $sdate = $c['starttime']->Format('Y-m-d');
   $edate = $c['endtime']->Format('Y-m-d');
   if($edate == $sdate)
   {

   $dh = $eh-$sh; // Duration
       for($i=1;$i<=$dh;$i++)
       {
           $hh = $hh.",".($sh+$i); // Busy hours
       }    
   $busyhrs[$sdate] = explode(",",$hh);
       $final[$sdate] = $busyhrs;
   }
   else
   {
       echo "false";
   }
   array_push($all_finals, $final);
}

print_r($all_final);
于 2013-02-14T02:19:03.953 回答
0

我找到了解决方案。我改变了制作最终数组的过程。非常感谢大家帮助我。我认为它很简单。这是我的代码。

可能会对某人有所帮助。

        $id = 6;    
        $cdata = $test->getResourceEvents($id);

        $h = "";
        $final= array();

        foreach($cdata as $c)
        {
            $sh = $c['starttime']->Format('H'); // Starting hour

            $eh = $c['endtime']->Format('H'); // End hour


            $hh = $sh;
            $busyhrs = array();
            $sdate = $c['starttime']->Format('Y-m-d');
            $edate = $c['endtime']->Format('Y-m-d');
            if($edate == $sdate)
            {
                $dh = $eh-$sh; // Duration
                for($i=1;$i<=$dh;$i++)
                {
                    $hh = $hh.",".($sh+$i); // Busy hours
                }   

                if($final != array() || $final != NULL)
                {
                    foreach($final as $key=>$val)
                    {
                        if($key==$sdate)
                        {
                            $final[$key] = $val.",".$hh;
                        }
                        else
                        {
                            $final[$sdate] = $hh;
                        }
                    }
                }
                else
                {
                    $final[$sdate] = $hh;
                }                           
            }
            else
            {
                echo "false";
            }
        }

        echo "<pre>";
        print_r($final);
于 2013-02-14T23:22:52.477 回答