smithvoice.com
 Y'herd thisun? 

“In the early 1950's Senator Joe McCarthy kept the nation guessing with his accusations that there were 35 or 200 or 56 or 111 communists in the State Department. As a result, people didn't ask whether or not there were communists in the State Department, but rather how many communists were there in the State Department.”

from Phantoms of Space by James Oberg

Calculating floating holidates

TaggedCoding, CSharp, VB

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



home     who is smith    contact smith     rss feed π
Since 1997 a place for my stuff, and it if helps you too then all the better smithvoice.com