C#: Understanding Basics of DateTime and TimeSpan with an Example: Finding Working Day Difference Between Two Dates Based on Weekend And Bank Holidays
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 DateTime is 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, null, true, false);
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, true, false);
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