SIGN UP MEMBER LOGIN:    
ARTICLE

Extending the DateTime structure in C#: Part II

Posted by Vulpes Articles | C# Language April 14, 2011
Reader Level:

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.

Login to add your contents and source code to this article
share this article :
post comment
 

It does sound a bit metaphysical but it's efficient (nothing but integer arithmetic) and it works which are the important things :)

Posted by Vulpes Apr 15, 2011

For other readers, don't be afraid to admit that you don't know what a notional year is. I don't know what it is; perhaps it refers to people that understand the notion of a year but don't know the details of how many days are in a year.

Posted by Sam Hobbs Apr 14, 2011

I'm a biginner and also a student of Sri Lanka Institute of Information Technology. You are doing a great help for students like us... Thank you very much....

Posted by Tharake Gunatilake Apr 14, 2011

Many thanks for your comments, Raja :) Yes, I could have used the ternary operator instead of if/else though as the expressions were on the long side, I thought if/else would look better in print.

Posted by Vulpes Apr 14, 2011

Very useful extension method. I like your implementation of Excess Days in WeeksBetween method..... well thought out..... One small suggestion though : you could use "?" operator instead of if then else statements. Great Job!!! 5 stars!!! (rating could be included for articles - suggestion to Admins :-)).

Posted by Raja Krishnamurthy Apr 14, 2011
6 Months Free & No Setup Fees ASP.NET Hosting!
Become a Sponsor
PREMIUM SPONSORS
  • Get 2 Months Free of ASP.NET Hosting for Only $4.95/month! Receive FREE MS SQL and MySQL Databases Including ASP.NET 4/3.5, MVC 3.0, Silverlight 4, Windows 2008/IIS 7.0 Plus FREE IIS 7 Modules. Host UNLIMITED ASP.NET Web Sites - Click Here!
    Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Become a Sponsor