增加天数避开周末

Adding days avoiding weekends

我有3个DateTimePicker,一个设置日期,第二个加3天,第三个加5个工作日。但是我的代码计算不正确。 我计算 "manually" 未来的日子会根据当天增加或减少天数。

  Private Sub Calculatedelivery()
    Dim normaldlvy As Integer
    Dim latedlvy As Integer
    If DateTimePicker1.Value.DayOfWeek = Day.Monday Then
        normaldlvy = 3
        latedlvy = 7
    End If
    If DateTimePicker1.Value.DayOfWeek = Day.Tuesday Then
        normaldlvy = 3
        latedlvy = 6
    End If
    If DateTimePicker1.Value.DayOfWeek = Day.Wednesday Then
        normaldlvy = 5
        latedlvy = 7
    End If
    If DateTimePicker1.Value.DayOfWeek = Day.Thursday Then
        normaldlvy = 5
        latedlvy = 7
    End If
    If DateTimePicker1.Value.DayOfWeek = Day.Friday Then
        normaldlvy = 5
        latedlvy  = 7
    End If
    If DateTimePicker1.Value.DayOfWeek = Day.Saturday Then
        normaldlvy = 4
        latedlvy = 6
    End If
    If DateTimePicker1.Value.DayOfWeek = Day.Sunday Then
        normaldlvy = 3
        latedlvy = 5
    End If
    DateTimePicker2.Value = DateTimePicker1.Value.AddDays(normaldlvy )
    DateTimePicker3.Value = DateTimePicker1.Value.AddDays(latedlvy)
End Sub

这是一个扩展方法,您可以像调用它一样调用很多方法AddDays

Imports System.Runtime.CompilerServices

Public Module DateTimeExtensions

    <Extension>
    Public Function AddWeekDays(source As Date, value As Integer) As Date
        Dim result = source

        Do
            result = result.AddDays(1)

            If result.IsWeekDay() Then
                value -= 1
            End If
        Loop Until value = 0

        Return result
    End Function

    <Extension>
    Public Function IsWeekDay(source As Date) As Boolean
        Return source.DayOfWeek <> DayOfWeek.Saturday AndAlso
               source.DayOfWeek <> DayOfWeek.Sunday
    End Function

End Module

然后你可以这样称呼它:

DateTimePicker3.Value = DateTimePicker1.Value.AddWeekDays(latedlvy)

请注意,与 Date.AddDays 不同,该方法采用 Integer 而不是 Double。此外,它只适用于正值。它可以改进为几乎完全按照 AddDays 的方式工作,但在这种情况下你可能不需要它。

如果您不确定扩展方法的工作原理,我建议您阅读有关该主题的文章。

编辑:我已经对这种方法做了一些工作并对其进行了显着改进。它现在可以处理负值和小数值,就像 Date.AddDays 一样。

Imports System.Runtime.CompilerServices

''' <summary>
''' Contains methods that extend the <see cref="DateTime"/> structure.
''' </summary>
Public Module DateTimeExtensions

    ''' <summary>
    ''' Gets a value indicating whether a <see cref="DateTime"/> value represents a week day.
    ''' </summary>
    ''' <param name="source">
    ''' The input <see cref="DateTime"/>, which acts as the <b>this</b> instance for the extension method.
    ''' </param>
    ''' <returns>
    ''' <b>true</b> if the represents a week day; otherwise <b>false</b>.
    ''' </returns>
    ''' <remarks>
    ''' All days other than Saturday and Sunday are considered week days.
    ''' </remarks>
    <Extension>
    Public Function IsWeekDay(source As Date) As Boolean
        Return source.DayOfWeek <> DayOfWeek.Saturday AndAlso
               source.DayOfWeek <> DayOfWeek.Sunday
    End Function

    ''' <summary>
    ''' Returns a new <see cref="DateTime"/> that adds the specified number of week days to a specified value.
    ''' </summary>
    ''' <param name="source">
    ''' The input <see cref="DateTime"/>, which acts as the <b>this</b> instance for the extension method.
    ''' </param>
    ''' <param name="value">
    ''' A number of whole and fractional days. The <i>value</i> parameter can be negative or positive.
    ''' </param>
    ''' <returns>
    ''' An object whose value is the sum of the date and time represented by this instance and the number of week days represented by <i>value</i>.
    ''' </returns>
    ''' <remarks>
    ''' All days other than Saturday and Sunday are considered week days.
    ''' </remarks>
    <Extension>
    Public Function AddWeekDays(source As Date, value As Double) As Date
        'A unit will be +/- 1 day.
        Dim unit = Math.Sign(value) * 1.0

        'Start increasing the date by units from the initial date.
        Dim result = source

        'When testing for zero, allow a margin for precision error.
        Do Until Math.Abs(value) < 0.00001
            If Math.Abs(value) < 1.0 Then
                'There is less than one full day to add so we need to see whether adding it will take us past midnight.
                Dim temp = result.AddDays(value)

                If temp.Date = result.Date OrElse temp.IsWeekDay() Then
                    'Adding the partial day did not take us into a weekend day so we're done.
                    result = temp
                    value = 0.0
                Else
                    'Adding the partial day took us into a weekend day so we need to add another day.
                    result = result.AddDays(unit)
                End If
            Else
                'Add a single day.
                result = result.AddDays(unit)

                If result.IsWeekDay() Then
                    'Adding a day did not take us into a weekend day so we can reduce the remaining value to add.
                    value -= unit
                End If
            End If
        Loop

        Return result
    End Function

End Module

这是执行此操作的另一种方法。我认为它相当直接 - 特别是如果您只计算几次。

Dim someDate As DateTime = DateTime.Now.Date

Dim dates = _
        Enumerable _
            .Range(1, 100000) _
            .Select(Function (x) someDate.AddDays(x)) _
            .Where(Function (x) x.DayOfWeek <> DayOfWeek.Saturday) _
            .Where(Function (x) x.DayOfWeek <> DayOfWeek.Sunday)

Dim nextDate = dates.Take(5).First()

这种方法的一大优势是您还可以添加更多 .Where 调用以删除 public 假期。

简单。