我正在为一个事件进行资源分配。
我的一个资源的事件表如下所示:
(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。
有谁能告诉我如何匹配日期以及如何获得一个日期的总忙碌时间?
答案 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);