查找一个人一天中的可用时间

时间:2013-02-14 01:21:15

标签: php sql-server-2008 datetime

我正在为一个事件进行资源分配。

我的一个资源的事件表如下所示:

(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

现在我想在一天内找到该资源的总可用性。

与2013年2月20日一样,我希望从此资源中删除忙碌时间,并希望仅显示新活动的可用时间。

我正在使用php和sql server 2008 r2。

只用一天的一个记录就可以了。现在我正在使用带计算的foreach循环。

我的代码是:

$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);

我的结果是:

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
                )

        )

)

前两个记录的日期相同。但这只计算第二行的小时数。不计算第一行的小时数是13,14,15。

有谁能告诉我如何匹配日期以及如何获得一个日期的总忙碌时间?

3 个答案:

答案 0 :(得分: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--;
                            }
                        }

答案 1 :(得分: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);

答案 2 :(得分: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);