6

List<T>在一天 24 小时内有一个可用时间,还有两个TimeSpansminTime 和 maxTime。

我需要在 和 之间找到一天中的时间,List<T>但是由于它在多个时区中使用,minTime 和 maxTime 可以在不同的日子里,并且跨越像下午 1 点到第二天凌晨 1 点minTimemaxTime

我最接近的是这个,但我觉得我在这里遗漏了一些主要组件,或者因为我对这个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);
}

使用列表TimesEST(我的当地时间),但是minTimemaxTime可以基于其他时区。

例如,如果我们针对夏威夷时区运行此算法,我们最终会得到minTime = new TimeSpan(13, 0, 0)maxTime = new TimeSpan(25, 0, 0),因为 8am - 8pm HST = 1pm - 1am EST。

Times集合是一个List<AppointmentTime>,并且是一个类,AppointmentTime如下所示:

class AppointmentTime
{
    DateTime Time { get; set; } 
    int Count { get; set; }
}

我很确定我在这里遗漏了一些重要的东西,或者应该有一种我不知道的更有效的方法来做到这一点,但我真的想不出它会是什么。我的算法有问题吗?或者找到可能跨越不同日子的TimeOfDay两个更有效的方法?TimeSpans

更新

我根据CasperOne 的回答找出了我缺少的东西。我忘记了日期实际上很重要,因为我的时间跨越不同的时区。

使用我上面的夏威夷时区示例,在星期一安排约会会导致在星期日晚上错误地安排夏威夷约会。

我的解决方案是在为 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);
4

2 回答 2

1

总体思路是,不要比较时间,要比较日期;将您的窗口从时间转换为日期,剩下的就很容易了。

您可以为列表中的每个项目生成一组新DateTime实例以比较最小值和最大值,使用该属性作为计算要比较的范围的上限的基础。Date

这假定您minTime总是小于maxTime,并且如果您的窗口覆盖一天以上,您通过在大于 24 小时的Hours属性值上表示重叠到新一天的范围。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组成部分(适当减去一天以创建窗口),它应该工作。

于 2012-12-14T20:54:14.133 回答
1

@Rachel,您能否提供一个反例使其无法使用?

nextAvailableTime = Times.OrderBy(i => i.Time).FirstOrDefault(i => i.Count < ConcurrentAppointments &&
    i.Time.TimeOfDay >= minTime &&
    i.Time.TimeOfDay < maxTime
            );

[编辑]

因此,以下应该有效。我理解它的方式,问题是如何有效地找到最小TimeOfDayAppointmentTime,遵循 和 之间的TimeOfDay最小值。对于 1,000,000 次迭代,Rachel 的代码运行时间约为 0.55 秒minTimemaxTime

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
                );
于 2012-12-14T20:54:49.033 回答