0

I need a little help on how to properly check if a worker has been absent from a block longer than 15 min. I do different checks for the worker during his working hours and check his presence in time blocks. This is my attempt at a code, which does not currently work for me to: Block class (in which I get the start and end of a block)

 public class Block
    {
        public DateTime Start { get; set; }
        public DateTime End { get; set; }
    }

Example with a block list, two blocks (in the morning and in the afternoon) this is how the time in the list looks like

 List<Block> blocks = new List<Block>();

![enter image description here

[0] = End{12/30/1899 11:45:00 AM}
[0] = Start{12/30/1899 8:30:00 AM}
[1] = End{12/30/1899 5:00:00 PM}
[1] = Start{12/30/1899 1:15:00 PM}

Now I have the Arrivals and Departures of workers that I get in this form in the database:

 List<Presence> presence = new List<Presence>();

time received in the presence list:

arrival                     departure 
1899-12-30 08:03:00.000;    1899-12-30 09:21:00.000
1899-12-30 09:36:00.000;    1899-12-30 10:34:00.000
1899-12-30 10:45:00.000;    1899-12-30 12:05:00.000
1899-12-30 13:03:00.000;    1899-12-30 14:24:00.000
1899-12-30 14:34:00.000;    1899-12-30 16:14:00.000
1899-12-30 16:27:00.000;    1899-12-30 18:02:00.000

enter image description here

So for each of the blocks I need to check if the worker is absent> 15 min. how can i check that?

This is my current code.

public class Block
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
}

public class Presence
{
    public DateTime Arrival { get; set; }
    public DateTime Departure { get; set; }

    public TimeSpan FlightTime => Arrival - Departure;
}

public static class PresenceExtensions
{
    public static TimeSpan GetTotalFlightTime(this IEnumerable<Presence> presences)
    {
        return new TimeSpan(presences.Sum(r => r.FlightTime.Ticks));
    }
}

public static class BlockExtensions
{
    public static Dictionary<Block, IEnumerable<Presence>> FilterPresences(this IEnumerable<Block> blocks, IEnumerable<Presence> presences)
    {
        Dictionary<Block, IEnumerable<Presence>> result = new Dictionary<Block, IEnumerable<Presence>>();
        foreach (Block block in blocks)
        {
            result.Add(block, presences.Where(p => p.Arrival <= block.End && p.Departure >= block.Start));
        }

        return result;
    }
}

usage:

List<Block> blocks = new List<Block>();
List<Presence> presences = new List<Presence>();

Dictionary<Block, IEnumerable<Presence>> filteredList = blocks.FilterPresences(presences);

foreach (KeyValuePair<Block, IEnumerable<Presence>> pair in filteredList)
{
      if (pair.Value.GetTotalFlightTime() > 15){
      Console.WriteLine("The worker is out of the block for more than 15 min")}

}

A colleague of mine suggested to me Algorithm to detect overlapping periods, but I couldn't calculate the amount between this time, and i really need to solve this, i've been having a problem with this for a few days now. Please understand.

I tried this code with his example but did not find the correct solution.

 if(presences.Any(x=>blocks.Any(b=> x.Arrival < b.End && b.Start < x.Departure)))
                {
                    errors.Add("The worker is out of the block for more than 15 min");
                        hasErrors = true;
                }

I also set different options when a message should be reported to cancel a block> 15 min.   Block time:

  First block:   08:30-11:45   Second block:   13: 15-17:00

The worker works from:

 1. (first Day) 07:00-12:00 | 13:00-16:55 // all right
    2. (second Day) 08:40-11:39 | 13:00-16:55 // missed in the first block> 15 min
    3. (third Day) 08:30-09:00, 09:10-10:00, 10:15-12: 01 | 13:00-16: 55 // missed in the first block> 15 min
   4. (fourt Day) 08:00-14:00 // Missed in the second block> 15 min
   5. (fifth Day)  12:00-17:00 // Missed in first block> 15 min


public class Presence
{
    public Presence(DateTime Arrival, DateTime Departure) { this.Arrival = Arrival; this.Departure = Departure; }
    public DateTime Arrival { get; set; }
    public DateTime Departure { get; set; }
    public DateTime Date { get; set; } //added
    public TimeSpan FlightTime;
    public int UserID {get; set;} //added
}

