Как рассчитать общее количество секунд многих перекрывающихся дат и времени - PullRequest
0 голосов
/ 16 января 2019

Я пытаюсь найти способ подсчета общего количества секунд, в течение которого конкретное устройство было неисправно из-за разного рода ошибок, которые имеют StartTime и и EndTime и могут происходить в один и тот же момент, но с разной длительностью.

У меня есть коллекция DateTimes:

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 голосов
/ 17 января 2019

Я не уверен, что вы бы посчитали "отвратительным", но если вы просто хотите, чтобы количество секунд для каждого промежутка времени было, вы могли бы создать метод для вашего класса TimeLapse, который возвращает именно это.

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

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

Разница между двумя объектами DateTime возвращает объект TimeSpan . И вместо того, чтобы иметь словарь ваших объектов TimeLapse, вы можете иметь словарь двойных значений (который представляет секунды) .

var _devices = new Dictionary<string, double>()
0 голосов
/ 17 января 2019

Демонстрация красоты 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 }
0 голосов
/ 16 января 2019

Один из способов сделать это - расширить класс с помощью дополнительного метода, который объединит список 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 является TimeSpan, вы Вы можете получить практически любую единицу измерения (например, TotalSeconds в вашем случае):

private static void Main()
{
    Dictionary<string, List<TimeLapse>> devices = GetDeviceList();

    foreach (var device in devices)
    {
        Console.WriteLine("{0}: {1} total days", device.Key,
            TimeLapse.Merge(device.Value).Sum(value => value.Duration.TotalDays));
    }

    GetKeyFromUser("Done! Press any key to exit...");
}

private static Dictionary<string, List<TimeLapse>> GetDeviceList()
{
    return new Dictionary<string, List<TimeLapse>>
    {
        // device1 total should be 4 days (1/1 - 1/5)
        {"device1", new List<TimeLapse>{
            new TimeLapse {StartTime = DateTime.Parse("1/1/2019"),
                EndTime = DateTime.Parse("1/3/2019")},
            new TimeLapse {StartTime = DateTime.Parse("1/2/2019"),
                EndTime = DateTime.Parse("1/3/2019")},
            new TimeLapse {StartTime = DateTime.Parse("1/3/2019"),
                EndTime = DateTime.Parse("1/5/2019")}}},

        // device2 total should be 7 days (1/1 - 1/4 plus 1/6 - 1/10)
        {"device2", new List<TimeLapse>{
            new TimeLapse {StartTime = DateTime.Parse("1/1/2019"),
                EndTime = DateTime.Parse("1/3/2019")},
            new TimeLapse {StartTime = DateTime.Parse("1/3/2019"),
                EndTime = DateTime.Parse("1/4/2019")},
            new TimeLapse {StartTime = DateTime.Parse("1/6/2019"),
                EndTime = DateTime.Parse("1/10/2019")}}},

        // device3 total should be 2 days (1/1 - 1/2 plus 1/6 - 1/7)
        {"device3", new List<TimeLapse>{
            new TimeLapse {StartTime = DateTime.Parse("1/1/2019"),
                EndTime = DateTime.Parse("1/2/2019")},
            new TimeLapse {StartTime = DateTime.Parse("1/6/2019"),
                EndTime = DateTime.Parse("1/7/2019")}}},

        // device4 total should be 2 days (1/1 - 1/3)
        {"device4", new List<TimeLapse>{
            new TimeLapse {StartTime = DateTime.Parse("1/1/2019"),
                EndTime = DateTime.Parse("1/3/2019")}}},
    };
}

выход

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...