2

私は、2 つの特定の日付の間のすべての日 (月、火、水) を取得することが最善かつ最速の方法であると考えています。

曜日は、パラメータとしてコンマ区切りの文字列として Web サービスに渡されます。mon,tue,wed,

これは現時点での疑似コードです。

だから私の(疑似)コードは次のようになります

function Add(startdate, enddate, days)
{
    dateList as New List(Of Date)
    if days.split(',').count < 7 then
    {
        // This is the problem area.

    }

    while(startdate < enddate)
    {
        // do some processing of either the dates in dateList or just all days
    }
}

私にとってこの問題は

1)

  • daysパラメータを配列に分割し、
  • 日の配列をループする
  • それぞれdayの日数:
    • ~の間の各日付をstartdate調べるenddate
    • の場合、このメソッドの dateList に追加するdate.day と、潜在的に (365 * 6) チェックが行われます (開始日と終了日が 1 年離れていると仮定します)。また、すべての日が選択されている場合は、それらをリストに入れることを無視できますday

2)

  • ~の間の各日付をstartdate調べるenddate
  • スイッチ (日付.曜日.tostring())
  • case 'mon':if days.contains('mon') then addDatetoList

3)

  • everyday~の間startdateのそれぞれについてenddate
  • if days.contains(everyday.dayofweek.tostring()) then addDatetoList

4)私が見落とした別の方法はありますか?これらはすべて完全に非効率的ですか?

計算は vb.net コードビハインドで行う必要があります。それ以外の場合は、SQL サーバーを使用して計算することを考えます。

編集

関数のタイミングの演習として、私はすぐに以下を構築して、私の方法や他の方法を試してみました。

