我在24小时内有List<T>
个可用时间,以及两个TimeSpans
,minTime和maxTime。
我需要找到位于List<T>
和minTime
之间的maxTime
内的时间,但是由于在多个时区中使用了该时间,minTime和maxTime可以是在不同的日子和第二天下午1点到凌晨1点之间的差距
我最接近的是这个,但我觉得我在这里缺少一些主要组件,或者做一些非常低效的事情,因为我对TimeSpan
对象很新。我只是想不出来......
// Make new TimeSpan out of maxTime to eliminate any extra days (TotalHours >= 24),
// then check if time on the MaxTime is earlier than the MinTime
if (new TimeSpan(maxTime.Hours, maxTime.Minutes, maxTime.Seconds) < minTime)
{
// If time on MaxTime is earlier than MinTime, the two times span separate days,
// so find first time after minTime OR before maxTime
nextAvailableTime = Times.FirstOrDefault(p =>
(p.Time.TimeOfDay >= minTime || (p.Time.TimeOfDay < maxTime))
&& p.Count < ConcurrentAppointments);
}
else
{
// If time on MaxTime is later than MinTime, the two times are for the same day
// so find first time after minTime AND before maxTime
nextAvailableTime = Times.FirstOrDefault(p =>
(p.Time.TimeOfDay >= minTime && p.Time.TimeOfDay < maxTime)
&& p.Count < ConcurrentAppointments);
}
Times
列表使用EST(我的当地时间),但minTime
和maxTime
可以基于其他时区。
例如,如果我们为夏威夷时区运行此算法,我们最终将拥有minTime = new TimeSpan(13, 0, 0)
和maxTime = new TimeSpan(25, 0, 0)
,从早上8点到晚上8点HST =下午1点 - 美国东部时间凌晨1点。
Times
集合是List<AppointmentTime>
,AppointmentTime
是一个如下所示的类:
class AppointmentTime
{
DateTime Time { get; set; }
int Count { get; set; }
}
我很确定我在这里缺少一些重要的东西,或者应该有一种更有效的方式来做这件事,我不知道,但我真的想不出它会是什么。我的算法有问题吗?或者更有效的方法是在两个TimeOfDay
之间找到可能跨越不同日期的TimeSpans
?
更新
我根据CasperOne's answer找出了我遗失的内容。我忘记了日期确实很重要,因为我的时间跨越了不同的时区。
使用我上面的夏威夷时区示例,安排星期一的约会将导致在周日晚上错误地安排夏威夷预约。
我的解决方案是在为24小时工作日的“第一个窗口”安排约会之前检查前一天是否有效,并在与.AddDays(maxTime.Days)
比较时调整约会日期maxTime
// If time on MaxTime is earlier than MinTime, the two times span separate days,
// so find first time after minTime OR before maxTime if previous day has appointments set as well
var isPreviousDayValid = IsValidDate(AppointmentDate.AddDays(-1));
nextAvailableTime = Times.FirstOrDefault(p =>
(p.Time.TimeOfDay >= minTime
|| (p.Time.AddDays(maxTime.Days).TimeOfDay < maxTime && isPreviousDayValid)
) && p.Count < ConcurrentAppointments);
答案 0 :(得分:1)
一般的想法是,不要比较次,比较日期;将您的窗户从时间转换为日期,其余的很容易。
您可以为列表中的每个项生成一组新的DateTime
实例,以比较最小值和最大值,使用Date
property作为计算基础您想要比较的范围上的上限。
这假定您的minTime
始终小于maxTime
,如果your window covers more than one day您通过Hours
property值表示重叠到新的一天的范围{3}} TimeSpan
。
您必须查看之前之前的日期以及之后的日期。例如:
(1) (2) (3) (4) (5)
----x----x----x----x----x----
(1) - 1/1/1900 11:00 PM - Date component in your list - 1 day + min time
(2) - 1/2/1900 12:05 AM - this is the date and time from your list
(3) - 1/2/1900 01:00 AM - Date component in your list - 1 day + max time
(4) - 1/2/1900 11:00 PM - Date component in your list + min time
(5) - 1/3/1900 01:00 AM - Date component in your list + max time
这意味着您需要创建两个窗口并检查以下内容:
nextAvailableTime = Times.FirstOrDefault(p => {
// Check count first, get this out of the way.
if (!(p.Count < ConcurrentAppointments)) return false;
// The date time and the date component
DateTime dt = p.Time;
DateTime d = dt.Date;
// The windows
DateTime prevWindowMin = d.AddDays(-1) + minTime;
DateTime prevWindowMax = d.AddDays(-1) + maxTime;
DateTime windowMin = d + minTime;
DateTime windowMax = d + maxTime;
// Is it in *either* window;
return
(prevWindowMin <= dt && dt <= prevWindowMax)||
(windowMin <= dt && dt <= windowMax);
});
您的问题并不完全清楚,但如果当天的某些时间是其他而不是列表中项目的Date
组件,则可以替换p.Time
对于的Date
组件日期(减去适当的一天来创建窗口),它应该可以工作。
答案 1 :(得分:1)
@Rachel,你能提供一个使这个无法使用的反例吗?
nextAvailableTime = Times.OrderBy(i => i.Time).FirstOrDefault(i => i.Count < ConcurrentAppointments &&
i.Time.TimeOfDay >= minTime &&
i.Time.TimeOfDay < maxTime
);
<强> [编辑] 强>
所以,以下应该有效。我理解它的方式,问题是如何有效地找到TimeOfDay
的最小AppointmentTime
,遵循TimeOfDay
和minTime
之间的最小值maxTime
}。对于1,000,000次迭代,Rachel的代码运行时间约为0.55秒
var Times = new List<AppointmentTime>();
var ConcurrentAppointments = 10;
Times.AddRange(new[]{
new AppointmentTime()
{
Count = 0,
Time = new DateTime(2012, 12, 1, 1, 30, 0)
},
new AppointmentTime()
{
Count = 0,
Time = new DateTime(2012, 12, 1, 13, 5, 0)
},
new AppointmentTime()
{
Count = 0,
Time = new DateTime(2012, 12, 1, 11, 0, 0)
}});
var minTime = new TimeSpan(13, 0, 0);
var maxTime = new TimeSpan(25, 0, 0);
// Version 1
// Not so performant, ~0.48 seconds for a loop of 1,000,000 iterations, see Version 2
//nextAvailableTime = Times.OrderBy(i => i.Time).FirstOrDefault(i => i.Count < ConcurrentAppointments &&
// i.Time.TimeOfDay.TotalSeconds >= Math.Min(maxTime.TotalSeconds % (3600 * 24), minTime.TotalSeconds)
// );
// Version 2
// Better performance, ~0.12 seconds for 1,000,000 iterations. We calculate the
// constant value we are comparing with outside the lambda expression
// We calculate the `totalSeconds` variable as the minimum of seconds within the
// 24h day. For that, we use the `% (3600 * 24)` operation to exclude the days.
var totalSeconds = (int)Math.Min(maxTime.TotalSeconds % (3600 * 24), minTime.TotalSeconds);
// We create a timespan variable called `timeOfDay` which is based on the
// `totalSeconds` variable above. Note that the day is not essential.
var timeOfDay = (new DateTime(1, 1, 1, totalSeconds / 3600, (totalSeconds % 3600) / 60, totalSeconds % 60)).TimeOfDay;
// Returns the `AppointmentTime` with the 01:30 AM. Note, again, that the
// date of the `AppointmentTime` is not essential
nextAvailableTime = Times.FirstOrDefault(i => i.Count < ConcurrentAppointments &&
i.Time.TimeOfDay >= timeOfDay
);