【问题标题】:How to gets the number of overlapping days between 2 ranges?如何获取 2 个范围之间的重叠天数?
【发布时间】:2013-12-30 09:06:22
【问题描述】:

让我先解释一下情况 - 假设我有 4 个日期:BS、BE、PS、PE(S 表示开始,E 表示结束)。 我需要知道在给定这些日期时重叠了多少天。 例如:BE-05.01、BS-10.01、PS-03.01、PE-07.01 结果是:3(05.01、06.01和07.01重叠)

我写了以下代码,但它似乎很乱,我想检查是否有更简单的方法来做到这一点:

private static double GetNumOfOverLappingDays(DateTime BS, DateTime BE, DateTime PS, DateTime PE)
    {
        //case 1:
        //                  |--- B ---|
        //                  |----P ---|

        //case 2:
        //                  |--- B ---|
        //                          | --- P --- |

        //case 3:
        //                  |--- B ---|
        //          | --- P ---- |

        //case 4:
        //                  |--- B ---|
        //                     | - P - |

        //case 5:
        //                  |--- B ---|
        //              | -------- P -------- |

        double days = -1;
        bool isNotOverLap = (BS > PE) || (PS > BE);

        if (isNotOverLap == false)
        {
            //case 1
            if (BS == PS && BS == PE)
            {
                days = (PE - PS).TotalDays;
            }
            //case 2
            else if (BE > PS && BE < PE)
            {
                days = (BE - PS).TotalDays;
            }
            //case 3
            else if (BS > PS && BS < PE)
            {
                days = (PE - BS).TotalDays;
            }
            //case 4
            else if (BS < PS && BE > PE)
            {
                days = (PE - PS).TotalDays;
            }
            //case 5
            else if (BS > PS && BE < PE)
            {
                days = (BE - PS).TotalDays;
            }
        }
        return days+1;
    }

提前感谢您的帮助,

阿米特

【问题讨论】:

  • 如果这些是日期,那么您需要确定是否要包含或排除结束日期,一方面。例如,假设它们是 all 2014 年 1 月 1 日。将值视为日期,我希望在这种情况下重叠 1 天 - 但您的代码将给出 0 天。日期范围通常包括结束点,而日期/时间范围不包括。这只是 Noda TimeLocalDate 作为单独类型的一个原因...
  • 你是正确的。我正在为“天”值添加 +1 以解决此问题
  • 请在问题中注明,这样会更清楚。
  • 回应 Jon 的评论 - 包括时间的范围通常表示为半开间隔 [start, end),其中不包括结束时间。例如,如果我说一个事件发生在 1:00 - 2:00,我通常的意思是 2:00 不在范围内,因为该事件已经结束。但是对于仅日期范围(DateTime 的时间部分被忽略),这些通常是完全包含 范围,[start, end],例如当我说Jan 1 - Jan 31 时。决定您将使用哪个,因为它确实会影响您的实现中的逻辑。

标签: c# date range


【解决方案1】:

试试这个:

    private static double GetOverlappingDays(DateTime firstStart, DateTime firstEnd, DateTime secondStart, DateTime secondEnd)
    {
        DateTime maxStart = firstStart > secondStart ? firstStart : secondStart;
        DateTime minEnd = firstEnd < secondEnd ? firstEnd : secondEnd;
        TimeSpan interval = minEnd - maxStart;
        double returnValue = interval > TimeSpan.FromSeconds(0) ? interval.TotalDays : 0;
        return returnValue;
    }

【讨论】:

  • 如果您使用适当的空格并让您的变量遵循 .NET 命名约定,这将更加清晰。
  • 抱歉,我可能过于依赖 VS IDE,无法为我放置空格。至于命名约定,你能举个例子吗?我一直在努力改掉使用匈牙利表示法的习惯,通常我会将 MaxStart 命名为 dtMaxStart,等等。
  • 我建议使用maxStartminEnd 等,而不是MaxStart 等。使用camelCase 表示变量,而不是PascalCase
  • 有趣的是,在渲染代码块时,StackOverflow 似乎根据大小写区分变量和类型...
  • 您可能需要编辑:TimeSpan 间隔 = minEnd.AddDays(1) - maxStart;如果第二个日期范围是一天(secondStart 和 secondEnd 都是同一日期)。
