Find the occurrence of a day within a month for a given date

Recently I had to determine, given a specific date, what the recurrence of that date’s day of the week was within the month; for example, today (the 20th of March 2009) is the 3rd Friday this month.

In addition, I also had to determine whether it was also the last recurrence of the day within the month, for example if the date was the 27th of March, 2009 then it would be the fourth Friday in March, but also the last Friday in March.. see what I mean?

To support this, I had to do a little design work to find a solution which would be fairly quick and compact.  It obviously had to take into account leap years, more than four occurrences of a day within a month (the max is five), as well ad months consisting of 28, 29, 30 and 31 days in length and – importantly – what day (of the week) the month started on!

The trick here was to use the [the .Net Framework’s] DayOfWeek enum in concert with DateTime’s handy .DaysInMonth() function.  The DayOfMonth assigns a numeric value to each day, and the week starts with a Sunday, as below.

public enum DayOfWeek
{
        Sunday = 0,
        Monday = 1,
        Tuesday = 2,
        Wednesday = 3,
        Thursday = 4,
        Friday = 5,
        Saturday = 6,
}

Simply put, a month could start on any day of the week (obviously), so we have to deal with our edge cases – weekends – where the day Sunday (DayOfWeek == 0) or Saturday (DayOFWeek == 6). 

Taking this into consideration, I wagered that by working out the number of days in a month in addition to the day on which the month started (relative to the day requested), we could reasonably determine the occurrence of the specified date within the month, e.g. the 15th of March (Sunday) relative to the 1st of March (also a Sunday) – are you with me so far?

Should a month not start on a weekend day or a day which comes after the requested day (e.g. requested date is a Tuesday, but month starts on a Thursday) then we have to add an extra recurrence (because there would be an extra occurrence within the first week (7 days) of the month) – does that make sense?

Here’s a complete sample written in C#.  Apologies for the use of an out param.

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace SampleProject
{
    class Program
    {
        static void Main(string[] args)
        {
            bool isLast = false;
            Assert.IsTrue(GetRecurrence(2009, 3, 29, out isLast) == RecurrenceEnum.Fifth, "Should be fifth (and last)
                                                                                                                                 Sunday in March");
            Assert.IsTrue(isLast, "Should be last day in month");
        }

        /// <summary>
        /// Represents the possible recurrence of a day within a month
        /// </summary>
        public enum RecurrenceEnum
        {
            First = 1,
            Second = 2,
            Third = 3,
            Fourth = 4,
            Fifth = 5
        }

        /// <summary>
        /// Obtain the occurrence of a day within a month, e.g. First Saturday or Fifth (and last) Friday
        /// </summary>
        /// <param name="year">Year of date to check</param>
        /// <param name="month">Month  of date to check</param>
        /// <param name="day">Day of date to check</param>
        /// <param name="isLast">Whether this is also the last occurrence within the month</param>
        /// <returns>the occurence of the day within the month</returns>
        public static RecurrenceEnum GetRecurrence(int year, int month, int day, out bool isLast)
        {
            isLast = false; //set the out param, defaulted to false           
            int numberOfDaysInMonth = DateTime.DaysInMonth(year, month);
            int firstDayOfMonth = (int)new DateTime(year, month, 1).DayOfWeek;
            int checkDayOfWeek = (int)new DateTime(year, month, day).DayOfWeek;
            int lastWeekInMonth = (numberOfDaysInMonth / 7);
            int checkWeekInMonth = (day / 7);

            //not directly divisible (i.e. not Feb (28 days))
            if (numberOfDaysInMonth % 7 != 0)
            {
                lastWeekInMonth += 1;
            }
            //day (date) not directly divisible by 7 (i.e. not 7th, 14th etc), so add 1
            if (day % 7 != 0)
            {
                checkWeekInMonth += 1;
            }
            //is this the last week in the month?
            if (lastWeekInMonth == checkWeekInMonth)
            {
                isLast = true;
            }
            //if the month started with a weekend day or a day which comes before the check date then
            //we just return the week (i.e. second week == second recurrence)
            if (firstDayOfMonth == 0 || firstDayOfMonth == 6 ||
                firstDayOfMonth <= checkDayOfWeek)
            {
                return (RecurrenceEnum)checkWeekInMonth;
            }
            //else it means we have an extra recurrence before the day requested, so add one recurrence
            else
            {
                return (RecurrenceEnum)checkWeekInMonth + 1;
            }
        }
    }
}

Leave a comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.