Dim DaysOfWeek As String = "Monday,Tuesday,Friday"
        Dim startDate As DateTime = DateTime.Now
        Dim endDate As DateTime = DateTime.Now.AddDays(365)
        Dim dateList As New List(Of DateTime)()

        Dim method1StopWatch As New Stopwatch
        Dim method2StopWatch As New Stopwatch
        Dim method3StopWatch As New Stopwatch
        Dim method4StopWatch As New Stopwatch

        Dim executionStart As Long
        Dim executionEnd As Long
        Dim noDays As Integer = 0

        'Method3

        method3StopWatch.Start()
        Do While (startDate < endDate)
            If DaysOfWeek.Contains(startDate.DayOfWeek.ToString()) Then
                dateList.Add(startDate)
                noDays = noDays + 1
            End If
            startDate = startDate.AddDays(1)

        Loop
        method3StopWatch.Stop()
        Label3.Text = method3StopWatch.ElapsedTicks.ToString() & " " & noDays & " Days"

        noDays = 0
        dateList.Clear()
        startDate = DateTime.Now

        'Method2

        method2StopWatch.Start()

        Do While (startDate < endDate)
            Select startDate.DayOfWeek.ToString()
                Case "Monday"
                    If DaysOfWeek.Contains("Monday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
                Case "Tuesday"
                    If DaysOfWeek.Contains("Monday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
                Case "Wednesday"
                    If DaysOfWeek.Contains("Wednesday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
                Case "Thursday"
                    If DaysOfWeek.Contains("Thursday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
                Case "Friday"
                    If DaysOfWeek.Contains("Friday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
                Case "Saturday"
                    If DaysOfWeek.Contains("Saturday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
                Case "Sunday"
                    If DaysOfWeek.Contains("Sunday") Then
                        dateList.Add(startDate)
                        noDays = noDays + 1
                    End If
            End Select
            startDate = startDate.AddDays(1)

        Loop
        method2StopWatch.Stop()
        Label2.Text = (method2StopWatch.ElapsedTicks).ToString() & " " & noDays & " Days"

        noDays = 0
        dateList.Clear()
        startDate = DateTime.Now

        method1StopWatch.Start()
        For Each Day As String In DaysOfWeek.Split(CChar(","))
            Do While (startDate < endDate)
                If startDate.DayOfWeek.ToString() = Day Then
                    noDays = noDays + 1
                    dateList.Add(startDate)
                End If
                startDate = startDate.AddDays(1)
            Loop
            startDate = DateTime.Now
        Next


        method1StopWatch.Stop()
        Label1.Text = (method1StopWatch.ElapsedTicks).ToString() & " " & noDays & " Days"


        noDays = 0
        dateList.Clear()
        startDate = DateTime.Now

        method4StopWatch.Start()
        Dim daysList As New List(Of DayOfWeek)()
        daysList.Add(DayOfWeek.Monday)
        daysList.Add(DayOfWeek.Tuesday)
        daysList.Add(DayOfWeek.Friday)
        Dim datesList As List(Of Date) = GetDayOfWeekDates(startDate, endDate, daysList)
        method4StopWatch.Stop()
        Label4.Text = (method4StopWatch.ElapsedTicks).ToString() & " " & datesList.Count & " Days"

    End Sub

    Public Function GetDayOfWeekDates(startDate As Date, endDate As Date, daysOfWeek As List(Of DayOfWeek)) As List(Of Date)
        Dim liReturn As New List(Of Date)()
        Dim currDay As Date = startDate
        While currDay <= endDate
            If daysOfWeek.Contains(currDay.Date.DayOfWeek) Then
                liReturn.Add(currDay.Date)
            End If
            currDay = currDay.AddDays(1)
        End While
        Return liReturn
    End Function

その結果、月、火、金の次の出力 (経過ティック数) が得られました。

Method 1 : 10650 - 157 Days
Method 2 : 4152 - 157 Days
Method 3 : 4084 - 157 Days
Method 4 : 179 - 157 Days

これはまた、私の方法とロジックが貧弱だったことを意味します。

4

2 に答える 2

3

これが私のやり方です。それが可能な限り最もパフォーマンスの高い方法かどうかはわかりませんが、私の意見では、これが最も読みやすく理解しやすいコードであり、大きな日付範囲をチェックしている場合でも非常に迅速に実行されるはずです。

Public Function GetDayOfWeekDates(startDate As Date, endDate As Date, daysOfWeek As List(Of DayOfWeek)) As List(Of Date)
    Dim liReturn As New List(Of Date)()
    Dim currDay As Date = startDate
    While currDay <= endDate
        If daysOfWeek.Contains(currDay.Date.DayOfWeek) Then
            liReturn.Add(currDay.Date)
        End If
        currDay = currDay.AddDays(1)
    End While
    Return liReturn
End Function

チェックする DayOfWeek のリストを作成して、関数を呼び出します。

    Dim daysList As New List(Of DayOfWeek)()
    daysList.Add(DayOfWeek.Monday)
    daysList.Add(DayOfWeek.Friday)
    Dim datesList As List(Of Date) = GetDayOfWeekDates(New Date(2012, 1, 1), New Date(2012, 12, 31), daysList)

この例では、結果はList(Of Date)のdatesListになります。

編集: Web サービスが曜日のカンマ区切りのリストを受け取るとおっしゃっていたことに気付きました。私の意見では、リストが無効な場合に Web サービスを介してエラーが返されるコード内のレベルで、別の関数でリストを解析する必要があります。日付のリストを検索する関数は、上で示したように厳密に型指定されたパラメーターを受け取る必要があります。

于 2013-01-08T14:59:09.900 に答える
2

編集2

更新された関数をテストしたところ、単純化できたので、少し速くなるはずです。

Public Shared Function DaysOfWeekRange(
    ByVal startDateTime As DateTime, _
    ByVal endDateTime As DateTime, _
    ByVal daysOfWeek As ISet(Of DayOfWeek)) As IEnumerable(Of DateTime)

    If (endDateTime < startDateTime) Then
        Return Enumerable.Empty(Of DateTime)()
    End If

    Dim startDate = startDateTime.Date
    Dim endDate = endDateTime.Date

    Dim firstDate As DateTime?
    Dim lastDate As DateTime
    Dim increment = 0
    Dim increments = New List(Of Integer)(7)

    ' Get Increments
    For d = 0 To 6
       If firstDate.HasValue Then
           increment += 1
           Dim day = startDate.AddDays(d)
           If daysOfWeek.Contains(day.DayOfWeek) Then
               lastDate = day
               increments.Add(increment)
               increment = 0
           End If           
       Else
           Dim possibleFirst = startDate.AddDays(d)
           If daysOfWeek.Contains(possibleFirst.DayOfWeek) Then
               firstDate = possibleFirst
               lastDate = firstDate
           End If
       End If
    Next

    If Not firstDate.HasValue Then
        Return Enumerable.Empty(Of DateTime)()
    End If

    ' Add loop back increment
    increments.Add((7 - lastDate.DayOfWeek) + firstDate.Value.DayOfWeek)

    ' Prepare iteration
    Dim wholeWeeks = Math.Floor(endDate.Subtract(firstDate).TotalDays() /7)
    Dim results = new List(Of DateTime)((wholeWeeks + 1) * daysOfWeek.Count)
    Dim thisDate = firstDate

    ' Whole Weeks
    For i = 1 To wholeWeeks Step 1
        For Each increment In increments
            results.Add(thisDate)
            thisDate = thisDate.AddDays(increment)
        Next
    Next

    ' Last Partial Week
    If (thisDate <= endDate) Then
        For Each increment In Increments
            results.Add(thisDate)
            thisDate = thisDate.AddDays(increment)
            If (thisDate > endDate) Then
                Exit For
            End If
        Next
    End if

    Return results
End Function

より効率的な方法を考えることができますが、それには の実装が含まれます。Visual Basic に同等のものEnumerator(Of DateTime)がないのは残念です。単純で効率的なオプションがあるでしょう。yield returnオプション希望の方Enumeratorはご相談ください。

とにかく、この関数は書くのが簡単で、遅すぎてはいけません。

Public Shared Function DaysOfWeekRange(
    ByVal startDateTime As DateTime, _
    ByVal endDateTime As DateTime, _
    ByVal daysOfWeek As ISet(Of DayOfWeek)) As IEnumerable(Of DateTime)

    If (endDateTime < startDateTime) Then
        Return Enumerable.Empty(Of DateTime)()
    End If

    Dim startDate = startDateTime.Date
    Dim endDate = endDateTime.Date

    Return Enumerable.Range(0, endDate.Subtract(startDate).TotalDays) _
        .Select(Function(i) startDate.AddDays(i)) _
        .Where(Function(day) daysOfWeek.Contains(day.DayOfWeek))
End Function

編集

このアプローチでは、前もって増分のサイクルを計算し、チェックなしで全体の週数を繰り返します。次に、小切手で最後の部分的な週をインクリメントします。範囲が十分に広い場合、これにはパフォーマンス上の利点があるはずです。

Public Shared Function DaysOfWeekRange(
    ByVal startDateTime As DateTime, _
    ByVal endDateTime As DateTime, _
    ByVal daysOfWeek As ISet(Of DayOfWeek)) As IEnumerable(Of DateTime)

    If (endDateTime < startDateTime) Then
        Return Enumerable.Empty(Of DateTime)()
    End If

    Dim startDate = startDateTime.Date
    Dim endDate = endDateTime.Date

    Dim firstDate = Enumerable.Range(0, 6) _
        .Select(Function(i) startDate.AddDays(i)) _
        .First(Function(d) daysOfWeek.Contains(d.DayOfWeek))

    Dim firstDayOfWeek = firstDate.DayOfWeek
    Dim offsets = New SortedSet(Of Integer)()
    For Each dayOfWeek in daysOfWeek
       Dim offset = dayOfWeek - firstDayOfWeek

       Select Case offset
           Case Is > 0
               offsets.Add(offset)
           Case Is < 0
               offsets.Add(7 + offset)
       End Select
    Next

    Dim increments = New List(Of Integer)()
    Dim lastOffset = 0
    For Each offset In offsets
        increments.Add(offset - lastOffset)
        lastOffset = offset
    Next
    increments.Add(7 - lastOffset)

    Dim results = new List(Of DateTime)()
    Dim wholeWeeks = Math.Floor(endDate.Subtract(firstDate).TotalDays() /7)
    Dim thisDate = firstDate

    'Whole Weeks
    For i = 1 To wholeWeeks Step 1
        For Each increment In increments
            results.Add(thisDate)
            thisDate = thisDate.AddDays(increment)
        Next
    Next

    'Last Partial Week
    If (thisDate <= endDate) Then
        For Each increment In Increments
            results.Add(thisDate)
            thisDate = thisDate.AddDays(increment)
            If (thisDate > endDate) Then
                Exit For
            End If
        Next
    End if

    Return results
End Function

より多くのコードのように見えますが、ほとんどの場合、前もって準備することで全体的な作業が少なくなると思います。

于 2013-01-08T15:37:22.897 に答える