Introduction


DateTime structure is a representation of time in date and time format. Whereas TimeSpan structure helps you to deal with a time interval, which means it represents a length of time, in C#. This article covers some of the basic applications of  DateTime and TimeSpan. DateTime can accept at most 8 parameters in its constructor, which are as follows:
	int year,
	int month,
	int day,
	int hour,
	int minute,
	int second,
	int millisecond,
	DateTimeKind kind

TimeSpan can accept at most 5 parameters in its constructor, which are as follows:
	int days,
	int hours,
	int minutes,
	int seconds,
	int milliseconds
Thus you can notice that we can use DateTime to represent any time in a date with time format and we can use TimeSpan to find interval between any DateTime very easily. Let's use TimeSpan structure to find the working day difference between two dates where we also take the weekend and bank holidays into consideration.

Pre-requisite and Assumption

In this example we will be implementing a method, which returns the difference in another structure, which is named as DaySpan and only consists of properties: Days, Hour, Minutes and Seconds. In this example, we could have simply returned the TimeSpan from the method but in that case, we wouldn't be able to explore some access features of TimeSpan.

The Code and It's Implementation 

If we have to find the difference between two DateTimes then we can simply use the method Subtract() of DateTime, which returns TimeSpan and has a signature as follows:
public TimeSpan Subtract(
    DateTime value
)

But using Subtract() method returns the TimeSpan (time interval) between two days without taking weekends or holidays into account in case if you may want to build a custom method that gets the working time interval between two dates. Follow along and I will show how we can achieve this.

The Algorithm


Before we proceed any further let's discuss the algorithm, which will be the heart of our custom method. The algorithm as follows:

// Working Algorithm
If FromDate > ToDate Then:
    return 0;
var timeDifference = ToDate.Subtract(FromDate)
while FromDate < ToDate:
    If FromDate == Saturday OR FromDate == Sunday Then:
        timeDifference = timeDifference.Subtract(timeSpanOfOneDay)
    If FromDate == BankHoliday
        timeDifference = timeDifference.Subtract(timeSpanOfOneDay)
    FromDate = FromDate.AddDays(1)
return timeDifference

The aforementioned algorithm shows the working principle of our custom method but when we implement this algorithm in our C# code, it can look a bit different, which we will explore little later. But before we proceed to convert this algorithm into our live C# code, let's discuss few methods and properties of TimeSpan, which will be useful for us to accomplish this task.

We can use the AddDays(int) method of DateTime to add the specified number of days to a DateTime as follows:
// A dummy date of 01/01/1900 00:00:00 is created
DateTime dummy = new DateTime(1900, 1, 1, 0, 0, 0);
DateTime nextDummyDay = dummy.AddDays(1); // AddDays() to add number of days

We can also use the AddHours(int) method of DateTime to add the specified number of hours and can use AddMinutes(int) to add specified minutes to the DateTime, similar to what is shown above. Majority of the time you will be using these methods to add days, hours, minutes, but there are other provisions as well and you can check the full list of the available methods here.

Another common property which we use of DateTimis  Ticks. This property accesses the number of ticks that represent the date and time of this instance. Another important property of DateTime, which is noteworthy is Now, which gets the current local date and time of the computer. In our exampl, we will use another property namedDayOfWeek, which will get the day of the date between Monday to Sunday of a week. Other noteworthy properties of DateTime but not inclusive of all in the list are as follows:

 Day  Gets the day of the month of the DateTime
 Hour  Gets the hour of the month of the DateTime
 Today  Gets the current date but the hour:minute:second is set to 12:00:00 AM of the current date and hence little different from Now property
 Year  Gets the year component of the instance of the DateTime

These are some of the popular access properties, which most developers use but there are many more and the full list can be found from the link in the reference.

Now, few of the important access properties of TimeSpan, which developers use most of the time are as follows:
TotalDays   Gets the total days representation of TimeSpan, which means TimeSpan instance is represented as a fractional day 
TotalHours  Gets the total hours representation of TimeSpan, which means TimeSpan instance is represented as a fractional hours
Days   Gets the number of whole days of the TimeSpan instance
Hours  Gets the number of whole hours of the TimeSpan instance
Minutes  Gets the number of whole minutes of the TimeSpan instance
Ticks  Get the total number of ticks that represents the TimeSpan instance

