我试图完全从MySQL查询中获取这些空闲时间块,但是我无法构建一个可以处理数据的查询。我选择从查询中获取start
和end
日期,并在PHP中计算出空闲时间块。然而,这也证明是困难的。
我有以下数组(只是一个例子,日期和时间会有所不同):
$meeting = array(array('start' => '2016-11-14 16:00:00',
'end' => '2016-11-14 16:30:00'
),
array('start' => '2016-11-14 16:45:00',
'end' => '2016-11-14 20:00:00'
),
array('start' => '2016-11-14 14:00:00',
'end' => '2016-11-14 15:00:00'
),
array('start' => '2016-11-14 13:00:00',
'end' => '2016-11-14 14:00:00'
),
array('start' => '2016-11-11 15:20:00',
'end' => '2016-11-11 16:00:00'
),
array('start' => '2016-11-11 14:00:00',
'end' => '2016-11-11 15:00:00'
),
array('start' => '2016-11-07 07:00:00',
'end' => '2016-11-09 15:00:00'
)
);
我想最终得到的是当天每个空闲时间段的开始和结束时间,每天只有09:00到17:00,不包括周末。因此,例如,上面的会议时间数组应该产生以下空闲时间数组:
$daily_free = array('2016-11-09' => array(array('free_start' => '15:00:00',
'free_end' => '17:00:00'
)
),
'2016-11-10' => array(array('free_start' => '09:00:00',
'free_end' => '17:00:00'
)
),
'2016-11-11' => array(array('free_start' => '09:00:00',
'free_end' => '14:00:00'
),
array('free_start' => '15:00:00',
'free_end' => '15:20:00'
),
array('free_start' => '16:00:00',
'free_end' => '17:00:00'
)
),
'2016-11-14' => array(array('free_start' => '09:00:00',
'free_end' => '13:00:00'
),
array('free_start' => '15:00:00',
'free_end' => '16:00:00'
),
array('free_start' => '16:30:00',
'free_end' => '16:45:00'
)
)
);
正如你所看到的,只有几天有任何空闲时间出现在阵列中,11月7日和8日没有空闲时间所以它们根本没有出现。此外,11月12日和13日没有出现在空闲时间阵列中,这是因为它们在周末出现。但是,如果第12和第13个不是周末,它们应该在空闲时间数组中显示为空闲时间的整天(09:00到17:00)。
我能想到实现这一目标的唯一方法是创建每天的数组,并且在其中有一个从05:00到17:00的5分钟增量数组,其值为NULL。然后遍历此数组,检查每个日期和5分钟增量是否在第一个数组的开始和结束时间内,如果是,则将其标记为忙。然后我可以再次循环并获取每天空值的开始和结束时间?这似乎不是一个很好的解决方案,如果会议时间少于几分钟也会失败。
我认为此时我不能看到树木用于树木,而且可能已经过度思考了。
还有其他解决方案吗?
修改 SQL Fiddle适用于任何想要通过SQL尝试的人
答案 0 :(得分:3)
他们通过它来解决这个问题,就像你在用材料雕刻一些东西一样。我会从一整天的空闲时间[09-17]开始,然后循环约会。对于每个约会,您要么拆分现有的空闲时间(然后按开始日期重新排序),要么更改范围。如果时间不重叠,那就容易多了。如果约会超出空闲时间范围,您可以忽略它。
{----} {--------} Free Time Range
[---] Appointment in the middle - you need to split
{----} {--------} Free Time Range
[---] Appointment at the beginning - change the start date
{----} {--------} Free Time Range
[---] Appointment at the end - change the end date
{----} {--------} Free Time Range
[--------] Appointment fills the whole spot - delete
答案 1 :(得分:3)
遵循@ guided1的逻辑,我开始运行一些测试,我越来越开始编写整个算法,就在这里。请转到@ guided1
LinearLayout
答案 2 :(得分:1)
纯MySQL解决方案。不是真正实用,但更像是大脑锻炼:
SELECT r.*
FROM (
SELECT DISTINCT dd.`day`,
IF(dd.`ba` = 1, f.`free_before_start`, f.`free_after_start`) as `start`,
IF(dd.`ba` = 1, f.`free_before_end`, f.`free_after_end`) as `end`
FROM (
SELECT
a.`day`,
IF(ISNULL(a.`id`) OR a.`before_day` < a.`day`, a.`day_start`, a.`before_end`) as `free_before_start`,
IF(ISNULL(a.`id`), a.`day_end`, a.`start`) as `free_before_end`,
IF(ISNULL(a.`id`), a.`day_start`, a.`end`) as `free_after_start`,
IF(ISNULL(a.`id`) OR a.`next_day` > a.`day`, a.`day_end`, a.`next_start`) as `free_after_end`
FROM (
SELECT n.*,
b.`day` as `before_day`, b.`end` as `before_end`,
t.`day` as `next_day`, t.`start` as `next_start`
FROM (
SELECT (@c1 := @c1 + 1) as `rowid`, s.*
FROM (
SELECT d.`day`, d.`start` AS `day_start`, d.`end` AS `day_end`,
m.`id`, GREATEST(d.`start`, m.`start`) AS `start`, LEAST(d.`end`, m.`end`) AS `end`
FROM days d
LEFT JOIN `meetings` m ON
m.start <= d.end
AND m.end >= d.start
ORDER BY 2 ASC, 6 ASC
) s
CROSS JOIN (SELECT @c1 := 0) AS dummy
) n
LEFT JOIN
(
SELECT (@c2 := @c2 + 1) as `rowid`, s.*
FROM (
SELECT d.`day`, d.`start` AS `day_start`, d.`end` AS `day_end`,
m.`id`, GREATEST(d.`start`, m.`start`) AS `start`, LEAST(d.`end`, m.`end`) AS `end`
FROM days d
LEFT JOIN `meetings` m ON
m.start <= d.end
AND m.end >= d.start
ORDER BY 2 ASC, 6 ASC
) s
CROSS JOIN (SELECT @c2 := 0) AS dummy
) t on t.`rowid` = n.`rowid` + 1
LEFT JOIN
(
SELECT (@c3 := @c3 + 1) as `rowid`, s.*
FROM (
SELECT d.`day`, d.`start` AS `day_start`, d.`end` AS `day_end`,
m.`id`, GREATEST(d.`start`, m.`start`) AS `start`, LEAST(d.`end`, m.`end`) AS `end`
FROM days d
LEFT JOIN `meetings` m ON
m.start <= d.end
AND m.end >= d.start
ORDER BY 2 ASC, 6 ASC
) s
CROSS JOIN (SELECT @c3 := 0) AS dummy
) b on b.`rowid` = n.`rowid` - 1
) a
) f
INNER JOIN (
SELECT `day`, 1 as `ba` FROM `days`
UNION ALL
SELECT `day`, 2 as `ba` FROM `days`
ORDER BY 1, 2
) dd on f.`day` = dd.`day`
WHERE f.`free_before_start` <> f.`free_before_end` OR f.`free_after_start` <> f.`free_after_end`
) r
WHERE r.`start` <> r.`end`
需要working hours
表格如下:
CREATE TABLE `days` (
`day` date NOT NULL,
`start` timestamp NOT NULL,
`end` timestamp NOT NULL,
PRIMARY KEY (`day`)
) ENGINE=InnoDB CHARSET=utf8;
INSERT INTO `days`
(`day`, `start`, `end`)
VALUES
('2016-11-06', '2016-11-06 07:00:00', '2016-11-06 17:00:00'),
('2016-11-07', '2016-11-07 07:00:00', '2016-11-07 17:00:00'),
('2016-11-08', '2016-11-08 07:00:00', '2016-11-08 17:00:00'),
('2016-11-09', '2016-11-09 07:00:00', '2016-11-09 17:00:00'),
('2016-11-10', '2016-11-10 07:00:00', '2016-11-10 17:00:00'),
etc, around 250 working days per year.
在整个代码中添加日期范围可以在很短的时间间隔内显着加快速度,但会降低其可读性。
答案 3 :(得分:0)
不是使用您拥有的日期和日期,而是将值存储为## create dummy raster
n <- 50
r <- raster(ncol=n, nrow=n, xmn=4, xmx=10, ymn=52, ymx=54)
projection(r) <- "+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"
values(r) <- 1:n^2+rnorm(n^2)
n.side <- 2 # number of tiles per side
dx <- (extent(r)[2]- extent(r)[1])/ n.side # extent of one tile in x direction
dy <- (extent(r)[4]- extent(r)[3])/ n.side # extent of one tile in y direction
xs <- seq(extent(r)[1], by= dx, length= n.side) #lower left x-coordinates
ys <- seq(extent(r)[3], by= dy, length= n.side) #lower left y-coordinates
cS <- expand.grid(x= xs, y= ys)
## loop over extents and crop
for(i in 1:nrow(cS)) {
ex1 <- c(cS[i,1], cS[i,1]+dx, cS[i,2], cS[i,2]+dy) # create extents for cropping raster
cl1 <- crop(r, ex1) # crop raster by extent
writeRaster(x = cl1, filename=paste("test",i,".tif", sep=""), format="GTiff", overwrite=T) # write to file
}
## check functionality...
test <- raster(paste("test1.tif", sep=""))
plot(test)
个对象,然后使用这些将日期转换为UNIX时间戳。这些只是自UNIX Epoch(1970-01-01 00:00:00)开始以来的秒的整数值。
现在,您可以进行简单的数学计算,找出日期范围内的差距(将开始日期/时间和结束日期/时间转换为结束标记的时间戳)。
一旦您将一个开始结束块标识为时间戳,您就可以将其反馈到DateTime
对象并将其转换回标准日期表示。
识别空闲块的长度也很简单。由于值均为秒数,因此从结束时间减去开始时间将产生秒数差异。为了确保它大于15分钟,你可以将它与整数值900(60 * 15)进行比较,如果它大于那么差距大于15分钟。