Мне нужно извлечь перерывы на рабочее время из списка инструкций по времени [C# | Vb.Net]

#vb.net

Вопрос:

Я работаю над API для импорта отчетов о времени (в основном листов Excel с указанием рабочего времени и т. Д.). Я успешно прочитал и сохранил данные в связывающем списке(MyClass), но теперь мне нужно извлечь временные интервалы из этого списка и поместить их в другой отдельный список.

Проблема в том, что часы нужно извлекать определенным образом: мне в основном нужно значение последнего столбца первой строки, и я должен объединить его со значением предпоследнего столбца второй строки, чтобы создать новую запись в отдельном списке.

Записи в отдельном списке различаются по дате.

Я попытался преобразовать свой список привязок в таблицу данных для работы с индексами строк, но я понятия не имею, как и с чего начать.

Надеюсь, я достаточно хорошо объяснил свою проблему. Я также приложил скриншот с двумя структурами списков и тем, как их следует обрабатывать.

Я с нетерпением жду ваших идей!

Ссылка на пример изображения

Вот мой класс перерывов, мой класс метки времени в основном такой же, без свойства Имени и фамилии:

  Public Class BreaksMA
    Implements INotifyPropertyChanged

    Private _ID As String
    Public Property ID As String
        Get
            Return _ID
        End Get
        Set(value As String)
            _ID = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ID"))
        End Set
    End Property

    Private _Date As String
    Public Property DateX As String
        Get
            Return _Date
        End Get
        Set(value As String)
            _Date = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("DateX"))
        End Set
    End Property

    Private _From As String
    Public Property From As String
        Get
            Return _From
        End Get
        Set(value As String)
            _From = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("From"))
        End Set
    End Property

    Private _To As String
    Public Property ToX As String
        Get
            Return _To
        End Get
        Set(value As String)
            _To = value
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("ToX"))
        End Set
    End Property

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
End Class
 

Вот мой список перерывов :

     Private _breakView As List(Of BreaksMA)
Public Property breakView As List(Of BreaksMA)
    Get
        Return _breakView
    End Get
    Set(value As List(Of BreaksMA))
        _breakView = value
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("breakView"))
    End Set
End Property
 

Комментарии:

1. Добро пожаловать в stackoverflow. Сайт обычно предназначен для людей, у которых есть проблемы с их кодом, поэтому мы можем помочь их исправить. Но вы не опубликовали свой подход, так что помочь трудно.

2. Можете ли вы показать нам определение MyClass . Я примерно на 99% уверен, что ты хочешь держаться подальше от DataTable А. Почему а BindingList<T> , а не просто а List<T>

3. A List<TimeStatement> достаточно для обработки данных и может быть проиндексировано. Предполагается TimeStatement , что это класс со свойствами ID, FistName, Фамилия, От, До. BindingList предназначен для привязки к пользовательскому интерфейсу. DataTable неудобно.

4. Почему datatable неудобен? Это в основном 2D массив или список<свойств>, в зависимости от того, как вы на это смотрите

5. Имеет ли смысл в этом случае использовать набор данных?

Ответ №1:

Вот решение без таблиц данных, наборов данных или индексов.

Во-первых, я настоятельно рекомендую вам работать в режиме времени для дат и промежутков времени для ваших отметок времени. Если вы сделаете это таким образом, вы можете выполнить математику даты и времени (обратите внимание, что мое решение имеет Duration свойство только для чтения, которое просто является разницей между двумя временами).

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

Я не внедряю INotifyPropertyChanged , это не важно для этого решения. Вы можете добавить его обратно.

Когда я начал это (между встречами несколько часов назад), ваш вопрос был помечен как C#, и вы не предоставили никакого кода. Мой C# лучше, чем мой VB — вы можете перевести его обратно. Совершенно очевидно, что вы хотите VB — извините, но я не буду делать это дважды. Все это не очень сложно, должно быть легко перенести в VB вручную.

Сначала я создаю два класса, один из которых представляет вашу первую группу (BreaksMA в коде, который вы в конечном итоге опубликовали, BreakTime в моем коде), а второй представляет вашу вторую группу (я назвал ее TimeStamp ). Помните, я начал это до того, как вы показали свой код. Не стесняйтесь переделывать имена.

 public class BreakTime
{
    public BreakTime(int id, string firstName, string lastName, DateTime date, TimeSpan fromTime, TimeSpan toTime)
    {
        Id = id;
        FirstName = firstName;
        LastName = lastName;
        Date = date;
        FromTime = fromTime;
        ToTime = toTime;
    }