And some of the popular methods of TimeSpan, which developers use from time to time are as follows:
 Add(TimeSpan)  Adds the specified TimeSpan to the current instance of TimeSpan and returns it
 Equals(TimeSpan)  Checks if two instances of TimeSpan are equal or not
 Compare(TimeSpan)  Compare the specified TimeSpan with the current TimeSpan instance and returns an integer to show the difference between the two
 FromTicks(Int64)  Returns a time TimeSpan of specified time, which is represented as ticks
 Parse(String)  Convert the String into a TimeSpan if possible
 TryParse(String, out TimeSpan)  Try to convert the String into TimeSpan as the out parameter

The aforementioned properties and methods of TimeSpan structure are only tip of the iceberg and you can refer to this link to check all the available features of TimeSpan.

Now, that we know quite of bit of both DateTime and TimeSpan, lets covert our aforementioned algorithm into our live C# code as follows:

public DaySpan ComputeDaysDifference(DateTime ToDate, DateTime FromDate, DateTime[] BankHolidays, bool WorkOnSaturday = false,bool WorkOnSunday = false)
        {
            DateTime TDate = new DateTime(FromDate.Year, FromDate.Month, FromDate.Day, FromDate.Hour, FromDate.Minute, FromDate.Second, FromDate.Millisecond);
            if (ToDate.Year == 1900)
            {
                ToDate = DateTime.Now;
            }
            int DaysOverdue = 0;
            int HoursOverdue = 0;
            int MinsOverdue = 0;
            int SecsOverdue = 0;
            // Time Difference between FromDate and ToDate in TimeSpan format
            TimeSpan difference = ToDate.Subtract(FromDate);
 
            // subtract the bank holidays
            DateTime dummy = new DateTime(1900, 1, 1, 0, 0, 0);
            DateTime nextDummyDay = dummy.AddDays(1); // AddDays() to add number of days
            DateTime nextDummyHour = dummy.AddHours(1); // AddHours() to add number of hours
            TimeSpan timespanOfOneDay = nextDummyDay.Subtract(dummy);
            TimeSpan timespanOfOneHour = nextDummyHour.Subtract(dummy);
 
            try
            {
                if (FromDate > ToDate)
                    return new DaySpan(); // not overdue
 
                DateTime intermediate = new DateTime(FromDate.Year, FromDate.Month, FromDate.Day);
 
                while (intermediate < ToDate)
                {
                    if (intermediate.DayOfWeek == DayOfWeek.Saturday)
                    {
                        if (!WorkOnSaturday)
                        {
                            difference = difference - timespanOfOneDay;
                        }
                    }
 
                    if (intermediate.DayOfWeek == DayOfWeek.Sunday)
                    {
                        if (!WorkOnSunday)
                        {
                            difference = difference - timespanOfOneDay;
                        }
                    }
 
                    intermediate = intermediate.AddDays(1);
                }
 
 
                if (BankHolidays != null && BankHolidays.Length > 0)
                {
                    for (int i = 0; i < BankHolidays.Length; i++)
                    {
                        if (BankHolidays[i].Ticks > FromDate.Ticks && BankHolidays[i].Ticks < ToDate.Ticks)
                        {
                            // Bank holidays is in between FromDate and ToDate
                            difference = difference - timespanOfOneDay;
                        }
                    }
                }
 
            }
            catch (Exception ex)
            {
                string error = "Failed to compute difference in days for Completion Date " + ToDate.ToString() + " and Target " + FromDate.ToString()
                                                ".  Error:" + ex.Message.ToString();
                throw new Exception(error);
            }
            finally
            {
                DaysOverdue = difference.Days;
                HoursOverdue = difference.Hours;
                MinsOverdue = difference.Minutes;
                SecsOverdue = difference.Seconds;
            }
            return new DaySpan { Days = DaysOverdue, Hours = HoursOverdue, Minutes = MinsOverdue, Seconds = SecsOverdue };
        }

// Custom DaySpan to be returned by ComputeDaysDifference()
public struct DaySpan
{
    public int Days;
    public int Hours;
    public int Minutes;
    public int Seconds;
}


Testing Our Custom C# Method


