Would you like to have easily accessible Extended Methods for DateTime variables like the below ?
- //calculates the age as of today, where DOB is a Date;
- DOB.AgeAsOfToday();
- //checks if myDate falls in a leap year
- myDate.IsLeapYear();
- //Returns the Full Month Name, which is not an out-of-box property of DateTime
- myDate.MonthFullName();
- //Checks whether your date falls within Range
- myDate.DateBetween(Convert.ToDateTime("12-December-1992"), Convert.ToDateTime("31-December-1994")
Dates are quite an interesting data type in the programming language. Though the DOT NET framework provides many functions out of the box, typically the business validations require some more functional checks OR capabilities like whether it's a leap year, what's the age from the current date, whether it falls within a range, etc.
We will use the capability of Extention Methods to achieve our objective. We cannot directly extend the base DateTime type as it's a sealed class.
Therefore we will define our own class that we can extend.
- public class Date
- {
- private DateTime? newDate;
- //Default constructor
- public Date()
- {}
- //overridden constructor
- public Date(DateTime d)
- {
- this.newDate = d;
- }
- /// <summary>
- /// If no value is set, the default value returned is Today's Date.
- /// </summary>
- public DateTime? DateValueDefault
- { get
- {
- if (newDate == null)
- newDate = DateTime.Today;
- return newDate;
- }
- set
- {
- newDate = value;
- }
- }
- }
Now we will define our custom functions in the Extended Class. We will try to implement it in a way so that all properties and methods that are available on a DateTime type variable are also applied in our extended class. So we will use reflection to call any method or property that is required. GetPropertyValue, InvokeMethodWithoutArguments, and InvokeMethodWithArguments methods are defined to access the default properties and methods on a DateTime type. All other methods are extended methods.
- //you will be needing the following namespaces
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
- //include them in your class
- public static class ExtendedDateFunctions
- {
- #region default properties of DateTime
- /// <summary>
- /// Invokes any property of DateTime data type
- /// </summary>
- /// <param name="d">The object on which the property is to be invoked</param>
- /// <param name="propertyName">name of the property invoked. Make sure that you spell it with correct casing as the Property Name of DateTime types</param>
- /// <returns></returns>
- public static object GetPropertyValue(this Date d, string propertyName)
- {
- DateTime date = d.DateValueDefault ?? DateTime.Today;
- return date.GetType().GetProperties()
- .Single(pi => pi.Name == propertyName)
- .GetValue(date, null);
- }
- /// <summary>
- /// Invokes any method of DateTime data type
- /// </summary>
- /// <param name="d">The object on which the method is to be invoked</param>
- /// <param name="methodName">name of the property invoked. Make sure that you spell it with correct casing as the Method Name of DateTime types</param>
- /// <returns></returns>
- public static object InvokeMethodWithoutArguments(this Date d, string methodName)
- {
- DateTime date = d.DateValueDefault ?? DateTime.Today;
- return date.GetType().GetMethod(methodName).Invoke(date, null);
- }
- /// <summary>
- /// Invokes any method of DateTime data type
- /// </summary>
- /// <param name="d">The object on which the method is to be invoked</param>
- /// <param name="methodName">name of the property invoked. Make sure that you spell it with correct casing as the Method Name of DateTime types</param>
- /// <param name="args"> List of arguments to be used in the method. Ensure the correct order</param>
- /// <returns></returns>
- public static object InvokeMethodWithArguments(this Date d, string methodName, List<object> args)
- {
- DateTime date = d.DateValueDefault ?? DateTime.Today;
- return date.GetType().GetMethod(methodName).Invoke(date, args.ToArray());
- }
- #endregion
- #region extended Methods
- public static bool DateLessThan(this Date d, DateTime endDate)
- {
- return (d.DateValueDefault < endDate);
- }
- public static bool DateLessThanEqual(this Date d, DateTime endDate)
- {
- return (d.DateValueDefault <= endDate);
- }
- public static bool DateGreaterThan(this Date d, DateTime startDate)
- {
- return (d.DateValueDefault > startDate);
- }
- public static bool DateGreaterEqual(this Date d, DateTime startDate)
- {
- return (d.DateValueDefault >= startDate);
- }
- public static bool DateBetween(this Date d, DateTime startDate, DateTime endDate)
- {
- return (d.DateValueDefault > startDate && d.DateValueDefault < endDate);
- }
- public static bool DateBetweenIncludeEndDates(this Date d, DateTime startDate, DateTime endDate)
- {
- return (d.DateValueDefault >= startDate && d.DateValueDefault <= endDate);
- }
- public static string MonthFullName(this Date d)
- {
- var month = (d.DateValueDefault ?? DateTime.Today).Month;
- return CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(month);
- }
- public static string MonthShortName(this Date d)
- {
- var month = (d.DateValueDefault ?? DateTime.Today).Month;
- return CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(month);
- }
- public static bool IsLeapYear(this Date d)
- {
- var y = (d.DateValueDefault ?? DateTime.Today).Year;
- bool _check4 = CheckDivisibility(y, 4, true); //if divisible by 4; (if yes, then only proceed)
- bool _check100 = CheckDivisibility(y, 100, _check4); //if divisible in 100; (if yes, then only proceed)
- bool _check400 = CheckDivisibility(y, 400, _check100);//if divisible by 400
- //The year is a leap year if it is multiple of 4 and 400, but not of 100;
- return (_check4 && !(_check100)) || (_check400);
- }
- public static int AgeAsOfToday(this Date d)
- {
- // Calculate the age.
- var age = DateTime.Today.Year - (d.DateValueDefault ?? DateTime.Today).Year;
- // Go back to the year in which the person was born in case of a leap year
- if (d.DateValueDefault?.Date > DateTime.Today.AddYears(-age)) age--;
- return age;
- }
- private static bool CheckDivisibility(int year, int divBy, bool cont)
- {
- bool response = false;
- //the value of cont is used to determine whether the previous condition is TRUE.
- //perform the current divisibility check only if the last condition is passed.
- //What we have done here is instead of making the check in the calling function we have moved the
- //check in this function and made this more like a single responsibility function.
- if (cont)
- {
- int rem = year % divBy;
- if (rem == 0)
- response = true;
- }
- return response;
- }
- #endregion
- }
On the client program, call your extended class to check your functions,
- static void Main(string[] args)
- {
- //Demo
- Date myDate = new Date(Convert.ToDateTime("12-December-1994"));
- Console.WriteLine("Date: {0}", myDate.DateValueDefault?.ToString());
- Console.WriteLine("---------------");
- Console.WriteLine("Some default date properties --");
- Console.WriteLine("Year (invoking a property): {0}", myDate.GetPropertyValue("Year"));
- List<object> arguments = new List<object>() { 400 };
- Console.WriteLine("adding 400 days (invoking a method): {0}", myDate.InvokeMethodWithArguments("AddDays", arguments)); //AddDays
- Console.WriteLine("---------------");
- Console.WriteLine("Some Extended methods --");
- Console.WriteLine("Age as of Today: {0}", myDate.AgeAsOfToday());
- Console.WriteLine("Is it leap year: {0}", myDate.IsLeapYear());
- Console.WriteLine("Month Full Name: {0}", myDate.MonthFullName());
- Console.WriteLine("Month Short Name: {0}", myDate.MonthShortName());
- Console.WriteLine("Lies between {0} and {1}: {2}",
- Convert.ToDateTime("12-December-1992"),
- Convert.ToDateTime("31-December-1994"),
- myDate.DateBetween(Convert.ToDateTime("12-December-1992"), Convert.ToDateTime("31-December-1994"))
- );
- Console.ReadLine();
- }
- Date: 12-Dec-94 12:00:00 AM
- ---------------
- Some default date properties --
- Year (invoking a property): 1994
- adding 400 days (invoking a method): 16-Jan-96 12:00:00 AM
- ---------------
- Some Extended methods --
- Age as of Today: 26
- Is it leap year: False
- Month Full Name: December
- Month Short Name: Dec
- Lies between 12-Dec-92 12:00:00 AM and 31-Dec-94 12:00:00 AM: True
Conclusion
- Allows your typical validations on your parameters that are derived from base types.
- You can implement reusable utility functions that are string types, integer types, etc.
- You can copy the above code and extend your own functions as required.

Join the conversation! Your thoughts help the community grow.