Introduction
In the conclusion to Part I of this article (http://www.c-sharpcorner.com/UploadFile/b942f9/6714/),
I mentioned that I would be adding some further extension methods for the
DateTime structure in Part II which deal with the durations between
dates.
You might expect that these methods should be extending the TimeSpan rather than
the DateTime structure but this in fact is not feasible. Whilst methods and
properties in the TimeSpan structure can easily work out the durations between
dates, they can do so only in terms of days; weeks, months and years are not
covered.
Although weeks can easily be calculated from the duration in days, months and
years are more awkward because of differences in the lengths of months and the
occurrence of leap years. Also it may not be clear whether the final day of an
interval should be taken into account or not. For example:
1 March 2011 to 30 April 2011 equals 1 complete month if the final day is
ignored or 2 complete months if it isn't.
29 January 2011 to 28 February 2011 equals 0 complete months if the final day is
ignored or 1 complete month if it isn't.
The following extension methods deal with this difficulty by giving the user the
option to take into account the final day or to ignore it (the default is to
ignore it).
Source code for the extension methods
using System;
namespace
Utilities
{
public static
partial class
DateTimeExtensions
{
private static
int DateValue(this
DateTime dt)
{
return dt.Year * 372 + (dt.Month -
1) * 31 + dt.Day - 1;
}
public static
int YearsBetween(this
DateTime dt,
DateTime dt2)
{
return dt.MonthsBetween(dt2) / 12;
}
public static
int YearsBetween(this
DateTime dt,
DateTime dt2, bool includeLastDay)
{
return dt.MonthsBetween(dt2,
includeLastDay) / 12;
}
public static
int YearsBetween(this
DateTime dt,
DateTime dt2, bool includeLastDay,
out int
excessMonths)
{
int months = dt.MonthsBetween(dt2,
includeLastDay);
excessMonths = months % 12;
return months / 12;
}
public static
int MonthsBetween(this
DateTime dt,
DateTime dt2)
{
int months = (dt2.DateValue() -
dt.DateValue()) / 31;
return
Math.Abs(months);
}
public static
int MonthsBetween(this
DateTime dt,
DateTime dt2, bool includeLastDay)
{
if (!includeLastDay)
return dt.MonthsBetween(dt2);
int days;
if (dt2 >= dt)
days = dt2.AddDays(1).DateValue() - dt.DateValue();
else
days = dt.AddDays(1).DateValue() - dt2.DateValue();
return days / 31;
}
public static
int WeeksBetween(this
DateTime dt,
DateTime dt2)
{
return dt.DaysBetween(dt2) / 7;
}
public static
int WeeksBetween(this
DateTime dt,
DateTime dt2, bool includeLastDay)
{
return dt.DaysBetween(dt2,
includeLastDay) / 7;
}
public static
int WeeksBetween(this
DateTime dt,
DateTime dt2, bool includeLastDay,
out int
excessDays)
{
int days = dt.DaysBetween(dt2,
includeLastDay);
excessDays = days % 7;
return days / 7;
}
public static
int DaysBetween(this
DateTime dt,
DateTime dt2)
{
return (dt2.Date -
dt.Date).Duration().Days;
}
public static
int DaysBetween(this
DateTime dt,
DateTime dt2, bool includeLastDay)
{
int days = dt.DaysBetween(dt2);
if (!includeLastDay)
return days;
return days + 1;
}
}
}
Technical note
To calculate months and years in an efficient manner, I've used an old
programming hack which assumes a notional year of 372 days split into 12 equal
months of 31 days, whether it's a leap year or not. Unusually, this is presented
as a private extension method as I didn't think it would be useful outside this
class.
Notes on usage
As in the case of the extension methods in Part I, these extension methods can
be used in any of your projects (C# 3.0 or later) by first compiling them to a
dynamic link library (dll), adding a reference to the dll to your project and
then adding the following "using" directive to the file:
using Utilities;
// or any other name you choose for the namespace
All methods work out the absolute value of the duration between the two dates (I
didn't think that catering for negative durations would be very useful) and so
it doesn't matter in which order they are used.
All methods completely ignore the time components of each DateTime instance (in
effect they treat it as midnight) and so it is possible that the result of the
DaysBetween() method may differ from the absolute value of the Days component of
the TimeSpan between the two dates. Indeed, this is why this method has been
included.
As well as overloads to determine whether the last day is included or not, there
are also overloads to return the excess months or days when the duration in
years or weeks, respectively, is being calculated. The
excess is returned in an 'out' parameter.
Example of usage
using System;
using Utilities;
class
Test
{
static void
Main()
{
DateTime dt =
new DateTime(2008,
4, 13);
DateTime dt2 =
new DateTime(2011,
4, 12);
int[] durations =
new int[12];
int excessMonths;
int excessDays;
durations[0] = dt.YearsBetween(dt2);
durations[1] = dt.YearsBetween(dt2, true);
durations[2] = dt.YearsBetween(dt2, false,
out excessMonths);
durations[3] = excessMonths;
dt =
new DateTime(2010,
9, 13);
durations[4] = dt.MonthsBetween(dt2);
durations[5] = dt.MonthsBetween(dt2, true);
dt =
new DateTime(2011,
1, 5);
durations[6] = dt.WeeksBetween(dt2);
durations[7] = dt.WeeksBetween(dt2, true);
durations[8] = dt.WeeksBetween(dt2, false,
out excessDays);
durations[9] = excessDays;
dt =
new DateTime(2011,
5, 13); // now ahead of dt2
durations[10] = dt.DaysBetween(dt2);
durations[11] = dt.DaysBetween(dt2, true);
foreach (int
duration in durations)
Console.WriteLine(duration);
Console.ReadKey(true);
}
}
The output of this program is as follows:
2
3
2
11
6
7
13
14
13
6
31
32
Conclusion
The extension methods in this article have been provided in a separate partial
static class to those in Part I - and do not use the latter - in case some
developers would prefer to use one set but not the other.