【问题标题】:Counting regular working days in a given period of time计算给定时间段内的正常工作日
【发布时间】:2010-09-14 12:34:52
【问题描述】:

需要帮助。我需要计算给定日期期间的正常工作日,例如,在我们国家,周一到周五有 5 个正常工作日,然后在代码中,当我在计算中使用它时,我需要排除周六和周日。

我在 C# 中需要类似这样的算法:

    int GetRegularWorkingDays(DateTime startDate, DateTime endDate)
    {

       int nonWorkingDays = ((endDate - startDate) % 7) * 2;

       return (endDate - startDate) - nonWorkingDays;
    }

我知道我的草稿还有很长的路要走:(。提前致谢。=)

PS:请大家投票选出下面最好/最快/最有效的答案。谢谢 =)

【问题讨论】:

  • 你想如何度过假期?
  • 这也是一个问题,但我的解决方案只是覆盖了我的计算中的某些内容。我们国家的定期假期不断变化。而且我不想说,建立一个日历表只是为了维护它..

标签: c# algorithm datetime


【解决方案1】:

查看this example on Code Project,它使用了一种非常有效的方式,不涉及任何循环;)

它使用这个算法:

  1. 以周为单位计算时间跨度数。叫它吧,W。
  2. 从周数中减去第一周。 W= W-1
  3. 将周数乘以每个工作日的工作日数 星期。叫它,D。
  4. 找出指定时间段内的假期。叫它,H。
  5. 计算第一周的天数。就这样吧,SD。
  6. 计算上周的天数。就这样吧,ED。
  7. 总结所有的日子。 BD = D + SD + ED - H。

【讨论】:

  • 这真的很关键。如果您有 6 或 7(或 66 或 77)周的跨度,则无需跳过周六和周日,逐周迭代。第一周和最后一周是特殊情况,你不知道跨度是否包括工作日或周末,但大中间只是每周减少 5 个工作日的假期。这将为您节省大量计算量。
  • +1:我不确定,当这个解决方案基本上是 O(1) 时,为什么循环的东西都被投票赞成......不过,根据 OP 的问题,不需要第 4 步。
  • @Kate:没错,循环是浪费时间,因为你可以很容易地计算出中间的工作日数,它只是需要特别注意的开始和结束周
  • MacOS 将此答案中的 � 呈现为 ? 符号。我目前假设它是一个 + 符号,因为它表示“总结所有的日子”。
  • @agm1984 由于某种原因,它在 Windows 上也显示不正确!但不,这实际上是一个减号,因为您想扣除假期,因为它们不是工作日
【解决方案2】:

单线!

int workingDays = Enumerable.Range(0, Convert.ToInt32(endDate.Subtract(startDate).TotalDays)).Select(i=>new [] { DayOfWeek.Saturday, DayOfWeek.Sunday }.Contains(startDate.AddDays(i).DayOfWeek) ? 0 : 1).Sum();

或者更高效:

DayOfWeek currDay = startDate.DayOfWeek;
int nonWorkingDays = 0;

foreach(var i in Enumerable.Range(0, Convert.ToInt32(endDate.Subtract(startDate).TotalDays)))
{
     if(currDay == DayOfWeek.Sunday || currDay == DayOfWeek.Saturday) 
          nonWorkingDays++;
     if((int)++currDay > 6) currDay = (DayOfWeek)0;
}

【讨论】:

  • 我不称之为单行。我称之为很长的一行:-)
  • 这太棒了,但它的效率如何?谢谢,我的意思是性能下降
  • Mwah,如果您不做银行软件,它并不真正关心,但您正在构造一个数组和一个日期时间对象。检查我的编辑以获取 supa dupa 有效的方式。
  • 这比Mr.Steven对Linq的建议更快吗?
  • 是的,您不会为每次迭代创建一个新的DateTime 对象。
【解决方案3】:

我编写了一个类型扩展器,允许我在给定日期添加(或减去)工作日。也许这会对你有所帮助。效果很好,所以如果这对您有帮助,请为我的帖子投票。

    /// <summary>
    /// Adds weekdays to date
    /// </summary>
    /// <param name="value">DateTime to add to</param>
    /// <param name="weekdays">Number of weekdays to add</param>
    /// <returns>DateTime</returns>
    public static DateTime AddWeekdays(this DateTime value, int weekdays)
    {
        int direction = Math.Sign(weekdays);
        int initialDayOfWeek = Convert.ToInt32(value.DayOfWeek);

        //---------------------------------------------------------------------------
        // if the day is a weekend, shift to the next weekday before calculating
        if ((value.DayOfWeek == DayOfWeek.Sunday && direction < 0)
            || (value.DayOfWeek == DayOfWeek.Saturday && direction > 0))
        {
            value = value.AddDays(direction * 2);
            weekdays += (direction * -1); // adjust days to add by one
        }
        else if ((value.DayOfWeek == DayOfWeek.Sunday && direction > 0)
            || (value.DayOfWeek == DayOfWeek.Saturday && direction < 0))
        {
            value = value.AddDays(direction);
            weekdays += (direction * -1); // adjust days to add by one
        }
        //---------------------------------------------------------------------------

        int weeksBase = Math.Abs(weekdays / 5);
        int addDays = Math.Abs(weekdays % 5);

        int totalDays = (weeksBase * 7) + addDays;
        DateTime result = value.AddDays(totalDays * direction);

        //---------------------------------------------------------------------------
        // if the result is a weekend, shift to the next weekday
        if ((result.DayOfWeek == DayOfWeek.Sunday && direction > 0)
            || (result.DayOfWeek == DayOfWeek.Saturday && direction < 0))
        {
            result = result.AddDays(direction);
        }
        else if ((result.DayOfWeek == DayOfWeek.Sunday && direction < 0)
            || (result.DayOfWeek == DayOfWeek.Saturday && direction > 0))
        {
            result = result.AddDays(direction * 2);
        }
        //---------------------------------------------------------------------------

        return result;
    }

