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
答案 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();
}
<强>输出强>