Date                               Arrival                 Departure
2020-12-23 00:00:00.000 1899-12-30 08:30:00.000 1899-12-30 10:15:00.000
2020-12-23 00:00:00.000 1899-12-30 10:20:00.000 1899-12-30 12:20:00.000
2020-12-23 00:00:00.000 1899-12-30 13:25:00.000 1899-12-30 15:00:00.000
2020-12-23 00:00:00.000 1899-12-30 15:05:00.000 1899-12-30 17:00:00.000
2020-12-23 00:00:00.000 1899-12-30 17:05:00.000 1899-12-30 18:30:00.000

from database prtsc

enter image description here

For example by @Rufus It looks good, shows me in almost all places how it should look for a message when it has been missing for more than 15 min, only one place does not show: The worker worked: from

new Presence { 
Arrival = DateTime.Parse("1899-12-30 12:53:00.000"), 
Departure = DateTime.Parse("1899-12-30 15:07:00.000") }, 

And in that day there is only one block and it is from

new Block { Start = DateTime.Parse("12/30/1899 08:00:00 AM"), 
End = DateTime.Parse("12/30/1899 16:30:00 PM"), },
Michael
  • 235
  • 1
  • 8
  • 1
    All those workers are dead now, does it matter? `1899` was `121` years ago! :) – Rufus L Jan 09 '20 at 08:17
  • @RufusL only timeFormat is used, it is relevant, the date is fixed. – Michael Jan 09 '20 at 08:20
  • Wouldn't it be easier to check the times workers are _absent_ instead of checking times they are _not present_ ? (See, where I am going?) – Fildor Jan 09 '20 at 08:21
  • @Fildor not clear what you think? – Michael Jan 09 '20 at 08:22
  • Invert the View on the times. Now you have "Worker is _present_ from X until Y". Take this and convert it to "Worker is _absent_ from A until B". Then you can filter this with your blocks and check the remaining timespans (of _absence_). – Fildor Jan 09 '20 at 08:24
  • @Fildor if a little help based on my code could mean a lot to me? – Michael Jan 09 '20 at 08:30
  • Busy at the moment, maybe in the afternoon (Central Europe Time) ... – Fildor Jan 09 '20 at 08:31
  • @Fildor ok thanks, i hope someone else can help me. – Michael Jan 09 '20 at 08:32
  • FYI: All the arrival values are less than their departure counterparts, which makes all the flight times a negative number. Also, `if (pair.Value.GetTotalFlightTime() > 15)` should result in a compile error. It should be `pair.Value.GetTotalFlightTime().TotalMinutes` – Rufus L Jan 09 '20 at 09:08
  • @RufusL that in this case get total flight time is always in minus or is 0. Can someone finally give me some solution to calculate quit outside the blocks? – Michael Jan 09 '20 at 09:47
  • @RufusL one question, is this really a difficult task so no one can help me or did I ask a bad question? i need help and i don't see anyone wanting to help me, i even got one negative on my question for some unknown reason ... – Michael Jan 09 '20 at 09:49
  • It shouldn't be too hard to solve, let me take a look. – Rufus L Jan 09 '20 at 11:45

2 Answers2

1

There are a few problems with the code.

First, the FlightTime calculation will always return a negative number because it's subtracting the Departure date from the Arrival date. This might make sense if the Departure date was from one Presence and the Arrival date was for the next Presence, but it doesn't work in the context of a single Presence.

Next, and related to the above, the GetTotalFlightTime is just returning a sum of the FlightTime for each Presence, instead of examining the time between consecutive presences within a Block.

To solve this problem I added a method to the Block class that takes in a List<Presence> and returns the number of absent minutes. This method filters out any Presence items that don't overlap with the block (just as your extension method was doing), and then it calculates the "absent time" by looking at the timespan between consecutive presences. It also checks to see if the first Presence arrives after the block's start, and if so, adds that time as well:

public class Block
{
    public DateTime Start { get; set; }
    public DateTime End { get; set; }

