我目前正在使用包含开始时间,日期和以秒为单位的持续时间的数据集,并且需要能够将从开始时间到(开始时间+持续时间,例如结束时间)的时间范围划分为小时,如果您愿意,则为“时段”。
例如,开始时间为08:30:00,持续时间为9000秒(2.5小时),从8:30:00到09:00:00的小时08持续1800秒,从09到10:00:00 3600秒,依此类推,直到11:00:00的结束时间为止。
什么是最好的方法?我正在使用PHP,但是一般的算法解决方案仍然会非常有用。
我当前的方法如下:
1. Calculate the hour difference between both times (round up to next hour if there's a non-zero number of minutes)
2. Let session progress = 0
3. Iterate the range from 1 to hour difference:
1. Let the current bucket duration = min(3600, duration)
2. Let the duration = max(0, duration - current bucket duration)
3. Let bucket start time = start time + session progress
4. Let bucket end time = bucket start time + current bucket duration
5. Do work based on these values
6. Let session progress += current bucket duration
我的PHP实现如下:
foreach ($csv as $listen_record) {
$start_dt = $listen_record['Date'] . ' ' . $listen_record['Time'];
$session_duration = $listen_record['Duration'];
$session_start_time = date_create_from_format($datetime_format, $start_dt);
$session_end_time = clone $session_start_time;
date_add($session_end_time, date_interval_create_from_date_string($session_duration . ' seconds'));
$diff = date_diff($session_end_time, $session_start_time);
$hourdiff = $diff->h;
if ($diff->m > 0)
$hourdiff += 1;
$session_progress = 0;
foreach (range(1, $hourdiff) as $hour) {
$record_duration = min(3600, $session_duration);
$session_duration = max(0, $session_duration - $record_duration);
$record_start_time = clone $session_start_time;
date_add($record_start_time, date_interval_create_from_date_string($session_progress . ' seconds'));
$record_end_time = clone $record_start_time;
date_add($record_end_time, date_interval_create_from_date_string($record_duration . ' seconds'));
if ($record_start_time == $record_end_time)
continue;
// DO WORK...
$session_progress += $record_duration;
}
}
[这种将每条记录分成几个小时的时段的工作在某些情况下(尤其是范围跨入新的一天的情况)给出了一些奇怪的结果,并且与实际的挂钟时间不符。
是否有更好的方法可以实际上与挂钟时间保持一致,并且在必须应付午夜的持续时间时却不会惊慌?
谢谢!
更新:我设法自己解决了这个问题,所以我发布了我的操作方法,以防万一其他人需要做类似的事情。
基本上,我通过在开始和结束时间戳之间创建一个范围,并过滤掉除开始和结束以外不是3600或3600之前的倍数的每个时间戳,来完成此操作。时间戳记的范围。然后,我可以将数组分成2个块,其中包含插槽的开始时间和结束时间。
我的PHP代码如下,现在可以正常工作:
function is_wall_clock_aligned_hour_boundary($timestamp) {
return ($timestamp % (60*60) === 0) || (($timestamp + 1) % (60*60) === 0);
}
function create_slot_object($slot) {
$start_time = reset($slot);
$end_time = end($slot);
$duration = $end_time - $start_time;
return array(
'startTime' => convert_timestamp_to_datetime($start_time),
'endTime' => convert_timestamp_to_datetime($end_time),
'duration' => $duration
);
}
function make_slots(DateTime $start_time, $duration) {
$start_timestamp = $start_time->getTimestamp();
$end_time = clone $start_time;
date_add($end_time, date_interval_create_from_date_string("$duration seconds"));
$end_timestamp = $end_time->getTimestamp();
$time_sequence = range($start_timestamp, $end_timestamp);
$slot_boundaries = array_filter($time_sequence, 'is_wall_clock_aligned_hour_boundary');
array_unshift($slot_boundaries, $start_timestamp);
array_push($slot_boundaries, $end_timestamp);
$slots = array_chunk($slot_boundaries, 2);
return array_map('create_slot_object', $slots);
}