    public BreakTime(int id, string firstName, string lastName, string date, string fromTime, string toTime)
    {
        Id = id;
        FirstName = firstName;
        LastName = lastName;
        DateString = date;
        FromTimeString = fromTime;
        ToTimeString = toTime;
    }

    public override string ToString()
    {
        return $"{Id}: {FirstName} {LastName}: {DateString} From: {FromTimeString}, To: {ToTimeString}";
    }

    private const string DateFormat = "dd.MM.yyyy";
    private const string ToFromFormat = @"hh:mm:ss";
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Date { get; set; }

    public string DateString
    {
        get => Date.ToString(DateFormat);
        set => Date = DateTime.ParseExact(value, DateFormat, CultureInfo.InvariantCulture);
    }

    public TimeSpan FromTime { get; set; }

    public string FromTimeString
    {
        get => FromTime.ToString(ToFromFormat);
        set => FromTime = TimeSpan.ParseExact(value, ToFromFormat, CultureInfo.InvariantCulture);
    }

    public TimeSpan ToTime { get; set; }

    public string ToTimeString
    {
        get => ToTime.ToString(ToFromFormat);
        set => ToTime = TimeSpan.ParseExact(value, ToFromFormat, CultureInfo.InvariantCulture);
    }
}
 

Обратите внимание, что вы можете получить или установить значения даты и времени либо с правильным типом (дата-время или промежуток времени), либо со строкой.

Другой тип-это в основном та же идея, без информации об имени. Возможно, вы захотите использовать для этого наследование, но я просто использовал копирование/вставку:

 public class TimeStamp
{
    public TimeStamp()
    {
        //everything gets set to default
    }

    public TimeStamp(int id, DateTime date, TimeSpan fromTime, TimeSpan toTime)
    {
        Id = id;
        Date = date;
        FromTime = fromTime;
        ToTime = toTime;
    }

    public TimeStamp(int id, string date, string fromTime, string toTime)
    {
        Id = id;
        DateString = date;
        FromTimeString = fromTime;
        ToTimeString = toTime;
    }

    public override string ToString()
    {
        return $"{Id}: {DateString} From: {FromTimeString}, To: {ToTimeString}, Duration: {Duration}";
    }

    private const string DateFormat = "MM.dd.yyyy";
    private const string ToFromFormat = @"hh:mm:ss";
    public int Id { get; set; }

    public DateTime Date { get; set; }

    public string DateString
    {
        get => Date.ToString(DateFormat);
        set => Date = DateTime.ParseExact(value, DateFormat, CultureInfo.InvariantCulture);
    }

    public TimeSpan FromTime { get; set; }

    public string FromTimeString
    {
        get => FromTime.ToString(ToFromFormat);
        set => FromTime = TimeSpan.ParseExact(value, ToFromFormat, CultureInfo.InvariantCulture);
    }

    public TimeSpan ToTime { get; set; }

    public string ToTimeString
    {
        get => ToTime.ToString(ToFromFormat);
        set => ToTime = TimeSpan.ParseExact(value, ToFromFormat, CultureInfo.InvariantCulture);
    }

    public TimeSpan Duration => ToTime - FromTime;

    public string DurationString => Duration.ToString(ToFromFormat);
}
 

Помимо информации об имени, единственным существенным отличием является то, что у второго класса есть конструктор по умолчанию.

Обратите внимание, что я переопределяю ToString в каждом классе. Это очень удобно для отладки.

Теперь я инициализирую список первого типа

