Would you like to have easily accessible Extended Methods for DateTime variables like the below ?
-
- DOB.AgeAsOfToday();
-
- myDate.IsLeapYear();
-
- myDate.MonthFullName();
-
- myDate.DateBetween(Convert.ToDateTime("12-December-1992"), Convert.ToDateTime("31-December-1994")
Sure, you can implement it inline; but when you are trying to define a cleaner code structure and want your team to apply a common code base and reuse utility functions, you may benifit from the approach I suggest below.
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;
-
- public Date()
- {}
-
- public Date(DateTime d)
- {
- this.newDate = d;
- }
-
-
-
- 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.
-
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.Linq;
-
-
- public static class ExtendedDateFunctions
- {
-
- #region default properties of DateTime
-
-
-
-
-
-
- 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);
- }
-
-
-
-
-
-
-
- public static object InvokeMethodWithoutArguments(this Date d, string methodName)
- {
- DateTime date = d.DateValueDefault ?? DateTime.Today;
- return date.GetType().GetMethod(methodName).Invoke(date, null);
- }
-
-
-
-
-
-
-
- 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);
- bool _check100 = CheckDivisibility(y, 100, _check4);
- bool _check400 = CheckDivisibility(y, 400, _check100);
-
- return (_check4 && !(_check100)) || (_check400);
- }
-
- public static int AgeAsOfToday(this Date d)
- {
-
- var age = DateTime.Today.Year - (d.DateValueDefault ?? DateTime.Today).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;
-
-
-
-
- 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)
- {
-
-
- 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));
- 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();
-
- }
Here is the Console output,
- 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.