|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MAHSetup.AppCode
{
public static class DateTimeExtensions
{
//--------------------------------------------------------------------------------
/// <summary>
/// Gets the date of the specified instance of the specified day of week within
/// the specified month for the specified year
/// </summary>
/// <param name="dt">The parent object of this method</param>
/// <param name="year">The year to calculate for</param>
/// <param name="month">The month to calculate for</param>
/// <param name="day">The day of week to find</param>
/// <param name="ordinal">The instance of the day of week to find</param>
/// <returns>The resulting DateTime</returns>
/// <remarks>
/// If the specified ordinal is smaller than 1, it's set to 1. If the ordinal is
/// greater than 5, it's set to 5 (there are no more than five instances of any
/// given weekday in any month). The month value is similarly normalized (at 1
/// and 12, of course). If the normalized ordinal results in a date that is
/// beyond the end of the specified month, the method will back the resulting
/// date off by 7 days. This happens until the resulting date falls within
/// the specified month. Since the max ordinal is 5, this recursive call should
/// only happen once at the very most (in February, depending on whether or not
/// the specified year is a leap year).</remarks>
public static DateTime GetDateByOrdinalDay(this DateTime dt, int year, int month, DayOfWeek dayOfWeek, int ordinal)
{
// normalize some values to make sure we're within acceptable ranges
ordinal = Math.Min(Math.Max(ordinal, 1), 5);
month = Math.Min(Math.Max(month, 1), 12);
// start out on the first of the specified month and of the current year
DateTime workingDate = new DateTime(year, month, 1);
DateTime lastDay = new DateTime(year += (month + 1 > 12) ? 1 : 0,
month += (month + 1 > 12)? - 11 : 1,
1).AddDays(-1);
// find the number of days in the specified month
int maxDays = (lastDay - workingDate).Days + 1;
// assume that the month starts on the specifed day of week
int gap = 0;
// if the the day of week of the first day is NOT the specified day of week
if (workingDate.DayOfWeek != dayOfWeek)
{
// determine the number of days between the first of the month and
// the first instance of the specified day of week
gap = (int)workingDate.DayOfWeek - (int)dayOfWeek;
gap = (gap < 0) ? Math.Abs(gap) : 7 - gap;
// and set the date to the first instance of the specified day of week
workingDate = workingDate.AddDays(gap);
}
// if we want something later than the first instance
if (ordinal > 1)
{
// determine how many days we're going to add to the working date to
// satisfy the specified ordinal
int daysToAdd = 7 * (ordinal - 1);
// now adjust back, just in case the specified ordinal - this loop
// should only iterate once or twice
while (daysToAdd + gap > maxDays-1)
{
daysToAdd -= 7;
}
// finally we adjust the date by the number of days to add
workingDate = workingDate.AddDays(daysToAdd);
}
// and return the date to the calling method
return workingDate;
}
//--------------------------------------------------------------------------------
/// <summary>
/// Gets the date of the specified instance of the specified day of week within the specified month for the current year
/// </summary>
/// <param name="dt">The parent object of this method</param>
/// <param name="month">The month to calculate for</param>
/// <param name="day">The day of week to find</param>
/// <param name="ordinal">The instance of the day of week to find</param>
/// <returns>The resulting DateTime</returns>
/// <remarks>
/// If the specified ordinal is smaller than 1, it's set to 1. If the ordinal is
/// greater than 5, it's set to 5 (there are no more than five instances of any
/// given weekday in any month). The month value is similarly normalized (at 1
/// and 12, of course). If the normalized ordinal results in a date that is
/// beyond the end of the specified month, the method will back the resulting
/// date off by 7 days. This happens until the resulting date falls within
/// the specified month. Since the max ordinal is 5, this recursive call should
/// only happen once at the very most (in February, depending on whether or not
/// the specified year is a leap year).</remarks>
public static DateTime GetDateByOrdinalDay(this DateTime dt, int month, DayOfWeek day, int ordinal)
{
return GetDateByOrdinalDay(dt, DateTime.Now.Year, month, day, ordinal);
}
//--------------------------------------------------------------------------------
/// <summary>
/// Sets this DateTime to the date as calculated by GetDateByOrdinalDay. No error checking is done
/// beyond making sure the ordinal is greater than 0 and month is between 1 and 12.
/// </summary>
/// <param name="dt">The parent object of this method</param>
/// <param name="month">The month to calculate for</param>
/// <param name="day">The day of week to find</param>
/// <param name="ordinal">The instance of the day of week to find</param>
/// <remarks>
/// If the specified ordinal is smaller than 1, it's set to 1. If the ordinal is
/// greater than 5, it's set to 5 (there are no more than five instances of any
/// given weekday in any month). The month value is similarly normalized (at 1
/// and 12, of course). If the normalized ordinal results in a date that is
/// beyond the end of the specified month, the method will back the resulting
/// date off by 7 days. This happens until the resulting date falls within
/// the specified month. Since the max ordinal is 5, this recursive call should
/// only happen once at the very most (in February, depending on whether or not
/// the specified year is a leap year).</remarks>
public static void SetDateByOrdinalDay(this DateTime dt, int month, DayOfWeek day, int ordinal)
{
dt = GetDateByOrdinalDay(dt, month, day, ordinal);
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.
My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.