【问题标题】:adding workdays to date taking into account weekends and holidays考虑到周末和节假日,添加迄今为止的工作日
【发布时间】:2010-12-08 04:47:07
【问题描述】:

给定日期和假期列表,我如何将给定的工作日数添加到该日期?对于不考虑假期的小问题,有很多解决方案(例如参见Adding Days to a Date but Excluding Weekends)。

编辑:我正在寻找 O(1) 或至少线性(关于假期数)的解决方案。

请帮忙 谢谢 康斯坦丁

【问题讨论】:

    标签: c# .net date


    【解决方案1】:

    接近 O(1)(没有初始化时间)

    如果你真的想要一个 O(1) 的解决方案,我希望你不要考虑初始化问题。

    初始化:

    • 构建可查询范围内有效返回值的所有日期的排序列表。 (使用类似于 Rik Garner 答案中的代码)
    • 使用上述列表中的日期构建哈希表,日期为键,列表中的索引为值

    初始化代码,你只需要一次,你缓存结果。

    查询/计算

     List<DateTime> validWorkdays = // ;
     Dictionary<DateTime, int> lookupIndexOfValidWorkday = // ;
    
     DateTime AddWorkdays(DateTime start, int count) {
        var startIndex = lookupIndexOfValidWorkday[start];
        return validWorkDays[startIndex + count];
     }
    

    Concerning the retrieve from the dictionary:

    获取或设置此属性的值接近 O(1) 操作。

    O(n) 节假日数

    假设假期列表从最旧到最新排序。 (Credits for weekdays formula)

    DateTime AddBusinessDay(DateTime start, int count, IEnumerable<DateTime> holidays) {
       int daysToAdd = count + ((count/ 5) * 2) + ((((int)start.DayOfWeek + (count % 5)) >= 5) ? 2 : 0);
       var end = start.AddDays(daysToAdd);
       foreach(var dt in holidays) {
         if (dt >= start && dt <= end) {
            end = end.AddDays(1);
            if (end.DayOfWeek == DayOfWeek.Saterday) {
              end = end.AddDays(2);
            }
         }
       }
       return end;
    }    
    

    这个方法可以优化。

    很难创建一个仅计算结果的简单公式,例如您链接到的问题中的答案。因为当您调整假期时,您需要考虑可能属于您范围的新假期。对于周末,您知道它们之间有固定的间隔。

    【讨论】:

    • O(n)方案有个bug:调整后需要检查新的结束日期是否在周末。
    • 是的,没想到那个。
    • 你不必在 O(n) 解决方案中强制转换 DayOfWeek:(int)start.DayOfWeek
    【解决方案2】:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    
    namespace DateThing
    {
        class Program
        {
            static void Main(string[] args)
            {
                var holidays = new List<DateTime>()
                                   {
                                       new DateTime(2010, 12, 25),
                                       new DateTime(2010, 12, 26)
                                   };
    
    
                var futureDate = CalculateFutureDate(DateTime.Today, 20, holidays);
    
            }
    
            static DateTime CalculateFutureDate(DateTime fromDate, int numberofWorkDays, ICollection<DateTime> holidays)
            {
                var futureDate = fromDate;
    
                for (var i = 0; i < numberofWorkDays; i++ )
                {
                    if (futureDate.DayOfWeek == DayOfWeek.Saturday || futureDate.DayOfWeek == DayOfWeek.Sunday ||
                        (holidays != null && holidays.Contains(futureDate)))
                        futureDate = futureDate.AddDays(1); // Increase FutureDate by one because of condition
    
                    futureDate = futureDate.AddDays(1); // Add a working day
                }
                return futureDate;
            }
        }
    }
    

    【讨论】:

    • 谢谢。我已经编辑了我的问题以澄清我在寻找什么。
    • 您不应该需要“while”而不是“if”吗?跳过周六 - 周日 - 假期组合。
    【解决方案3】:

    试试这个……

     private DateTime CalculateFutureDate(DateTime fromDate, int numberofWorkDays, ICollection<DateTime> holidays)
        {
            var futureDate = fromDate;
            var daterange = Enumerable.Range(1, numberofWorkDays * 2);
            var dateSet = daterange.Select (d => futureDate.AddDays(d));
            var dateSetElim = dateSet.Except(holidays).Except(dateSet.Where( s =>s.DayOfWeek == DayOfWeek.Sunday).Except(dateSet.Where  (s=>s.DayOfWeek==DayOfWeek.Saturday) ));
    
            //zero-based array
            futureDate = dateSetElim.ElementAt(numberofWorkDays-1);
            return futureDate;
        }
    

    【讨论】:

    • 谢谢。假设假期集合包含 numberofWorkDays * 2 个假期并排从 fromDate 开始...
    • 您可以调整Range中的值以适应;在我从我们那里得到的代码中,我们总是添加很多工作日,即 numberOfWorkDays >> 假期 [在 from 和 to 之间]。在你的情况下,你可以做 (numberOfWorkDays*2)+holidays.Count 吗?
    【解决方案4】:

    //下面的代码正在运行...如果您有任何问题,请告诉我

    DateTime AddBusinessDays(int noofDays, DateTime dtCurrent)
        {
            var holidays = new List<DateTime>() { new DateTime(2013, 10, 22), new DateTime(2013, 10, 28)};
    
            DateTime tempdt = new DateTime(dtCurrent.Year, dtCurrent.Month, dtCurrent.Day);
            // if starting day is non working day adjust to next working day
            tempdt = ExcludeNotWorkingDay(tempdt, holidays);
    
            // if starting day is non working day adjust to next working day then minus 1 day in noofadding days
            if (tempdt.Date > dtCurrent.Date && !(noofDays == 0))
                noofDays = noofDays - 1;
    
            while (noofDays > 0)
            {   
                tempdt = tempdt.AddDays(1);
                // if day is non working day adjust to next working day
                tempdt = ExcludeNotWorkingDay(tempdt, holidays);
                noofDays = noofDays - 1;
            }
    
            return tempdt;
        }
        DateTime ExcludeNotWorkingDay(DateTime dtCurrent, List<DateTime> holidays)
        {
            while (!IsWorkDay(dtCurrent, holidays))
            {
                dtCurrent = dtCurrent.AddDays(1);
            }
            return dtCurrent;
        }
        bool IsWorkDay(DateTime dtCurrent, List<DateTime> holidays)
        {
            if ((dtCurrent.DayOfWeek == DayOfWeek.Saturday || dtCurrent.DayOfWeek == DayOfWeek.Sunday ||
                 holidays.Contains(dtCurrent)))
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    

    【讨论】:

      【解决方案5】:

      可能是这样的

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      namespace DateThing
      {
          class Program
          {
              static void Main(string[] args)
              {
                  var holidays = new List<DateTime>()
                                     {
                                         new DateTime(2010, 12, 25),
                                         new DateTime(2010, 12, 26)
                                     };
      
      
                  var workDays = GetNumberOfWorkDays(DateTime.Today, new DateTime(2011, 1, 1), holidays);
              }
      
              static int GetNumberOfWorkDays(DateTime fromDate, DateTime toDate, ICollection<DateTime> holidays)
              {
                  var days = 0;
                  for (var i = fromDate; i < toDate; )
                  {
                      if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday && 
                          (holidays != null && !holidays.Contains(i)))
                          days++;
      
                      i = i.AddDays(1);
                  }
                  return days;
              }
          }
      
      
      }
      

      【讨论】:

      • 我正在寻找一种可以逆转的方法:fromDate + GetNumberOfWorkDays 应该产生 toDate。
      猜你喜欢
      • 1970-01-01
      • 2017-04-06
      • 1970-01-01
      • 1970-01-01
      • 2019-04-05
      • 1970-01-01
      • 2012-06-10
      • 1970-01-01
      • 2021-10-02
      相关资源
      最近更新 更多