Пожалуйста, когда вы делаете что-то подобное, включайте свои данные в какой-нибудь текстовый формат. Половина усилий в этом ответе была связана с повторным вводом ваших данных

 public static List<BreakTime> Initialize()
{
    var returnedBreakTimes = new List<BreakTime>
    {
        new BreakTime(97, "John", "Snow", new DateTime(2021, 2, 1), new TimeSpan(12, 59, 0), new TimeSpan(16, 14, 0) ),
        new BreakTime(97, "John", "Snow", new DateTime(2021, 2, 1), new TimeSpan(16, 19, 0), new TimeSpan(16, 54, 0) ),
        new BreakTime(97, "John", "Snow", new DateTime(2021, 2, 1), new TimeSpan(17, 43, 0), new TimeSpan(19, 07, 0) ),
        new BreakTime(97, "John", "Snow", new DateTime(2021, 2, 1), new TimeSpan(19, 11, 0), new TimeSpan(20, 48, 0) ),
        new BreakTime(97, "John", "Snow", new DateTime(2021, 2, 1), new TimeSpan(20, 56, 0), new TimeSpan(22, 48, 0) ),

        //do the second day using strings (to test things out)
        new BreakTime(97, "John", "Snow", "02.02.2021", "12:56:00", "15:58:00" ),
        new BreakTime(97, "John", "Snow", "02.02.2021", "16:48:00", "18:36:00" ),
        new BreakTime(97, "John", "Snow", "02.02.2021", "18:40:00", "19:56:00" ),
        new BreakTime(97, "John", "Snow", "02.02.2021", "20:02:00", "21:45:00" ),
        new BreakTime(97, "John", "Snow", "02.02.2021", "21:49:00", "22:39:00" ),
    };

#if DEBUG
    foreach (var item in returnedBreakTimes)
    {
        Debug.WriteLine(item);
    }
#endif
    return returnedBreakTimes;
}
 

Обратите внимание, что я использую как Дату/Время, так и строку для инициализации. Таким образом, я могу убедиться, что правильно интерпретирую форматы и т. Д.

Наконец, я перехожу к той части, которая вас интересует, анализируя данные слева от вашего листа Excel и получая данные справа. Для этого я просматриваю исходные данные и составляю окончательные данные, решая, изменился ли день и т. Д. Код не очень сложный.

Это выглядит так:

 public static List<TimeStamp> ParseFromBreakTimes(List<BreakTime> breakTimes)
{
    var listToReturn = new List<TimeStamp>();
    var currentDate = default(DateTime);
    var previousToTime = default(TimeSpan);

    foreach (var breakTime in breakTimes)
    {
        if (breakTime.Date != currentDate) //change in date
        {
            previousToTime = new TimeSpan();
        }

        if (previousToTime != default(TimeSpan))
        {
            listToReturn.Add(new TimeStamp(breakTime.Id, breakTime.Date, previousToTime, breakTime.FromTime));
        }

        previousToTime = breakTime.ToTime;
        currentDate = breakTime.Date;
    }

    return listToReturn;
}
 

Чтобы проверить это, я добавил этот код в #if DEBUG блок в Initialize подпрограмме:

 var timestamps = ParseFromBreakTimes(returnedBreakTimes);
foreach (var item in timestamps)
{
    Debug.WriteLine(item);
}
 

Когда он запускается, вывод выглядит следующим образом (обратите внимание, я добавил его в Duration качестве теста/демонстрации):

 97: John Snow: 01.02.2021 From: 12:59:00, To: 16:14:00
97: John Snow: 01.02.2021 From: 16:19:00, To: 16:54:00
97: John Snow: 01.02.2021 From: 17:43:00, To: 19:07:00
97: John Snow: 01.02.2021 From: 19:11:00, To: 20:48:00
97: John Snow: 01.02.2021 From: 20:56:00, To: 22:48:00
97: John Snow: 02.02.2021 From: 12:56:00, To: 15:58:00
97: John Snow: 02.02.2021 From: 16:48:00, To: 18:36:00
97: John Snow: 02.02.2021 From: 18:40:00, To: 19:56:00
97: John Snow: 02.02.2021 From: 20:02:00, To: 21:45:00
97: John Snow: 02.02.2021 From: 21:49:00, To: 22:39:00

97: 02.01.2021 From: 16:14:00, To: 16:19:00, Duration: 00:05:00
97: 02.01.2021 From: 16:54:00, To: 17:43:00, Duration: 00:49:00
97: 02.01.2021 From: 19:07:00, To: 19:11:00, Duration: 00:04:00
97: 02.01.2021 From: 20:48:00, To: 20:56:00, Duration: 00:08:00
97: 02.02.2021 From: 15:58:00, To: 16:48:00, Duration: 00:50:00
97: 02.02.2021 From: 18:36:00, To: 18:40:00, Duration: 00:04:00
97: 02.02.2021 From: 19:56:00, To: 20:02:00, Duration: 00:06:00
97: 02.02.2021 From: 21:45:00, To: 21:49:00, Duration: 00:04:00
 

Комментарии:

1. Большое вам спасибо за ваше время и усилия! Вы правы насчет правильного использования времени и промежутков времени, я обязательно обращу на это внимание в следующий раз, когда они мне понадобятся.