6

2 つの日時が与えられます。それらの間の労働時間数を計算する最良の方法は何ですか. 勤務時間は月曜日の 8 時から 5 時 30 分、火曜日から金曜日の 8 時 30 分から 5 時 30 分であり、祝日になる可能性があることを考慮してください。

これは私の努力であり、非常に非効率に見えますが、反復回数と、IsWorkingDay メソッドが DB にアクセスして、その日時が祝日かどうかを確認します。

誰でも最適化や代替案を提案できますか?

 public decimal ElapsedWorkingHours(DateTime start, DateTime finish)
        {

            decimal counter = 0;

            while (start.CompareTo(finish) <= 0)
            {   
                if (IsWorkingDay(start) && IsOfficeHours(start))
                {
                    start = start.AddMinutes(1);
                    counter++;
                }
                else
                {
                    start = start.AddMinutes(1);
                }
            }

            decimal hours;

            if (counter != 0)
            {
                hours = counter/60;
            }

            return hours;
        }
4

9 に答える 9

3

最適化を開始する前に、2 つの質問を自問してください。

a) それは機能しますか?

b) 遅すぎませんか?

両方の質問に対する答えが「はい」の場合にのみ、最適化を開始する準備ができています。

それとは別に

  • 開始日と終了日の分と時間を気にするだけで済みます。間にある日は、休日や週末でない限り、明らかに完全な 9/9.5 時間になります。
  • 休日かどうかを確認するために週末の日を確認する必要はありません

これが私がそれを行う方法です

// Normalise start and end    
while start.day is weekend or holiday, start.day++, start.time = 0.00am
    if start.day is monday,
        start.time = max(start.time, 8am)
    else
        start.time = max(start.time, 8.30am)
while end.day is weekend or holiday, end.day--, end.time = 11.59pm
end.time = min(end.time, 5.30pm)

// Now we've normalised, is there any time left?    
if start > end
   return 0

// Calculate time in first day    
timediff = 5.30pm - start.time
day = start.day + 1
// Add time on all intervening days
while(day < end.day)
   // returns 9 or 9.30hrs or 0 as appropriate, could be optimised to grab all records
   // from the database in 1 or 2 hits, by counting all intervening mondays, and all
   // intervening tue-fris (non-holidays)
   timediff += duration(day) 

// Add time on last day
timediff += end.time - 08.30am
if end.day is Monday then
    timediff += end.time - 08.00am
else
    timediff += end.time - 08.30am

return timediff

SELECT COUNT(DAY) FROM HOLIDAY WHERE HOLIDAY BETWEEN @Start AND @End GROUP BY DAY のようなことができます。

月曜日、火曜日、水曜日などの祝日の数を数えます。現時点では何も考えられませんが、おそらく SQL で月曜日と月曜日以外をカウントする方法です。

于 2008-09-26T19:20:17.150 に答える
1

特に、IsWorkingDay メソッドが DB にアクセスして、その日が祝日かどうかを確認することを考慮すると、

問題がデータの量ではなくクエリの数である場合は、ループの反復ごとにクエリを実行するのではなく、最初に必要な日の範囲全体についてデータベースから稼働日のデータをクエリします。

于 2008-09-26T19:18:55.547 に答える
1

TimeSpan クラスを見てください。これにより、任意の 2 回の間の時間が表示されます。

1 回の DB 呼び出しで、2 回の間に休日を取得することもできます。次の行に沿ったもの:

SELECT COUNT(*) FROM HOLIDAY WHERE HOLIDAY BETWEEN @Start AND @End

その数に 8 を掛けて、合計時間から引きます。

-イアン

編集:以下に対応して、休日の場合は一定の時間数ではありません。HolidayStartDB に aとTimeを保持し、HolidayEndそれらを呼び出しから db に返すこともできます。メイン ルーチンで決めた方法と同じように時間を数えます。

于 2008-09-26T19:23:19.090 に答える
1

再帰的な解決策もあります。必ずしも効率的ではありませんが、とても楽しいです:

