Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# DateTime range exclude specific ranges

Tags:

c#

datetime

enums

I have a Flags Enum:

[Flags]
public enum WeekDays
{

    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64
}

and then a class

public class Task
{
     public Task()
     {

     }

     public int Days { get; set; }
     public TimeSpan Start { get; set; }
     public TimeSpan End { get; set; } 
}

Assuming the following values

 Task task = new Task();
 task.Days = 7; // from WeekDays enum: 7 = Monday, Tuesday, Wednesday
 task.Start = TimeSpan.Parse("00:00"); //midnight
 task.End =   TimeSpan.Parse("06:00"); //morning 06 AM

and a specific range period:

 DateTime now = DateTime.Now;
 DateTime start = now.AddDays(-6);

 TimeSpan time = now - start;

 double toatlSeconds = time.TotalSeconds; // get total seconds between the start and now datetime range

How can substract from the totalSeconds the task's totalSeconds?

So basically I want to exclude from the totalSeconds the period when the task occures which is every Monday, Tuesday, Wednesday between 00:00 and 06:00 which is 3x6hours = 18 hours which is 64800 seconds?

I need to do this by applying the mask to the time range ...

like image 698
user2818430 Avatar asked Nov 10 '22 06:11

user2818430


1 Answers

This should be what you are looking for. I decided to rename your class because there is already a Task class in C#. Also I made it have factory method for creation as you want to do some checks on the values. Also note the need for a mapping from the DayOfWeek enum used by DateTime to your WeekDays enum. Basically you need to iterate through each day that exists between the two DateTimes to see how much time should be removed from the total duration.

public class ScheduledTask
{
    private ScheduledTask() { }

    public static ScheduledTask CreateTask(TimeSpan start, TimeSpan end, WeekDays days)
    {
        if (start < TimeSpan.Zero || start >= TimeSpan.FromDays(1))
        {
            throw new ArgumentOutOfRangeException("start");
        }

        if (end < TimeSpan.Zero || end >= TimeSpan.FromDays(1))
        {
            throw new ArgumentOutOfRangeException("end");
        }

        if (start > end)
        {
            throw new ArgumentException("End cannot be before Start", "end");
        }

        if ((int)days < 1 || (int)days >= 128)
        {
            throw new ArgumentOutOfRangeException("days");
        }

        return new ScheduledTask { Start = start, End = end, Days = days };
    }

    public WeekDays Days { get; private set; }
    public TimeSpan Start { get; private set; }
    public TimeSpan End { get; private set; }

    public TimeSpan DifferenceMinusTaskTime(DateTime from, DateTime to)
    {
        var actualFrom = from;
        var actualTo = to;
        if (from > to)
        {
            actualFrom = to;
            actualTo = from;
        }

        TimeSpan duration = TimeSpan.Zero;
        DateTime dayStart = actualFrom;
        DateTime dayEnd = actualFrom.Date == actualTo.Date 
                          ? actualTo 
                          : actualFrom.Date.AddDays(1);
        bool endIsNextDay = dayEnd.Date > dayStart.Date;

        while (dayStart < actualTo)
        {
            duration += dayEnd - dayStart;

            if (Days.HasFlag(mapping[dayStart.DayOfWeek]) 
                && dayStart.TimeOfDay < End
                && (dayEnd.TimeOfDay > Start || endIsNextDay))
            {
                if (dayStart.TimeOfDay < Start)
                {
                    if (dayEnd.TimeOfDay > End || endIsNextDay)
                    {
                        duration -= End - Start;
                    }
                    else
                    {
                        duration -= dayEnd.TimeOfDay - Start;
                    }
                }
                else 
                {
                    if (dayEnd.TimeOfDay > End || endIsNextDay)
                    {
                        duration -= End - dayStart.TimeOfDay;
                    }
                    else
                    {
                        duration -= dayEnd - dayStart;
                    }
                }
            }

            dayStart = dayEnd;
            dayEnd = dayStart.Date == actualTo.Date 
                     ? actualTo 
                     : dayStart.Date.AddDays(1);
            endIsNextDay = dayEnd.Date > dayStart.Date;
        }

        return from > to ? TimeSpan.Zero - duration : duration;
    }

    private Dictionary<DayOfWeek, WeekDays> mapping = 
        new Dictionary<DayOfWeek, WeekDays>
        {
            { DayOfWeek.Monday, WeekDays.Monday },
            { DayOfWeek.Tuesday, WeekDays.Tuesday },
            { DayOfWeek.Wednesday, WeekDays.Wednesday },
            { DayOfWeek.Thursday, WeekDays.Thursday },
            { DayOfWeek.Friday, WeekDays.Friday },
            { DayOfWeek.Saturday, WeekDays.Saturday },
            { DayOfWeek.Sunday, WeekDays.Sunday }
        };
}

With the above the following

var task = ScheduledTask.CreateTask(
    TimeSpan.FromHours(2),
    TimeSpan.FromHours(8),
    WeekDays.Monday | WeekDays.Tuesday | WeekDays.Wednesday);

Console.WriteLine(
    task.DifferenceMinusTaskTime(
        new DateTime(2015, 8, 24), 
        new DateTime(2015, 8, 27))); // 2 days 6 hours
Console.WriteLine(
    task.DifferenceMinusTaskTime(
       new DateTime(2015, 8, 27), 
       new DateTime(2015, 8, 24))); // -2 days 6 hours
Console.WriteLine(
    task.DifferenceMinusTaskTime(
        new DateTime(2015, 8, 28), 
        new DateTime(2015, 8, 29))); // 1 day
Console.WriteLine(
    task.DifferenceMinusTaskTime(
        new DateTime(2015, 8, 24), 
        new DateTime(2015, 8, 24, 10, 0, 0))); // 4 hours
Console.WriteLine(
    task.DifferenceMinusTaskTime(
        new DateTime(2015, 8, 24), 
        new DateTime(2015, 8, 24, 4, 0, 0))); // 2 hours
Console.WriteLine(
    task.DifferenceMinusTaskTime(
       new DateTime(2015, 8, 24, 4, 0, 0), 
       new DateTime(2015, 8, 24, 6, 0, 0))); // 0 hours
Console.WriteLine(
    task.DifferenceMinusTaskTime(
        new DateTime(2015, 8, 24, 4, 0, 0), 
        new DateTime(2015, 8, 24, 10, 0, 0))); // 2 hours
Console.WriteLine(
    task.DifferenceMinusTaskTime(
        new DateTime(2015, 8, 24, 10, 0, 0), 
        new DateTime(2015, 8, 24, 14, 0, 0))); // 4 hours

will output

2.06:00:00

-2.06:00:00

1.00:00:00

04:00:00

02:00:00

00:00:00

02:00:00

04:00:00

like image 149
juharr Avatar answered Nov 15 '22 11:11

juharr