To test our custom method we run the following program and we check for validation as follows:

    public static void Main(string[] args)
    {
        TimeDifference td = new TimeDifference();
        DateTime fromDate = new DateTime(1990, 12, 13);
        DateTime toDate = new DateTime(1990, 12, 14);
 
        // Difference of one day
        var dayDifference = td.ComputeDaysDifference(toDate, fromDate, null);
        Console.WriteLine("Case 1");
        Console.WriteLine("Day Difference: " + dayDifference.Days);
        Console.WriteLine("Hour Difference: " + dayDifference.Hours);
        Console.WriteLine("Minute Difference: " + dayDifference.Minutes);
        Console.WriteLine("Seconds Difference: " + dayDifference.Seconds);
        Console.WriteLine("\n");
 
        // Difference of one day and seven hours
        fromDate = new DateTime(1990, 12, 13, 6, 0, 0);
        toDate = new DateTime(1990, 12, 14, 13, 0, 0);
        dayDifference = td.ComputeDaysDifference(toDate, fromDate, null);
        Console.WriteLine("Case 2");
        Console.WriteLine("Day Difference: " + dayDifference.Days);
        Console.WriteLine("Hour Difference: " + dayDifference.Hours);
        Console.WriteLine("Minute Difference: " + dayDifference.Minutes);
        Console.WriteLine("Seconds Difference: " + dayDifference.Seconds);
        Console.WriteLine("\n");
 
        // Difference of two days and seven hours. There's one Saturday and Sunday in between
        fromDate = new DateTime(1990, 12, 13, 6, 0, 0);
        toDate = new DateTime(1990, 12, 17, 13, 0, 0);
        dayDifference = td.ComputeDaysDifference(toDate, fromDate, null);
        Console.WriteLine("Case 3");
        Console.WriteLine("Day Difference: " + dayDifference.Days);
        Console.WriteLine("Hour Difference: " + dayDifference.Hours);
        Console.WriteLine("Minute Difference: " + dayDifference.Minutes);
        Console.WriteLine("Seconds Difference: " + dayDifference.Seconds);
        Console.WriteLine("\n");
 
        // Difference of three days and seven hours. There's one Saturday and Sunday in between.
        // We work on Saturday and hence it is true.
        fromDate = new DateTime(1990, 12, 13, 6, 0, 0);
        toDate = new DateTime(1990, 12, 17, 13, 0, 0);
        dayDifference = td.ComputeDaysDifference(toDate, fromDate, nulltruefalse);
        Console.WriteLine("Case 4");
        Console.WriteLine("Day Difference: " + dayDifference.Days);
        Console.WriteLine("Hour Difference: " + dayDifference.Hours);
        Console.WriteLine("Minute Difference: " + dayDifference.Minutes);
        Console.WriteLine("Seconds Difference: " + dayDifference.Seconds);
        Console.WriteLine("\n");
 
        // Difference of one day and seven hours. There's one Saturday and Sunday in between.
        // We work on Saturday and hence it is true. And 14th and 17th are holidays.
        fromDate = new DateTime(1990, 12, 13, 6, 0, 0);
        toDate = new DateTime(1990, 12, 17, 13, 0, 0);
        DateTime[] holidays = { new DateTime(1990, 12, 14), new  DateTime(1990, 12, 17)};
        dayDifference = td.ComputeDaysDifference(toDate, fromDate, holidays, truefalse);
        Console.WriteLine("Case 5");
        Console.WriteLine("Day Difference: " + dayDifference.Days);
        Console.WriteLine("Hour Difference: " + dayDifference.Hours);
        Console.WriteLine("Minute Difference: " + dayDifference.Minutes);
        Console.WriteLine("Seconds Difference: " + dayDifference.Seconds);
        Console.WriteLine("\n");
    }
}

Result




Code Repository


The aforementioned code can be downloaded from TechNet Gallery: https://gallery.technet.microsoft.com/C-Finding-Working-Day-5f965e69

References

DateTime Structure: https://msdn.microsoft.com/en-us/library/system.datetime%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
TimeSpan Structure: https://msdn.microsoft.com/en-us/library/system.timespan(v=vs.110).aspx
C# TimeSpan Examples: https://www.dotnetperls.com/timespan