public decimal ElapseddWorkingHours(DateTime start, DateTime finish)
{
    if (start.Date == finish.Date)
        return (finish - start).TotalHours;

    if (IsWorkingDay(start.Date))
        return ElapsedWorkingHours(start, new DateTime(start.Year, start.Month, start.Day, 17, 30, 0))
            + ElapsedWorkingHours(start.Date.AddDays(1).AddHours(DateStartTime(start.Date.AddDays(1)), finish);
    else
        return ElapsedWorkingHours(start.Date.AddDays(1), finish);
}
于 2008-09-26T19:42:45.410 に答える
0

@OregonGhost の発言に基づいて、IsWorkingDay() 関数を使用して日を受け入れてブール値を返すのではなく、範囲を受け入れて範囲内の休日の数を示す整数を返す HolidayCount() 関数を用意します。ここでの秘訣は、境界の開始日と終了日の部分的な日付を扱っている場合、それらの日付自体が休日であるかどうかを判断する必要がある場合があることです。ただし、その場合でも、新しいメソッドを使用して、DB への呼び出しが最大で 3 回必要であることを確認できます。

于 2008-09-26T19:26:41.563 に答える
0

これを行う最も効率的な方法は、時差の合計を計算してから、週末または休日の時間を差し引くことです。考慮すべき非常に多くのケースがありますが、範囲の最初と最後の日を取り、それらを別々に計算することで単純化できます。

Ian Jacobsによって提案された COUNT(*) メソッドは、休日を数える良い方法のようです。何を使用しても、1 日を処理するだけなので、開始日と終了日を別々にカバーする必要があります。

週末の日数を数えるのは簡単です。月曜日に 0 を返し、日曜日に 6 を返す関数 Weekday(date) がある場合、次のようになります。

saturdays = ((finish - start) + Weekday(start) + 2) / 7;
sundays = ((finish - start) + Weekday(start) + 1) / 7;

注: (終了 - 開始) は文字通り解釈されるべきではありません。期間を日数で計算するものに置き換えてください。

于 2008-09-26T19:47:38.410 に答える
0

これらの行に沿って何かを試してください:

TimeSpan = TimeSpan Between Date1 And Date2
cntDays = TimeSpan.Days 
cntNumberMondays = Iterate Between Date1 And Date2 Counting Mondays 
cntdays = cntdays - cntnumbermondays
NumHolidays = DBCall To Get # Holidays BETWEEN Date1 AND Date2
Cntdays = cntdays - numholidays 
numberhours = ((decimal)cntdays * NumberHoursInWorkingDay )+((decimal)cntNumberMondays * NumberHoursInMondayWorkDay )
于 2008-09-26T19:37:31.757 に答える
0

@Ian のクエリを使用して日付を確認し、営業日でない日を見つけます。次に、開始時刻または終了時刻が非稼働日に該当するかどうかを計算し、その差を差し引きます。

したがって、開始が土曜日の正午で、終了が月曜日の正午である場合、クエリは 2 日を返し、そこから 48 時間 (2 x 24) を計算します。IsWorkingDay(start) に対するクエリが false を返す場合、開始から深夜までの時間を 24 から引くと、12 時間、つまり合計 36 時間の非稼働時間が得られます。

ここで、オフィスアワーが毎日同じである場合、同様のことを行います。オフィスアワーが少しばらばらだと、さらに問題が発生します。

理想的には、データベースに対して単一のクエリを作成して、2 つの時間 (または日付) の間のすべての営業時間を取得します。次に、そのセットからローカルで計算を行います。

于 2008-09-26T19:51:33.750 に答える
-1
Dim totalMinutes As Integer = 0

For minute As Integer = 0 To DateDiff(DateInterval.Minute, contextInParameter1, contextInParameter2)
    Dim d As Date = contextInParameter1.AddMinutes(minute)
    If d.DayOfWeek <= DayOfWeek.Friday AndAlso _
       d.DayOfWeek >= DayOfWeek.Monday AndAlso _
       d.Hour >= 8 AndAlso _
       d.Hour <= 17 Then
        totalMinutes += 1
    Else
        Dim test = ""
    End If
Next minute

Dim totalHours = totalMinutes / 60

ケーキのピース!

乾杯!

于 2010-08-30T18:38:18.350 に答える