【问题标题】:calculate date add, but only weekdays计算日期添加,但只有工作日
【发布时间】:2010-12-06 20:51:30
【问题描述】:

我想简单地使用内置的 dateadd 函数来计算一个新的日期,但要考虑到只应计算工作日(或者可以说是“工作日”)。

我想出了这个简单的算法,它不关心假期等。我已经用一些简单的日期对此进行了测试,但如果可以以更好的方式完成,我希望得到一些输入。

此示例假设一周有 5 个工作日(周一至周五),其中一周的第一天是周一。此处使用的日期格式为 d-m-yyyy,示例以 2009 年 10 月 1 日的开始日期计算。

这是简单的形式:

Dim d_StartDate As DateTime = "1-10-2009"
Dim i_NumberOfDays As Integer = 12
Dim i_CalculateNumberOfDays As Integer 

If i_NumberOfDays > (5 - d_StartDate.DayOfWeek) Then
    i_CalculateNumberOfDays = i_NumberOfDays
Else
    i_CalculateNumberOfDays = i_NumberOfDays + (Int(((i_NumberOfDays + (7 - d_StartDate.DayOfWeek)) / 5)) * 2)
End If

MsgBox(DateAdd(DateInterval.Day, i_CalculateNumberOfDays, d_StartDate))

我尝试用以下代码来解释:

''' create variables to begin with
Dim d_StartDate as Date = "1-10-2009"
Dim i_NumberOfDays as Integer = 5

''' create var to store number of days to calculate with
Dim i_AddNumberOfDays as Integer 


''' check if amount of businessdays to add exceeds the 
''' amount of businessdays left in the week of the startdate
If i_NumberOfDays > (5 - d_StartDate.DayOfWeek) Then


    ''' start by substracting days in week with the current day,
    ''' to calculate the remainder of days left in the current week
    i_AddNumberOfDays = 7 - d_StartDate.DayOfWeek

    ''' add the remainder of days in this week to the total
    ''' number of days we have to add to the date
    i_AddNumberOfDays += i_NumberOfDays

    ''' divide by 5, because we need to know how many 
    ''' business weeks we are dealing with
    i_AddNumberOfDays = i_AddNumberOfDays / 5

    ''' multiply the integer of current business weeks by 2
    ''' those are the amount of days in the weekends we have 
    ''' to add to the total
    i_AddNumberOfDays = Int(i_AddNumberOfDays) * 2

    ''' add the number of days to the weekend days
    i_AddNumberOfDays += i_NumberOfDays

Else

    ''' there are enough businessdays left in this week
    ''' to add the given amount of days
    i_AddNumberOfDays = i_NumberOfDays

End If

