【问题标题】:Determine the difference between two DateTimes, only counting opening hours确定两个 DateTime 之间的差异,仅计算营业时间
【发布时间】:2011-02-15 15:56:35
【问题描述】:

对于我们的 C# 支持软件,我需要确定两个 DateTime 之间的时间跨度,但我只想计算开放时间(即工作日从 09:00 到 17:00)。

因此,例如,如果第一个 DateTime 是 15/02/2011 16:00,第二个是 16/02/2011 10:00,则该方法应返回 2 小时。

非常感谢任何帮助!

【问题讨论】:

  • SO 上已经有一些类似的问题。例如stackoverflow.com/questions/3835041/…
  • 假期?分和秒?
  • 精确到最近的小时应该足以满足我的目的。丹麦假期会很好,但并不重要。感谢您到目前为止的回答。我还没有时间测试它们。

标签: c# datetime


【解决方案1】:
DateTime start = DateTime.Parse("15/02/2011 16:00");
DateTime end = DateTime.Parse("16/02/2011 10:00");

int count = 0;

for (var i = start; i < end; i = i.AddHours(1))
{
    if (i.DayOfWeek != DayOfWeek.Saturday && i.DayOfWeek != DayOfWeek.Sunday)
    {
        if (i.TimeOfDay.Hours >= 9 && i.TimeOfDay.Hours < 17)
        {
            count++;
        }
    }
}

Console.WriteLine(count);

【讨论】:

  • 这个答案正在计算整个小时。分钟呢?秒?
  • 我认为只需要几个小时。
  • 我选择了这个解决方案,只是将 i.AddHours(i) 更改为 i.AddMinutes(1) 以获得分钟精度。这可能缺乏 LINQ 解决方案的优雅,但我喜欢它的简单易读性。
【解决方案2】:

我们开始吧,花了一段时间为你做这个。 :)

有检测假期的空间(如果您编写一个检查DateTime 是否为假期的函数),检测日期之间的周末,并且处理的不仅仅是几个小时。

该算法是计算从开始到结束营业以及从营业开始到结束的时间,然后计算中间的天数。落在同一天是一个特例。

警告:我做了一些基本测试,但可能没有得到所有极端情况。

    public static TimeSpan BusinessTimeDelta(DateTime start, DateTime stop)
    {
        if (start == stop)
            return TimeSpan.Zero;

        if (start > stop)
        {
            DateTime temp = start;
            start = stop;
            stop = temp;
        }

        // First we are going to truncate these DateTimes so that they are within the business day.

        // How much time from the beginning til the end of the day?
        DateTime startFloor = StartOfBusiness(start);
        DateTime startCeil = CloseOfBusiness(start);
        if (start < startFloor) start = startFloor;
        if (start > startCeil) start = startCeil;

        TimeSpan firstDayTime = startCeil - start;
        bool workday = true; // Saves doublechecking later
        if (!IsWorkday(start))
        {
            workday = false;
            firstDayTime = TimeSpan.Zero;
        }

        // How much time from the start of the last day til the end?
        DateTime stopFloor = StartOfBusiness(stop);
        DateTime stopCeil = CloseOfBusiness(stop);
        if (stop < stopFloor) stop = stopFloor;
        if (stop > stopCeil) stop = stopCeil;

        TimeSpan lastDayTime = stop - stopFloor;
        if (!IsWorkday(stop))
            lastDayTime = TimeSpan.Zero;

        // At this point all dates are snipped to within business hours.

        if (start.Date == stop.Date)
        {
            if (!workday) // Precomputed value from earlier
                return TimeSpan.Zero;

            return stop - start;
        }

        // At this point we know they occur on different dates, so we can use
        // the offset from SOB and COB.

        TimeSpan timeInBetween = TimeSpan.Zero;
        TimeSpan hoursInAWorkday = (startCeil - startFloor);

        // I tried cool math stuff instead of a for-loop, but that leaves no clean way to count holidays.
        for (DateTime itr = startFloor.AddDays(1); itr < stopFloor; itr = itr.AddDays(1))
        {
            if (!IsWorkday(itr))
                continue;

            // Otherwise, it's a workday!
            timeInBetween += hoursInAWorkday;
        }

        return firstDayTime + lastDayTime + timeInBetween;
    }

    public static bool IsWorkday(DateTime date)
    {
        // Weekend
        if (date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday)
            return false;

        // Could add holiday logic here.

        return true;
    }

    public static DateTime StartOfBusiness(DateTime date)
    {
        return new DateTime(date.Year, date.Month, date.Day, 9, 0, 0);
    }

    public static DateTime CloseOfBusiness(DateTime date)
    {
        return new DateTime(date.Year, date.Month, date.Day, 17, 0, 0);
    }

