如何计算许多重叠的DateTime的总秒数

时间:2019-01-16 18:15:41

标签: c# datetime

我正试图找到一种方法来计算特定设备因具有StartTime和EndTime的各种错误而出现故障的总秒数,这些错误可能在同一时刻发生,但持续时间不同。 / p>

这是我拥有的DateTime的集合:

private class TimeLapse
{
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }
}

Dictionary<string, List<TimeLapse>> _devices = new Dictionary<string, List<TimeLapse>>();

其中字典中的字符串是设备的名称;

但是我不知道从哪里开始不编写令人作呕的代码来做到这一点。 任何人都有相同的问题要解决吗?

3 个答案:

答案 0 :(得分:4)

执行此操作的一种方法是使用其他方法扩展您的类,该方法将合并TimeLapse个对象列表,方法是将重叠的对象合并成一个TimeLapse,然后返回这套。如果执行此操作,则只需将集合中每个项目的持续时间加起来即可。您还可以添加一个属性,该属性公开Duration对象的TimeLapse

private class TimeLapse
{
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }
    public TimeSpan Duration => (EndTime - StartTime).Duration();

    public static List<TimeLapse> Merge(List<TimeLapse> items)
    {
        if (items == null || items.Count < 2) return items;

        var results = new List<TimeLapse>();

        foreach (var item in items)
        {
            var overlappingItem = results.FirstOrDefault(item.OverlapsWith);
            if (overlappingItem == null) results.Add(item);
            else overlappingItem.CombineWith(item);
        }

        return results;
    }

    private bool OverlapsWith(TimeLapse other)
    {
        return other != null &&
               other.StartTime <= EndTime &&
               other.EndTime >= StartTime;
    }

    private void CombineWith(TimeLapse other)
    {
        if (!OverlapsWith(other)) return;
        if (other.StartTime < StartTime) StartTime = other.StartTime;
        if (other.EndTime > EndTime) EndTime = other.EndTime;
    }
}

以下是如何显示词典中每个项目的持续时间的示例。

我提供了一种生成设备虚拟列表的方法,所以我使用Days是因为它更容易编写和验证结果是否正确,但是由于Duration是{ {1}},您几乎可以获得所需的任何度量单位(例如TimeSpan):

TotalSeconds

输出

enter image description here

答案 1 :(得分:1)

通过编写扩展方法来演示LINQ的美丽。

/// <summary>
/// Gets the duration of the set union of the specified intervals.
/// </summary>
/// <param name="timeLapses">Sequence of <see cref="TimeLapse"/> ordered by <see cref="TimeLapse.StartTime"/>.</param>
public static TimeSpan UnionDurations(this IEnumerable<TimeLapse> timeLapses)
{
    using (var e = timeLapses.GetEnumerator())
    {
        if (!e.MoveNext()) // no items, no duration
            return TimeSpan.Zero;

        var prev = e.Current;
        var total = prev.EndTime - prev.StartTime; // set running total to duration of 1st interval

        while (e.MoveNext())
        {
            var curr = e.Current;
            if (curr.StartTime < prev.StartTime) throw new Exception($"{nameof(timeLapses)} are not in ascending {nameof(TimeLapse.StartTime)} order.");

            var increase = curr.EndTime - (curr.StartTime > prev.EndTime ? curr.StartTime : prev.EndTime);
            if (increase <= TimeSpan.Zero) continue;
            total += increase;
            prev = curr;
        }

        return total;
    }
}

测试代码:

var input = new Dictionary<string, IList<TimeLapse>>
{
    {
        "A",
        new[]
        {
            new TimeLapse{ StartTime = new DateTime(2019, 1, 17, 0, 0, 0), EndTime = new DateTime(2019, 1, 17, 3, 0, 0)},
            new TimeLapse{ StartTime = new DateTime(2019, 1, 17, 1, 0, 0), EndTime = new DateTime(2019, 1, 17, 2, 0, 0)},
            new TimeLapse{ StartTime = new DateTime(2019, 1, 17, 1, 0, 0), EndTime = new DateTime(2019, 1, 17, 4, 0, 0)},
            new TimeLapse{ StartTime = new DateTime(2019, 1, 17, 5, 0, 0), EndTime = new DateTime(2019, 1, 17, 7, 0, 0)}
        }
    },
    {
        "B",
        new TimeLapse [0]
    }
};
var result = input
    .Select(kv => new
    {
        Device = kv.Key,
        FaultyDuration = kv.Value
            // .OrderBy(tl => tl.StartTime) // this line can be removed if already ordered by StartTime
            .UnionDurations()
    })
    .ToList();
// { Device = A, FaultyDuration = 06:00:00 }
// { Device = B, FaultyDuration = 00:00:00 }

答案 2 :(得分:0)

我不确定您会认为“令人作呕的”是什么,但是如果您只希望每次间隔时间都用几秒钟,则可以为TimeLapse类创建一个返回该方法的方法。

private class TimeLapse
{
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }

    public double GetSecondsPassed() {
        return (EndTime - StartTime).TotalSeconds
    }
}

两个DateTime对象之间的差返回一个TimeSpan对象。而不是拥有TimeLapse对象的字典,您可以拥有双(which represents seconds)的字典。

var _devices = new Dictionary<string, double>()