计算TimeSpans中重叠的百分比

时间:2018-01-30 17:40:56

标签: c# datetime overlap timespan

BREAK
24.1.2018 13.10 - 24.1.2018 13.30
24.1.2018 19.00 - 26.1.2018 15.00

AVAILABILITY    
24.1 13.00-14.00 = 66.67%
25.1 13.00-14.00 = 0%;

我看到上面的例子有休息。 我想查看这个休息时间占2个给定日期时间的百分比。例如,对于24.1和25.1,13.00-14.00 ......正如眼睛告诉25)13-14,可用性为0 ...而24.1 13.00-14.00为66.67%。

我如何借助图书馆或代码以编程方式计算这个百分比?

  

CalcPercentage(Breakstart,breakend,可用性开始,可用性   开始)将返回例如66,67

2 个答案:

答案 0 :(得分:3)

如果我们想从所考虑的可用期间的总长度中减去休息时间,我们必须确保中断时段不会相互重叠,否则我们可能会减去一段时间或两部分。

首先,我们需要一个代表句点的类型:

public class Period
{
    public Period(DateTime start, DateTime end)
    {
        Start = start;
        End = end;
    }

    public DateTime Start { get; }
    public DateTime End { get; }
    public TimeSpan Duration => End - Start;

    public Period Intersect(Period other)
    {
        long start = Math.Max(Start.Ticks, other.Start.Ticks);
        long end = Math.Min(End.Ticks, other.End.Ticks);
        if (start > end) { // Periods not overlapping or touching.
            return null;
        }
        return new Period(new DateTime(start), new DateTime(end));
    }

    public Period Union(Period other)
    {
        if (other.Start > End || other.End < Start) { // Periods not overlapping or touching.
            return null;
        }
        return new Period(
            new DateTime(Math.Min(Start.Ticks, other.Start.Ticks)),
            new DateTime(Math.Max(End.Ticks, other.End.Ticks))
        );
    }
}

它还包含交集(=重叠部分)和句点联合的方法。

用单个句点替换重叠或触摸时段:

private List<Period> CondensePeriods(IEnumerable<Period> periods)
{
    List<Period> tmp = periods.ToList();
    for (int i = 0; i < tmp.Count; i++) {
        Period first = tmp[i]; // Compare this period to all following ones.
        // Loop in reverse order because we are removing entries.
        for (int j = tmp.Count - 1; j > i; j--) {
            Period condensed = first.Union(tmp[j]);
            if (condensed != null) { // Periods overlap or are touching.
                // Replace first period with a condensed period.
                tmp[i] = condensed;

                // Remove the other period.
                tmp.RemoveAt(j);
            }
        }
    }
    return tmp;
}

请注意,CondensePeriods具有O(n 2 )复杂度,因此未针对多个中断周期进行优化。

最后我们可以像这样计算可用性:

public double AvailabilityPercentage(IEnumerable<Period> breaks, Period period)
{
    // First replace overlapping or touching break periods by single period.
    breaks = CondensePeriods(breaks);

    // Now remove these non-overlapping breaks from the tested period.
    long totalPeriodDuration = period.Duration.Ticks;
    long available = totalPeriodDuration;
    foreach (Period brk in breaks) {
        // Take part of break that lies within the tested period.
        var intersection = brk.Intersect(period);
        if (intersection != null) { // Break is not outside of period.
            available -= intersection.Duration.Ticks;
        }
    }

    return 100.0 * available / totalPeriodDuration;
}

(测试)

如果您确定自己的休息时间不会重叠,可以放弃breaks = CondensePeriods(breaks);

答案 1 :(得分:0)

鉴于您的样本方法签名,您可以执行以下操作:

public static double CalcPercentage(DateTime breakStart, DateTime breakEnd, 
    DateTime availStart, DateTime availEnd)
{
    // "fail or return fast" argument checks
    if (breakStart >= breakEnd || availStart >= availEnd) return 0;
    if (breakStart > availEnd || breakEnd < availStart) return 100;
    if (breakStart < availStart && breakEnd > availEnd) return 0;

    // Calc total minutes available, actual minutes available, and the percentage
    var totalAvailMin = (availEnd - availStart).TotalMinutes;
    var actualAvailMin = 0.0;
    if (availStart < breakStart) actualAvailMin += (breakStart - availStart).TotalMinutes;
    if (availEnd > breakEnd) actualAvailMin += (availEnd - breakEnd).TotalMinutes;
    var percentage = actualAvailMin / totalAvailMin * 100;

    return percentage;
}

使用您的示例数据使用此方法可能如下所示:

static void Main()
{
    var breakStart = new DateTime(2018, 1, 24, 13, 10, 0);
    var breakEnd = new DateTime(2018, 1, 24, 13, 30, 0);
    var availStart = new DateTime(2018, 1, 24, 13, 00, 0);
    var availEnd = new DateTime(2018, 1, 24, 14, 00, 0);
    var availPercent = CalcPercentage(breakStart, breakEnd, availStart, availEnd);

    Console.WriteLine($"{availPercent:0.00}%");

    Console.Write("\nPress any key to exit...");
    Console.ReadKey();
}

<强>输出

enter image description here

相关问题