【讨论】:

  • 好东西,2 年后它很有帮助。非常感谢!
  • 这很好,我稍微改动了一下,但其他方面都很棒,因为在我的情况下,时间不是每天固定的。
  • 这真是一个很棒的解决方案
【解决方案3】:

使用 LINQ:

DateTime dt1 = new DateTime(2010, 10, 1, 16, 0, 0);
DateTime dt2 = new DateTime(2010, 10, 2, 10, 0, 0);

int hours = Enumerable.Range(1, (dt2 - dt1).Hours)
                 .Where(h =>
                    {
                        var dt = dt1.AddHours(h);
                        return dt.DayOfWeek != DayOfWeek.Saturday
                               && dt.DayOfWeek != DayOfWeek.Sunday
                               && dt.Hour >= 9 && dt.Hour <= 17;
                    }).Count();

这里我假设所有MinuteSecond 都为零。否则(dt2 - dt1).Hours 会给出意想不到的值。

【讨论】:

    【解决方案4】:

    你可以试试这个算法:

    1. 计算总小时数(假设 TotalHours = (Date2 - Date1).TotalHours)
    2. 计算节假日数(节假日,包括周末和其他节假日)
    3. 计算工作日数 ((Date2-Date1).TotalDays - Holidays) as int

    工作时间 = TotalHours - 24*holidays - 16*WorkingDays

    (16 = 下午 5 点和第二天早上 9 点之间的时差)

    【讨论】:

      【解决方案5】:

      试试这个

      namespace ConsoleApplication1
      {
      class Program
      {
          static void Main(string[] args)
          {
              var date1 = new DateTime(2010, 10, 12, 12, 00, 00);
              var date2 = new DateTime(2010, 10, 14, 15, 00, 00);
      
              var hr1 = ((date1.Hour > 9) && (date1.Hour < 17)) ? 17 - date1.Hour : 0;
              var hr2 = ((date2.Hour > 9) && (date2.Hour < 17)) ? 17 - date2.Hour : 0;
      
              var middleHours = ((date2.Date -  date1.Date).Days -1) * 8 ;
      
              Console.WriteLine(hr1+hr2+ middleHours);
              Console.ReadKey();
      
          }
      }
      

      }

      我试过了,效果很好:)

      【讨论】:

      • 跨周末的开始和结束日期呢?
      【解决方案6】:

      这是一种提供分钟级精度并支持夜间时间范围的方法,以 Oleg Rudckivsky 接受的答案为基础。

      /// <summary>
      /// Counts the number of minutes between <paramref name="from"/> and <paramref name="to"/>
      /// that fall between <paramref name="rangeStart"/> and <paramref name="rangeEnd"/>.
      /// </summary>
      /// <returns>The total minutes spanned between the given range.</returns>
      public static int GetMinutesInRange(DateTime from, DateTime to, TimeSpan rangeStart, TimeSpan rangeEnd) {
          int minutes = 0;
          bool overnight = rangeStart > rangeEnd;
          for (var m = from; m < to; m = m.AddMinutes(1)) {
              if (overnight) {
                  if (rangeStart <= m.TimeOfDay || m.TimeOfDay < rangeEnd) {
                      minutes++;
                  }
              } else {
                  if (rangeStart <= m.TimeOfDay) {
                      if (m.TimeOfDay < rangeEnd) {
                          minutes++;
                      } else {
                          break;
                      }
                  }
              }
          }
          return minutes;
      }
      

      示例用法:

      DateTime from = new DateTime(1990, 1, 1, 6, 30, 0);  // 7:30 AM
      DateTime to = new DateTime(1990, 1, 1, 13, 30, 0);   // 1:30 PM
      TimeSpan rangeStart = new TimeSpan(9, 0, 0);  // 9 AM
      TimeSpan rangeEnd = new TimeSpan(17, 0, 0);   // 5 PM
      
      // Results in 270 (4.5 hours)
      int minutes = GlobalHelpers.GetMinutesInRange(from, to, rangeStart, rangeEnd);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-03-31
        • 1970-01-01
        • 2019-08-04
        • 2020-11-18
        • 2013-05-28
        • 2020-05-13
        相关资源
        最近更新 更多