    public double MinutesAbsent(IEnumerable<Presence> presences)
    {
        // Given a list of presences, select only those that overlap this block 
        var relevantPresences = Presence.CombineOverlapping(
            presences?.Where(p => p?.OverlapsWith(this) == true))?
            .OrderBy(p => p.Arrival)
            .ToList();

        // If there aren't any relevant presences, return the total minutes for this block
        if (relevantPresences == null || relevantPresences.Count == 0)
            return (End - Start).TotalMinutes;

        // Get any absent minutes at the start of the block by determining
        // if the first presence arrived after the block's start. If it did,
        // begin with the difference between the block's Start and the 
        // first presence's Arrival. 
        var minutesAbsent = relevantPresences.First().Arrival > Start
            ? (relevantPresences.First().Arrival - Start).TotalMinutes
            : 0;

        // Then add the number of minutes between each presence's 
        // Departure and the next presence's Arrival
        for (var i = 0; i < relevantPresences.Count - 1; i++)
        {
            minutesAbsent += (relevantPresences[i + 1].Arrival -
                              relevantPresences[i].Departure).TotalMinutes;
        }

        // Finally, add any minutes after the last presence 
        // if it departed before the end of the block
        if (relevantPresences.Last().Departure < End)
            minutesAbsent += (End - relevantPresences.Last().Departure).TotalMinutes;

        return minutesAbsent;
    }
}

public class Presence
{
    public DateTime Arrival { get; set; }
    public DateTime Departure { get; set; }

    public TimeSpan FlightTime => Arrival - Departure;

    public Presence(){ }

    public Presence(DateTime arrival, DateTime departure)
    {
        Arrival = arrival;
        Departure = departure;
    }

    public bool OverlapsWith(Block block)
    {
        return Arrival < block?.End && Departure > block.Start;
    }

    public static IEnumerable<Presence> CombineOverlapping(IEnumerable<Presence> presences)
    {
        var items = presences?.ToList()
            .Where(p => p != null)
            .OrderBy(presence => presence.Arrival)
            .ToList();

        if (items?.Any() != true) return items;

        var combined = new List<Presence>();
        var current = items.First();

        for (var i = 1; i < items.Count; i++)
        {
            if (items[i].Arrival <= current.Departure)
            {
                if (items[i].Departure > current.Departure)
                {
                    current.Departure = items[i].Departure;
                }
            }
            else
            {
                combined.Add(current);
                current = items[i];
            }
        }

        combined.Add(current);

        return combined;
    }

    public override string ToString()
    {
        return $"{Arrival} - {Departure}";
    }
}

In use this looks like:

static void Main()
{
    var blocks = GetSampleBlockData();
    var presences = GetSamplePresenceData();

    for(var i = 0; i < blocks.Count; i++)
    {
        var minutesAbsent = blocks[i].MinutesAbsent(presences);

        if (minutesAbsent > 15)
        {
            Console.WriteLine("Error: User was absent for "+
                $"{minutesAbsent} minutes in block # {i + 1}");
        }
    }

    GetKeyFromUser("\nPress any key to exit...");
}

Output

enter image description here


For completeness, here are the methods that generate the sample data:

private static List<Block> GetSampleBlockData()
{
    return new List<Block>
    {
        new Block
        {
            Start = DateTime.Parse("12/30/1899 8:30:00 AM"),
            End = DateTime.Parse("12/30/1899 11:45:00 AM"),
        },
        new Block
        {
            Start = DateTime.Parse("12/30/1899 1:15:00 PM"),
            End = DateTime.Parse("12/30/1899 5:00:00 PM"),
        },
    };
}