【解决方案2】:

尝试使用for循环:

DateTime d1 = new DateTime(2013,12,1),
            d2 = new DateTime(2013,12,14),
            d3 = new DateTime(2013,12,10),
            d4 = new DateTime(2013,12,20);
int count = 0;

for (var d = d1.Date; d <= d2.Date; d = d.AddDays(1))
{
      if (d >= d3.Date && d <= d4.Date)
           count++;
}

Console.WriteLine(count);

请注意,这段代码给出了整数值。如果您需要以小时为单位的价值,则此代码不适合。这也不是最有效的方法,但它很简单,适用于小范围。

【讨论】:

  • 如果使用这种方法,计算两个跨越 10 年的范围的值将比计算跨越一天的范围的值多 3650 倍。
【解决方案3】:

如果您有两个高度相关的值,例如开始日期和结束日期,那么最好使用object which will hold them both。考虑创建DateRange 类:

public class DateRange
{
    public DateRange(DateTime start, DateTime end)
    {
        if (start > end) throw new ArgumentException();

        Start = start;
        End = end;
    }

    public DateTime Start { get; private set; }
    public DateTime End { get; private set; }
    public double TotalDays { get { return (End - Start).TotalDays; } }

    public bool Includes(DateTime value)
    {
        return (Start <= value) && (value <= End);
    }

    public DateRange Intersect(DateRange range)
    {
        if (range.Includes(Start))
            return new DateRange(Start, (End < range.End) ? End : range.End);

        if (range.Includes(End))
            return new DateRange(range.Start, End);

        if (Start < range.Start && range.End < End)
            return range;

        return null;
    }
}

用法:

public static double GetNumOfOverLappingDays(DateRange b, DateRange p)
{
   DateRange overlap = b.Intersect(p);
   if (overlap == null)
       return 0;

   return overlap.TotalDays;
}

创建日期范围并将它们传递给此方法:

var b = new DateRange(new DateTime(2013, 12, 20), new DateTime(2013, 12, 25));
var p = new DateRange(new DateTime(2013, 12, 22), new DateTime(2013, 12, 26)); 
double totalDays = GetNumOfOverLappingDays(b, p);

【讨论】:

    【解决方案4】:

    我用它获取两个时间范围之间的重叠范围,你可以很容易地从结果中得到重叠天数

    public static (DateTime, DateTime)? GetOverlappingTimeRange((DateTime, DateTime) firstTimeRange, 
                                                                (DateTime, DateTime) secondTimeRange)
    {
        var leftIndex = Math.Max(firstTimeRange.Item1.Ticks, secondTimeRange.Item1.Ticks);
        var rightIndex = Math.Min(firstTimeRange.Item2.Ticks, secondTimeRange.Item2.Ticks);
    
        if (leftIndex > rightIndex)
        {
            return null;
        }
        else
        {
            return (new DateTime(leftIndex), new DateTime(rightIndex));
        }
    }
    

    【讨论】:

      【解决方案5】:

      我建议计算最晚开始和最早结束,然后将两者相减。请注意,如果时间完全不重叠,此示例可以返回负数,但这不是您提到的 5 种情况之一。

          static void Main(string[] args)
          {
              DateTime start1 = new DateTime(2013,1,2);
              DateTime end1 = new DateTime(2013,1,14);
      
              DateTime start2 = new DateTime(2013,1,1);
              DateTime end2 = new DateTime(2013,1,12);
      
              DateTime lateStart = GetLatest(start1, start2);
              DateTime earlyEnd = GetEarliest(end1, end2);
      
              Console.WriteLine((earlyEnd-lateStart).TotalDays + 1);
              Console.ReadLine();
          }
      
          private static DateTime GetEarliest(DateTime start1, DateTime start2)
          {
              return start1 < start2 ? start1 : start2;
          }
      
          private static DateTime GetLatest(DateTime start1, DateTime start2)
          {
              return start1 > start2 ? start1 : start2;
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-24
        • 1970-01-01
        • 2019-10-21
        • 2013-10-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多