since 1997 a place for my stuff, and if it helps you then so much the better

 
...
...

Calculating floating holidates


When rendering a calendar's dayboxes or creating a company's HR days-off list, figuring out New Years Day, Christmas and Valentines Day are easy, but what about those floating holidays that depend on the instance of the Day in the Month?

This set of utilities gives a dev a lot of options plus offers protection against accidental weird arguments.

What's more, now all floating Holidays a company cares about are simply defined in a config file with month, instance number and/or flag for relative instance position from start or end of the month. (Non-floating holidays are likewise easily defined with a flag for specific Day number.)

Example test harness: start a Winform project, copy the class code to a class file, drop two textboxes on the form (txtDate and txtInstance), add a button to the form, double click on the button and paste this code in the stub:

  Private Sub butTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
    Handles butTest.Click
 
    If IsDate(txtDate.Text) Then
        Try
            MsgBox(DemoDateUtils.GetInstanceofWeekdayinMonth( _
               CDate(txtDate.Text), DayOfWeek.Sunday, CInt(txtInstance.Text) _
               ))
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
    End If
 
    ''another way the dev can now call it, by checking the instance count first
 
    If IsDate(txtDate.Text) Then
        Try
            If CInt(txtInstance.Text) > DemoDateUtils.GetInstanceCountOfWeekdayInMonth( _
                CDate(txtDate.Text), DayOfWeek.Sunday) Then
                MsgBox("I don't even have to call it, there aren't that number of instances")
            Else
                MsgBox(DemoDateUtils.GetInstanceofWeekdayinMonth( _
                    CDate(txtDate.Text), DayOfWeek.Sunday, CInt(txtInstance.Text)))
 
            End If
        Catch ex As Exception
            MsgBox(ex.ToString)
        End Try
 
    End If
  
 
    ''what is memorial day?  last monday in May?
 
    MsgBox("Memorial day this year is: " & _
        DemoDateUtils.GetLastInstancedDayOfMonth( _
            New DateTime(Now.Year, 5, 1), _
            DayOfWeek.Monday).ToLongDateString)
 
 
    ''and when is Mothers day?  2nd sunday in May?
 
    MsgBox("Mothers day day this year was: " & _
       DemoDateUtils.GetInstanceofWeekDayinMonth( _
       New DateTime(Now.Year, 5, 1), DayOfWeek.Sunday, 2).ToLongDateString)
 
  End Sub

Similarly, when filling a Calendar control just read celebrated holidays from an xml file and test each day on the DayRender event, setting the Text to add a special icon or flyover tooltip or different backcolor to the cells that have holidays.

Here's the class code for floating Holidates in VB and C#:

VB code:

  Public Class DemoDateUtils
 
     Public Shared Function GetInstanceofWeekdayinMonth( _
          ByVal dt As Date, _
          ByVal weekday As DayOfWeek, _
          ByVal instance As Integer) As Date
 
         If instance <= 0 Then
             Throw New ArgumentException("Instance count must be greater than zero", _
 	      "instance")
         End If
 
         Dim dtRet As DateTime
 
         Dim dtFirstDay As DateTime = _
             GetFirstInstancedDayOfMonth(dt, weekday)
 
         Dim instancesInMonth As Integer = GetInstanceCountOfWeekdayInMonth(dt, weekday)
 
         If instance <= instancesInMonth Then
             Dim padDays As Integer = 7 * (instance - 1)
             dtRet = New DateTime(dt.Year, dt.Month, dtFirstDay.Day + padDays)
         Else
             Throw New ArgumentException("Instance range exceeded, _
 	      max: " & instancesInMonth.ToString, "instance")
         End If
 
         Return dtRet
  
 
     End Function
 
 
     Public Shared Function GetInstanceCountOfWeekdayInMonth( _
         ByVal dt As Date, _
         ByVal weekday As DayOfWeek) As Integer
 
         Return ((GetLastInstancedDayOfMonth(dt, weekday).Day - 
 	   GetFirstInstancedDayOfMonth(dt, weekday).Day) \ 7) + 1
 
     End Function
 
 
     Public Shared Function GetFirstInstancedDayOfMonth( _
         ByVal dt As Date, _
         ByVal weekday As DayOfWeek) As DateTime
 
 
         Dim dtFirstDay As DateTime = _
             New DateTime(dt.Year, dt.Month, 1)
 
         If weekday < dtFirstDay.DayOfWeek Then
             dtFirstDay = dtFirstDay.AddDays(weekday - dtFirstDay.DayOfWeek + 7)
         Else
             dtFirstDay = dtFirstDay.AddDays(weekday - dtFirstDay.DayOfWeek)
         End If
 
         Return dtFirstDay
 
     End Function
 
 
     Public Shared Function GetLastInstancedDayOfMonth( _
         ByVal dt As Date, _
         ByVal weekday As DayOfWeek) As DateTime
 
 
         Dim dtLastDay As DateTime = _
             New DateTime(dt.Year, dt.Month, 1).AddMonths(1).AddDays(-1)
 
         If weekday > dtLastDay.DayOfWeek Then
             dtLastDay = dtLastDay.AddDays(weekday - dtLastDay.DayOfWeek - 7)
         Else
             dtLastDay = dtLastDay.AddDays(weekday - dtLastDay.DayOfWeek)
         End If
         Return dtLastDay
 
    End Function
 
 
  End Class

 

C# code:

  using System;
 
  public class csDemoDateUtils
  {
 
     public static DateTime GetInstanceofWeekdayInMonth(DateTime dt, 
        DayOfWeek weekday, int instance)
     {
         if (instance <= 0)
         {
             throw new ArgumentException("Instance count must be greater than zero", 
 	      "instance");
         }
 
         DateTime dtRet;
 
         DateTime dtFirstDay = GetFirstInstancedDayOfMonth(dt, weekday);
 
         int instancesInMonth = GetInstanceCountOfWeekdayInMonth(dt, weekday);
 
         if (instance <= instancesInMonth)
         {
             int padDays = 7 * (instance - 1);
             dtRet = new DateTime(dt.Year, dt.Month, dtFirstDay.Day + padDays);
         }
         else
         {
             throw new ArgumentException("Instance range exceeded, max: " + 
 	      instancesInMonth.ToString(), "instance");
         }
 
         return dtRet;
 
     }
 
 
     public static int GetInstanceCountOfWeekdayInMonth(DateTime dt, DayOfWeek weekday)
     {
         return ((GetLastInstancedDayOfMonth(dt, weekday).Day - 
 	   GetFirstInstancedDayOfMonth(dt, weekday).Day) / 7) + 1;
 
     }
 
 
     public static DateTime GetFirstInstancedDayOfMonth(DateTime dt, DayOfWeek weekday)
     {
         DateTime dtFirstDay = new DateTime(dt.Year, dt.Month, 1);
 
         if (weekday < dtFirstDay.DayOfWeek)
         {
             dtFirstDay = dtFirstDay.AddDays(weekday - dtFirstDay.DayOfWeek + 7);
         }
         else
         {
             dtFirstDay = dtFirstDay.AddDays(weekday - dtFirstDay.DayOfWeek);
         }
         return dtFirstDay;
 
     }
  
 
     public static DateTime GetLastInstancedDayOfMonth(DateTime dt, DayOfWeek weekday)
     {
         DateTime dtLastDay = new DateTime(dt.Year, dt.Month, 1).AddMonths(1).AddDays(-1);
 
         if (weekday > dtLastDay.DayOfWeek)
         {
             dtLastDay = dtLastDay.AddDays(weekday - dtLastDay.DayOfWeek - 7);
         }
         else
         {
             dtLastDay = dtLastDay.AddDays(weekday - dtLastDay.DayOfWeek);
         }
 
         return dtLastDay;
 
     }
  }

 
Hope that helps.

Robert Smith
Kirkland, WA

added February 2006


...
...

"In theory, theory and practice are the same. In practice, they are not." -Albert Einstein