6

1List<T>日 24 時間以内に 1 つの利用可能な時間とTimeSpans、minTime と maxTime の 2 つがあります。

List<T>との間にある時刻を見つける必要がありますが、これは複数のタイムゾーンで使用されているため、minTimeminTimemaxTimemaxTime は別々の日になり、午後 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 (現地時間) を使用していますが、minTimemaxTimeは他のタイム ゾーンに基づいている場合があります。

たとえば、ハワイのタイム ゾーンでこのアルゴリズムを実行した場合、最終的にminTime = new TimeSpan(13, 0, 0)andが得られmaxTime = new TimeSpan(25, 0, 0)ます。

Timesコレクションは でList<AppointmentTime>あり、クラスは次のAppointmentTimeようになります。

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

ここで大きな何かが欠けているか、私が気付いていないより効率的な方法があるはずだと確信していますが、それが何であるかは本当にわかりません。私のアルゴリズムに何か問題がありますか? または、別々の日にまたがる可能性のあるTimeOfDay2 つの間のより効率的な方法はありますか?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);
4

2 に答える 2

1

一般的な考え方は、時間を比較するのではなく、日付を比較することです。ウィンドウを時間から日付に変換すれば、あとは簡単です。

比較対象の範囲の上限を計算するためのベースとしてプロパティを使用して、最小値と最大値を比較するために、リスト内の項目に対して新しいDateTimeインスタンスのセットを生成できます。Date

minTimeこれは、が常に よりも小さいことを前提としています。maxTimeまた、ウィンドウが 1 日を超える場合は、 に 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

これは、2 つのウィンドウを作成し、次のいずれかを確認する必要があることを意味します。

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);
});

あなたの質問からは完全には明らかではありませんが、1 日の時間帯がリスト内のアイテムの構成要素以外のものである場合は、その日付の構成要素を置き換えることができ(ウィンドウ作成するために必要に応じて 1 日を減算します)、それはすべきです仕事。Datep.TimeDate

于 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
            );

[編集]

したがって、次のように動作するはずです。私が理解した方法では、問題は、と の間の最小値に続いて、 の最小値TimeOfDayを効率的に見つける方法です。1,000,000 回の反復で、Rachel のコードは ~0.55 秒で実行されますAppointmentTimeTimeOfDayminTimemaxTime

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 に答える