private static List<Presence> GetSamplePresenceData()
{
    return new List<Presence>
    {
        new Presence
        {
            Arrival = DateTime.Parse("1899-12-30 08:03:00.000"),
            Departure = DateTime.Parse("1899-12-30 09:21:00.000")
        },
        new Presence
        {
            Arrival = DateTime.Parse("1899-12-30 09:36:00.000"),
            Departure = DateTime.Parse("1899-12-30 10:34:00.000")
        },
        new Presence
        {
            Arrival = DateTime.Parse("1899-12-30 10:45:00.000"),
            Departure = DateTime.Parse("1899-12-30 12:05:00.000")
        },
        new Presence
        {
            Arrival = DateTime.Parse("1899-12-30 13:03:00.000"),
            Departure = DateTime.Parse("1899-12-30 14:24:00.000")
        },
        new Presence
        {
            Arrival = DateTime.Parse("1899-12-30 14:34:00.000"),
            Departure = DateTime.Parse("1899-12-30 16:14:00.000")
        },
        new Presence
        {
            Arrival = DateTime.Parse("1899-12-30 16:27:00.000"),
            Departure = DateTime.Parse("1899-12-30 18:02:00.000")
        },
    };
}
Rufus L
  • 36,127
  • 5
  • 30
  • 43
  • I get this error =>System.InvalidOperationException: "The sequence contains no elements." here=>var minutesAbsent = relevantPresences.First().Arrival > Start ? (Start - relevantPresences.First().Arrival).TotalMinutes : 0; – Michael Jan 09 '20 at 13:12
  • @Michael thanks! I added a `null` check to fix this – Rufus L Jan 09 '20 at 20:58
  • I think we did not get along well, please look again at my question. I have added some examples at the end of the day, when an absence from the block> 15 min should be reported. – Michael Jan 11 '20 at 08:35
  • Oh, I see - made a fix so it returns the all the minutes in the block (instead of `0`) if there were no overlapping presences. It should work for all your scenarios now, I believe. – Rufus L Jan 13 '20 at 18:30
  • Again I get an error ("System.InvalidOperationException:" The sequence contains no elements. "") On the part => var minutesAbsent Can you correct your code? and the question is, does the code include you checking if the employee is landing at all on the block? did you read my question that I completed by the end?  Thank you very much for your hard work and help. – Michael Jan 14 '20 at 07:03
  • Argh, sorry, I only checked for null, but not if the list was empty! Please try updated sample, and note that I added a couple of new methods to the `Presence` class also. And yes, that's what I meant in my previous comment - if no presences overlap with the block (i.e. the employee did not land on it), then it returns the entire duration of the block (in minutes) as "absent time". – Rufus L Jan 14 '20 at 18:27
  • It looks good, shows me in almost all places how it should look for a message when it has been missing for more than 15 min, only one place does not show: The worker worked: from new Presence { Arrival = DateTime.Parse("1899-12-30 12:53:00.000"), Departure = DateTime.Parse("1899-12-30 15:07:00.000") }, And in that day there is only one block and it is from new Block { Start = DateTime.Parse("12/30/1899 08:00:00 AM"), End = DateTime.Parse("12/30/1899 16:30:00 PM"), }, – Michael Jan 14 '20 at 19:38
  • Does this code omit this case in your code as well? Thanks a lot for everything !!! – Michael Jan 14 '20 at 19:38
  • I corrected the question, at the end there is only one other condition under which the code leaves you with an exception message> 15 min, can you check it also please? Thanks a lot for your hard work, you really helped me a lot. – Michael Jan 14 '20 at 19:44
  • Ah, I see...I don't get an exception message, but it does produce a negative number - I had `Start - Arrival` instead of `Arrival - Start` in one of the checks. I fixed that now. – Rufus L Jan 14 '20 at 19:58
  • that's it, great! Thank you so much! You really did a great job! – Michael Jan 14 '20 at 20:22
0

Could you please check below possible solution? It can be enhanced again if it does not meet your needs or if it has missing parts.

[v7] 2020/01/15 UTC 10:41 - Some further fixes are done.

using System;
using System.Collections.Generic;
using System.Linq;

public class Block
{
    public Block(String ID, DateTime Start, DateTime End) { this.ID = ID; this.Start = Start; this.End = End; }
    public DateTime Start { get; set; }
    public DateTime End { get; set; }
    public String ID { get; set; }
}

public class Presence
{
    public Presence(DateTime Arrival, DateTime Departure) { this.Arrival = Arrival; this.Departure = Departure; }
    public DateTime Arrival { get; set; }
    public DateTime Departure { get; set; }

    public TimeSpan FlightTime;
}

public class Worker
{
    public Worker(string ssn) { this.ssn = ssn; }
    public String ssn { get; set; }
    public List<Block> blocksWorked = new List<Block>();
}