【讨论】:

  • 我有很多类型扩展器,我将它们放在 System 命名空间中,因此它们始终可用。我很高兴这可以帮助你——花了很长时间才弄好! :)
  • 这只是在.. 我喜欢扩展方法!
  • 先生,当我尝试从值中添加 3 个工作日时出现错误。当它是周六和周日时,答案应该是周三,但它会在周五返回...
  • 太棒了...正是我需要的。我对 .NET 和 C# 有点陌生,你能发布一个快速示例如何实现这个吗?我如何把它放在系统命名空间中? tnx
  • 要将其放入 System 命名空间,将其包装在 namespace System { ... } 中。一旦你有了它,你会看到它是一个日期时间的函数DateTime x = DateTime.Now.AddWeekdays(3);
【解决方案4】:

不是很快,但这会解决问题:

int GetRegularWorkingDays(DateTime start, DateTime end)
{
    return (
        from day in Range(start, end)
        where day.DayOfWeek != DayOfWeek.Saturday
        where day.DayOfWeek != DayOfWeek.Sunday
        select day).Count();
}

IEnumerable<DateTime> Range(DateTime start, DateTime end)
{
    while (start <= end)
    {
        yield return start;
        start = start.AddDays(1);
    }
}

【讨论】:

  • 谢谢先生,但我也需要最高效的,因为我将在平均 5000 行的 For 循环中使用它......谢谢......
【解决方案5】:

您可以尝试一种简单的方法来计算天数。如果这通常是在几个月而不是几年的时间段内完成,并且没有被重复调用,那么这不会对性能造成影响。

int GetWorkingDays(DateTime startDate, DateTime endDate)
{
    int count = 0;
    for (DateTime currentDate = startDate; currentDate < endDate; currentDate = currentDate.AddDays(1))
    {
        if (currentDate.DayOfWeek == DayOfWeek.Sunday || currentDate.DayOfWeek == DayOfWeek.Saturday)
        {
            continue;
        }
        count++;
    }
    return count;
}

【讨论】:

  • 我还需要跟踪非工作日,如果我用非工作日计数器替换继续??
  • 首先你需要有一个存储所有假期(预定义)的表格,并检查 current_day 是否与 day_holiday_table 匹配......你不应该把它算在内,休息是工作日。
  • 我对假期有不同的解决方案。并为此创建一个数据库表真的不是我的选择。我只是从字面上想数周一至周五..谢谢顺便说一句=)
【解决方案6】:

您可以使用时间线辅助类来实现 - 该类还允许其他时间间隔:

public class TimeLine
{
    public static IEnumerable<DateTime> CreateTimeLine(DateTime start, TimeSpan interval) {
        return TimeLine.CreateTimeLine(start, interval, DateTime.MaxValue);
    }

    public static IEnumerable<DateTime> CreateTimeLine(DateTime start, TimeSpan interval, DateTime end) {
        var currentVal = start;
        var endVal = end.Subtract(interval);

        do {
            currentVal = currentVal.Add(interval);
            yield return currentVal;
        } while (currentVal <= endVal);
    }
}

要解决您的问题,您可以执行以下操作:

var workingDays = TimeLine.CreateTimeLine(DateTime.Now.Date, TimeSpan.FromDays(1), DateTime.Now.Date.AddDays(30))
                          .Where(x => x.DayOfWeek != DayOfWeek.Saturday && x.DayOfWeek != DayOfWeek.Sunday);
var noOfWorkingDays = workingDays.Count();

这个时间线类可以用于任意区间的任意连续时间线。

【讨论】:

    【解决方案7】:

    获取工作日的简单方法:

    int GetRegularWorkingDays(DateTime startDate, DateTime endDate)
    {
        int total = 0;
    
        if (startDate < endDate)
        {
            var days = endDate - startDate;
    
            for( ; startDate < endDate; startDate = startDate.AddDays(1) )
            {
                switch(startDate.DayOfWeek)
                {
                    case DayOfWeek.Saturday :
                    case DayOfWeek.Sunday :                     
                        break;
                    default:
                        total++;
                        break;
                }
            }
        }
        return total;
    }
    

    【讨论】:

      【解决方案8】:
      int count = 0;
      switch (dateTimePicker2.Value.DayOfWeek.ToString())
      {
          case "Saturday": count--; break;
          case "Sunday": count--; break;
          default:break;
      }
      switch (dateTimePicker3.Value.DayOfWeek.ToString())
      {
          case "Saturday": count--; break;
          case "Sunday": count--; break;
          default:break;
      }
      if (count == -2)
          count = -1;
      int weeks = t.Days / 7;
      int daycount =count+ t.Days - (2 * weeks)+1;
      label7.Text = "No of Days : " + daycount.ToString();
      

      【讨论】:

        猜你喜欢
        • 2021-12-16
        • 1970-01-01
        • 2018-07-27
        • 1970-01-01
        • 2011-02-19
        • 2018-07-03
        • 1970-01-01
        • 2011-04-01
        • 1970-01-01
        相关资源
        最近更新 更多