''' this is the numberof dates to calculate with in DateAdd
dim d_CalculatedDate as Date
d_CalculatedDate = DateAdd(DateInterval.Day, i_AddNumberOfDays, d_StartDate)

提前感谢您的 cmets 和对此的投入。

【问题讨论】:

  • 当正确的结果是 2009 年 1 月 27 日时,我认为您的算法在 10-jan-2009 (26-jan-2009) 没有给出正确的结果。
  • 确实如此。我忘了提到我在这里使用 dd-mm-yyyy 格式。所以当我编译它说 1-oktober-2009。对不起,会改变问题。

标签: vb.net date


【解决方案1】:

我使用 .DayOfWeek 函数来检查是否是周末。这不包括假期实施。它已经过测试。我意识到这个问题很老,但接受的答案没有用。但是,我确实喜欢它的干净程度,所以我想我会更新它并发布。我确实更改了 while 循环中的逻辑。

Function AddBusinessDays(startDate As Date, numberOfDays As Integer) As Date
    Dim newDate As Date = startDate
    While numberOfDays > 0
        newDate = newDate.AddDays(1)

        If newDate.DayOfWeek() > 0 AndAlso newDate.DayOfWeek() < 6 Then '1-5 is Mon-Fri
            numberOfDays -= 1
        End If

    End While
    Return newDate
End Function

【讨论】:

    【解决方案2】:
    Public Shared Function AddBusinessDays(ByVal startDate As DateTime, _
                                           ByVal businessDays As Integer) As DateTime
        Dim di As Integer
        Dim calendarDays As Integer
    
        '''di: shift to Friday. If it's Sat or Sun don't shift'
        di = (businessDays - Math.Max(0, (5 - startDate.DayOfWeek)))
    
        ''' Start = Friday -> add di/5 weeks -> end = Friday'
        ''' -> if the the remaining (<5 days) is > 0: add it + 2 days (Sat+Sun)'
        ''' -> shift back to initial day'
        calendarDays = ((((di / 5) * 7) _
                       + IIf(((di Mod 5) <> 0), (2 + (di Mod 5)), 0)) _
                       + (5 - startDate.DayOfWeek))
    
        Return startDate.AddDays(CDbl(calendarDays))
    
    End Function
    

    【讨论】:

      【解决方案3】:

      您的计划似乎应该可行。确保将其包装在一个函数中,而不是在使用它的每个地方都进行计算,这样如果/当您发现需要考虑假期时,就不必在很多地方进行更改。

      我能想到的实现假期支持的最佳方式是在循环中一次添加第一天。每次迭代,检查它是周末还是假期,如果是添加另一天并继续(跳过它)。下面是一个伪代码示例(我不知道 VB);不保证其正确。当然,您需要为 isWeekend() 和 isHoliday() 提供自己的实现。

      function addBusinessDays(startDate, numDays)
      {
          Date newDate = startDate;
          while (numDays > 0)
          {
               newDate.addDays(1);
               if (newDate.isWeekend() || newDate.isHoliday())
                   continue;
      
               numDays -= 1;
          }
          return newDate;
      }
      

      我对假期的第一个想法是简单地查找开始日期和结束日期之间的假期数并将其添加到您的计算中,但这当然行不通,因为结束日期取决于该时间间隔内的假期数。我认为迭代解决方案是假期中最好的解决方案。

      【讨论】:

      • 我同意你的观点,不只是简单地添加假期数量(例如,如果假期在周末怎么办?)。感谢您的功能(确实,这些东西应该始终放在一个功能中)。
      • 仅供参考,newDate.addDays(1);应该是 newDate = newDate.addDays(1); addDays(1) 返回日期 + 1,但不更新 newDate 本身。
      【解决方案4】:

      我正在使用此代码来计算日期:

          dayOfWeek = startDate.DayOfWeek
          weekendDays = 2 * Math.Floor((dayOfWeek + businessDays - 0.1) / 5)
          newDate = startDate.AddDays(businessDays + weekendDays)
      

      第二行计算我们必须添加的完整周末数,然后将它们乘以 2 以获得天数。

      如果 (dayOfWeek + businessDays) 是 5 的倍数,并且最终日期是星期五,则使用附加的 -0.1 常量来避免添加天数。

      【讨论】:

        【解决方案5】:
        Private Function AddBusinessDays(ByVal dtStartDate As DateTime, ByVal intVal As Integer) As DateTime
        
            Dim dtTemp As DateTime = dtStartDate
        
            dtTemp = dtStartDate.AddDays(intVal)
        
            Select Case dtTemp.DayOfWeek
                Case 0, 6
                    dtTemp = dtTemp.AddDays(2)
            End Select
        
            AddBusinessDays = dtTemp
        
        End Function
        

        【讨论】:

        • 我不确定这些解决方案是否能正确处理超过 5 个工作日的添加
        【解决方案6】:

        请检查此代码以添加工作日

        Dim strDate As Date = DateTime.Now.Date
        Dim afterAddDays As Date
        Dim strResultDate As String
        Dim n As Integer = 0
        
        For i = 1 To 15
            afterAddDays = strDate.AddDays(i)
            If afterAddDays.DayOfWeek = DayOfWeek.Saturday Or afterAddDays.DayOfWeek = DayOfWeek.Sunday Then
                n = n + 1
            End If
        Next
        
        strResultDate = afterAddDays.AddDays(n).ToShortDateString()
        

        【讨论】:

          【解决方案7】:
          Private Function AddXWorkingDays(noOfWorkingDaysToAdd As Integer) As Date
          
              AddXWorkingDays = DateAdd(DateInterval.Weekday, noOfWorkingDaysToAdd + 2, Date.Today)
              If Weekday(Today) + noOfWorkingDaysToAdd < 6 Then AddXWorkingDays = DateAdd(DateInterval.Weekday, 2, Date.Today)
          
          End Function
          

          【讨论】:

          • 欢迎来到 Stack Overflow!虽然这可能是一个有效的答案,但您更有可能通过解释代码的作用和工作原理来帮助他人。仅代码的答案往往受到较少的积极关注,并且不如其他答案有用。
          【解决方案8】:

          一种方法是从开始日期开始迭代,如果日期不是周六或周日,则每次迭代都会增加或减少一天。如果将零作为 iAddDays 传递,则该函数将返回 dDate,即使该日期是星期六或星期日。如果这种情况是可能的,您可以使用逻辑来获得您正在寻找的结果。

           Public Function DateAddWeekDaysOnly(ByVal dDate As DateTime, ByVal iAddDays As Int32) As DateTime
              If iAddDays <> 0 Then
                  Dim iIncrement As Int32 = If(iAddDays > 0, 1, -1)
                  Dim iCounter As Int32
          
                  Do
                      dDate = dDate.AddDays(iIncrement)
                      If dDate.DayOfWeek <> DayOfWeek.Saturday AndAlso dDate.DayOfWeek <> DayOfWeek.Sunday Then iCounter += iIncrement
                  Loop Until iCounter = iAddDays
              End If
          
              Return dDate
          End Function
          

          【讨论】:

            【解决方案9】:

            简单的方法

            function addWerktage($date,$tage){
                for($t=0;$t<$tage;$t++){
                    $date   = $date + (60*60*24);
                    if(date("w",$date) == 0 || date("w",$date) == 6){ $t--; }
                }
                return $date;
            }
            

            【讨论】:

              【解决方案10】:

              这会产生与接受的答案相同的结果,包括从周末开始,同时处理负偏移量并且不循环。它是用 C# 编写的,但应该适用于数字工作日从星期日开始到星期六结束并且整数除法四舍五入到 0 的任何环境。

              public static DateTime AddWeekdays(DateTime date, int offset)
              {
                  if (offset == 0)
                  {
                      return date;
                  }
                  // Used to calculate the number of weekend days skipped over
                  int weekends;
                  if (offset > 0)
                  {
                      if (date.DayOfWeek == DayOfWeek.Saturday)
                      {
                          // Monday is 1 weekday away, so it will take an extra day to reach the next weekend
                          int daysSinceMonday = -1;
                          // Add two weekends for every five days
                          int normalWeekends = (offset + daysSinceMonday) / 5 * 2;
                          // Add one for this Sunday
                          int partialWeekend = 1;
                          weekends = normalWeekends + partialWeekend;
                      }
                      else
                      {
                          // It will take this many fewer days to reach the next weekend.
                          // Note that this works for Sunday as well (offset -1, same as above)
                          int daysSinceMonday = date.DayOfWeek - DayOfWeek.Monday;
                          // Add two weekends for every five days (1 business week)
                          weekends = (offset + daysSinceMonday) / 5 * 2;
                      }
                  }
                  else
                  {
                      // Same as the positive offset case, but counting backwards.
                      // daysSinceFriday will be 0 or negative, except for Saturday, which is +1
                      int daysSinceFriday = date.DayOfWeek - DayOfWeek.Friday;
                      weekends = date.DayOfWeek == DayOfWeek.Sunday
                          ? (offset + 1) / 5 * 2 - 1
                          : (offset + daysSinceFriday) / 5 * 2;
                  }
                  return date.AddDays(offset + weekends);
              }
              

              由于每 5 天额外 2 天的模式会在一周后重复,因此您可以有效地对其进行详尽的测试:

              private static DateTime AddWeekdaysLooping(DateTime date, int offset)
              {
                  DateTime newDate = date;
                  int step = Math.Sign(offset);
                  while (offset != 0)
                  {
                      newDate = newDate.AddDays(step);
                      if (newDate.DayOfWeek != DayOfWeek.Sunday && newDate.DayOfWeek != DayOfWeek.Saturday)
                      {
                          offset -= step;
                      }
                  }
                  return newDate;
              }
              
              void TestWeekdays()
              {
                  DateTime initial = new DateTime(2001, 1, 1);
                  for (int day = 0; day < 7; day += 1)
                  {
                      for (int offset = -25; offset <= 25; offset += 1)
                      {
                          DateTime start = initial.AddDays(day);
                          DateTime expected = AddWeekdaysLooping(start, offset);
                          DateTime actual = AddWeekdays(start, offset);
                          if (expected != actual) {
                              throw new Exception($"{start.DayOfWeek} + {offset}: expected {expected:d}, but got {actual:d}");
                          }
                      }
                  }
              }
              

              【讨论】:

                【解决方案11】:
                Dim result As Date
                    result = DateAdd("d", 2, Today)
                    If result.DayOfWeek = DayOfWeek.Saturday Then
                        result = DateAdd("d", 2, result)
                        MsgBox(result)
                    ElseIf result.DayOfWeek = DayOfWeek.Sunday Then
                        result = DateAdd("d", 1, result)
                        MsgBox(result)
                    ElseIf result.DayOfWeek = DayOfWeek.Monday Then
                        MsgBox(result)
                    ElseIf result.DayOfWeek = DayOfWeek.Tuesday Then
                        MsgBox(result)
                    ElseIf result.DayOfWeek = DayOfWeek.Wednesday Then
                        MsgBox(result)
                    ElseIf result.DayOfWeek = DayOfWeek.Thursday Then
                        MsgBox(result)
                    ElseIf result.DayOfWeek = DayOfWeek.Friday Then
                        MsgBox(result)
                    End If
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-05-30
                  • 1970-01-01
                  • 2018-12-15
                  • 1970-01-01
                  • 2021-10-15
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-19
                  相关资源
                  最近更新 更多