public static class PresenceExtensions
{
    public static IEnumerable<Presence> CalculateFlightTimes(this IEnumerable<Presence> presences, Block block, Worker worker)
    {
        int iterationIndex = 0;

        if (presences.Count() == 0)
        {
            Presence presence = new Presence(block.Start, block.End);
            presence.FlightTime = block.End - block.Start;
            presences = presences.Concat(new[] { presence });
        }
        else
        {
            IEnumerable<Presence> absentAfterOneBlockEndPresences = presences.Where(p => p.Arrival > block.End);
            IEnumerable<Presence> absentBeforeOneBlockStartPresences = presences.Where(p => p.Departure < block.Start);
            presences = presences.Except(absentAfterOneBlockEndPresences).Except(absentBeforeOneBlockStartPresences);

            foreach (Presence presence in presences)
            {
                if (iterationIndex == 0)
                {
                    presence.FlightTime = presence.Arrival > block.Start ? presence.Arrival - block.Start : TimeSpan.Zero;

                    if (iterationIndex == presences.Count() - 1){
                        presence.FlightTime += presence.Departure < block.End ? block.End - presence.Departure : TimeSpan.Zero;
                    }
                }
                else
                {
                    presence.FlightTime = presence.Arrival - presences.ElementAt(iterationIndex - 1).Departure;

                    if (iterationIndex == presences.Count() - 1){
                        presence.FlightTime += presence.Departure < block.End ? block.End - presence.Departure : TimeSpan.Zero;
                    }
                }   

                iterationIndex++;   
            }   

            worker.blocksWorked.Add(block);

            // Calculating arrival after block ends
            if (absentAfterOneBlockEndPresences.Count() > 0)
            {
                // If there are no presences related to current block
                if (presences.Count() == 0)
                {
                    Presence firstPresence = absentAfterOneBlockEndPresences.ElementAt(0);
                    firstPresence.FlightTime = firstPresence.Arrival - block.End;   
                    presences = presences.Concat(new[] { firstPresence });

                    // Remove the block from worked blocks because worker is absent
                    if (worker.blocksWorked.Contains(block))
                    {
                        worker.blocksWorked.Remove(block);
                    }
                }           
            }

            // Calculating departure before block starts (**This may not be needed**. If it is not needed, this code part can be safely disabled)
            // See last presence (new Presence(new DateTime(1899, 12, 6, 8, 0, 0), new DateTime(1899, 12, 6, 12, 59, 0)))
            // Ex. 08:00 - 12:59
            if (absentBeforeOneBlockStartPresences.Count() > 0)
            {       
                // If there are no presences related to current block
                if (presences.Count() == 0)
                {
                    Presence lastPresence = absentBeforeOneBlockStartPresences.ElementAt(absentBeforeOneBlockStartPresences.Count() - 1);
                    lastPresence.FlightTime = block.Start - lastPresence.Departure; 
                    presences = presences.Concat(new[] { lastPresence });

                    // Remove the block from worked blocks because worker is absent
                    if (worker.blocksWorked.Contains(block))
                    {
                        worker.blocksWorked.Remove(block);
                    }
                }           
            }           
        }

        return presences;
    }
    public static TimeSpan GetTotalFlightTime(this IEnumerable<Presence> presences)
    {
        return new TimeSpan(presences.Sum(r => r.FlightTime.Ticks));
    }
    public static long TicksToMinutes(this TimeSpan ts)
    {
        return (ts.Ticks / (60 * 10000000));
    }
    public static bool SameDayControl(this DateTime dateTime1, DateTime dateTime2)
    {
        return dateTime1.Date.Equals(dateTime2.Date);
    }
}

public static class BlockExtensions
{
    public static Dictionary<Block, IEnumerable<Presence>> FilterPresences(this IEnumerable<Block> blocks, IEnumerable<Presence> presences)
    {
        Dictionary<Block, IEnumerable<Presence>> result = new Dictionary<Block, IEnumerable<Presence>>();
        foreach (Block block in blocks)
        {
            // Filtering same date block and presences regarding absent times before block starts and after block ends
            List<Presence> ieP = new List<Presence>();
            ieP.AddRange(presences.Where(p => block.End.SameDayControl(p.Arrival.Date) && block.Start.SameDayControl(p.Departure.Date) && (p.Arrival <= block.End && p.Departure >= block.Start)));
            Presence after = presences.Where(p => block.End.SameDayControl(p.Arrival.Date) && block.Start.SameDayControl(p.Departure.Date) && (p.Arrival > block.End)).OrderBy(p => p.Arrival).FirstOrDefault();
            if(after != null)
            {
                ieP.Add(after); 
            }
            Presence before = presences.Where(p => block.End.SameDayControl(p.Arrival.Date) && block.Start.SameDayControl(p.Departure.Date) && (p.Departure < block.Start)).OrderBy(p => p.Departure).LastOrDefault();
            if(before != null)
            {
                ieP.Add(before);    
            }

            result.Add(block, (IEnumerable<Presence>) ieP);
        }

        return result;
    }
}

public static class ListExtensions
{
    public static String ToStr(this List<Block> blockList)
    {
        String str = "";
        int blockIndex = 0;

        foreach (Block block in blockList)
        {
            str += block.ID + ", ";

            if (blockIndex == blockList.Count - 1)
            {
                str = str.Substring(0, str.Length - 2);
            }

            blockIndex++;
        }

        return str;
    }
}

public class AbsentDetectionProgram
{
    public static void Main()
    {
        Console.WriteLine("Process started..\n");

        List<Block> blocks = new List<Block>();
        // First blocks example
        blocks.Add(new Block("1", new DateTime(1899, 12, 30, 8, 30, 0), new DateTime(1899, 12, 30, 11, 45, 0)));
        blocks.Add(new Block("2", new DateTime(1899, 12, 30, 13, 15, 0), new DateTime(1899, 12, 30, 17, 0, 0)));
        blocks.Add(new Block("3", new DateTime(1899, 12, 30, 18, 15, 0), new DateTime(1899, 12, 30, 22, 0, 0)));

        // Second blocks example
        blocks.Add(new Block("4", new DateTime(1899, 12, 1, 8, 30, 0), new DateTime(1899, 12, 1, 11, 45, 0)));
        blocks.Add(new Block("5", new DateTime(1899, 12, 1, 13, 15, 0), new DateTime(1899, 12, 1, 17, 0, 0)));
        blocks.Add(new Block("6", new DateTime(1899, 12, 2, 8, 30, 0), new DateTime(1899, 12, 2, 11, 45, 0)));
        blocks.Add(new Block("7", new DateTime(1899, 12, 2, 13, 15, 0), new DateTime(1899, 12, 2, 17, 0, 0)));
        blocks.Add(new Block("8", new DateTime(1899, 12, 3, 8, 30, 0), new DateTime(1899, 12, 3, 11, 45, 0)));
        blocks.Add(new Block("9", new DateTime(1899, 12, 3, 13, 15, 0), new DateTime(1899, 12, 3, 17, 0, 0)));
        blocks.Add(new Block("10", new DateTime(1899, 12, 4, 8, 30, 0), new DateTime(1899, 12, 4, 11, 45, 0)));
        blocks.Add(new Block("11", new DateTime(1899, 12, 4, 13, 15, 0), new DateTime(1899, 12, 4, 17, 0, 0)));
        blocks.Add(new Block("12", new DateTime(1899, 12, 5, 8, 30, 0), new DateTime(1899, 12, 5, 11, 45, 0)));
        blocks.Add(new Block("13", new DateTime(1899, 12, 5, 13, 15, 0), new DateTime(1899, 12, 5, 17, 0, 0)));
        blocks.Add(new Block("14", new DateTime(1899, 12, 6, 8, 30, 0), new DateTime(1899, 12, 6, 11, 45, 0)));
        blocks.Add(new Block("15", new DateTime(1899, 12, 6, 13, 15, 0), new DateTime(1899, 12, 6, 17, 0, 0)));

        // Only two worker is calculated
        Worker worker1 = new Worker("1234567890");
        Worker worker2 = new Worker("1234567891");

        List<Presence> presencesForWorker1 = new List<Presence>();
        presencesForWorker1.Add(new Presence(new DateTime(1899, 12, 30, 8, 3, 0), new DateTime(1899, 12, 30, 9, 21, 0)));
        presencesForWorker1.Add(new Presence(new DateTime(1899, 12, 30, 9, 36, 0), new DateTime(1899, 12, 30, 10, 34, 0)));
        presencesForWorker1.Add(new Presence(new DateTime(1899, 12, 30, 10, 45, 0), new DateTime(1899, 12, 30, 12, 5, 0)));
        presencesForWorker1.Add(new Presence(new DateTime(1899, 12, 30, 13, 3, 0), new DateTime(1899, 12, 30, 14, 24, 0)));
        presencesForWorker1.Add(new Presence(new DateTime(1899, 12, 30, 14, 34, 0), new DateTime(1899, 12, 30, 16, 14, 0)));
        presencesForWorker1.Add(new Presence(new DateTime(1899, 12, 30, 16, 27, 0), new DateTime(1899, 12, 30, 18, 2, 0)));

        List<Presence> presencesForWorker2 = new List<Presence>();
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 1, 7, 0, 0), new DateTime(1899, 12, 1, 12, 0, 0)));//
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 1, 13, 0, 0), new DateTime(1899, 12, 1, 16, 55, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 2, 8, 40, 0), new DateTime(1899, 12, 2, 11, 39, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 2, 13, 0, 0), new DateTime(1899, 12, 2, 16, 55, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 3, 8, 30, 0), new DateTime(1899, 12, 3, 9, 0, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 3, 9, 10, 0), new DateTime(1899, 12, 3, 10, 0, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 3, 10, 15, 0), new DateTime(1899, 12, 3, 12, 1, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 3, 13, 0, 0), new DateTime(1899, 12, 3, 16, 55, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 4, 8, 0, 0), new DateTime(1899, 12, 4, 14, 0, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 5, 12, 1, 0), new DateTime(1899, 12, 5, 17, 0, 0)));
        presencesForWorker2.Add(new Presence(new DateTime(1899, 12, 6, 8, 0, 0), new DateTime(1899, 12, 6, 12, 59, 0)));

        Dictionary<Worker, List<Presence>> workerPresenceDict = new Dictionary<Worker, List<Presence>>();
        workerPresenceDict.Add(worker1, presencesForWorker1);
        workerPresenceDict.Add(worker2, presencesForWorker2);

        foreach (KeyValuePair<Worker, List<Presence>> workerPresencePair in workerPresenceDict)
        {
            Worker worker = workerPresencePair.Key;
            List<Presence> presences = workerPresencePair.Value;
            Dictionary<Block, IEnumerable<Presence>> filteredList = blocks.FilterPresences(presences);

            foreach (KeyValuePair<Block, IEnumerable<Presence>> pair in filteredList)
            {
                Block block = pair.Key;
                IEnumerable<Presence> presencesIEN = pair.Value;
                presencesIEN = presencesIEN.CalculateFlightTimes(block, worker);
                long minuteDiff = presencesIEN.GetTotalFlightTime().TicksToMinutes();       

                // If there is more than 15 minutes difference and if the difference is smaller than the whole block time
                // If the minute difference is equals to whole block time, then we are assuming that worker didn't work in that block, so do not log                
                if (minuteDiff > 15 && minuteDiff < (block.End - block.Start).TicksToMinutes())
                {
                    Console.WriteLine("Worker #" + worker.ssn + " - Total Worker Flight Time: " + minuteDiff);
                    Console.WriteLine("Worker #" + worker.ssn + " - Worker Work Time Details: [Block Start: " + block.Start + " Block End: " + block.End + " In-Out Count: " + presencesIEN.Count() + "] The worker is out of the block for more than 15 min"); 
                }   
            }

            // Printing worked and not worked blocks (Logging purpose only)
            Console.WriteLine("\nSummary");
            Console.WriteLine("-------");
            if (worker.blocksWorked.Count == 0)
            {
                Console.WriteLine("Worker #" + worker.ssn + " didn't work in any block");
            }
            else
            {
                Console.WriteLine("Worker #" + worker.ssn + " worked in block(s) " + worker.blocksWorked.ToStr());
                Console.WriteLine("Worker #" + worker.ssn + " didn't work in block(s) " + blocks.Except(worker.blocksWorked).ToList().ToStr() + "\n");  
            }
        }

        Console.WriteLine("Process finished..");
    }
}

Output: Output Image

Live Example: https://dotnetfiddle.net/YDDPSK

Erdem Savasci
  • 697
  • 5
  • 12
  • thank you very much for your hard work, I used you code, it works but not in all conditions, for example: if a worker started at 2:00 pm and worked until 6:00 pm he should report again that he was not in the first block for 15 min. This also applies if the employee worked until 2 pm, then he was not in the second block. So checking is only important if the employee is absent outside the block> 15 min. (for each block separately> 15 min may be absent) – Michael Jan 09 '20 at 13:52
  • I think we did not get along well, please look again at my question. I have added some examples at the end of the day, when an absence from the block> 15 min should be reported. – Michael Jan 11 '20 at 08:35
  • why did you add SameDayControl control? are you checking somewhere in the code to see if it's an incoming time and delayed for the appropriate day? – Michael Jan 13 '20 at 08:50
  • @Michael That same day control was for filtering only. Now I have removed the other use which was in main method. Could you please check again if it meets your needs? I think this example code will provide an insight about the solution. – Erdem Savasci Jan 13 '20 at 10:57
  • thanks for your help, please correct your code again (I think you are using date checker from your arrival and departure, and for that I have a special parameter (field in the database)) for the day used in attendance class, I corrected the question and eventually added data from the database – Michael Jan 13 '20 at 13:17