No, I certainly can’t help you get dates or have successful dates. But I can offer a couple of functions that might help you work with dates in your code. I suck at dates (all types). Every time I want to calculate dates, I need to print a calendar from Outlook and count the days. It’s pretty ironic that one of my previous projects was all about schedules. And one of my current projects deals with delivery schedules as well. So I can’t escape it.
I always would get frustrated because every month was different. Every month started on a different day and had a different number of days. It felt impossible to get all the different combinations. But recently, I had a moment of clarity and realized some basic facts about a month. Things like:
- No month has less than 28 days
- This guarantees every month will have 4 weeks
- This guarantees there are no less than 4 and no more than 5 of every weekday in a month
- The only weekdays that will have 5 occurrences will be the days in excess of 28. These days can be accounted for at the beginning or end of the month – it doesn’t matter.
- By extension, there are a minimum of 20 workdays in a month (Mon-Fri)
- And, any additional workdays will be those in excess of 28 that are between Monday and Friday
Earlier attempts to figure out the number of workdays in a month resulted in a brute force loop that would run through every day from 1 to 31 and if the DayOfWeek was Mon-Fri, increment a counter. Now, with these new guidelines, I can start at 20 and only deal with 0-3 excess days. Like with this function:
Shared Function WorkdaysInMonth(ByVal d As Date) As Integer
Dim daysInMonth As Integer
Dim extraWeekDays As Integer
daysInMonth = New Date(d.Year, d.Month, 1).AddMonths(1).AddDays(-1).Day
For i As Integer = 1 To daysInMonth - 28
Select Case New Date(d.Year, d.Month, i).DayOfWeek
Case DayOfWeek.Monday, DayOfWeek.Tuesday, _
DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday
extraWeekDays += 1
End Select
Next
Return 20 + extraWeekDays
End Function
Using the other rules for weekdays, we know we only need to deal with the exceptions, to find out whether there are 5 weekdays in a month:
Private Shared Function NumberOfWeekdaysInMonth(ByVal weekday As DayOfWeek, ByVal referenceDate As Date) As Integer
Dim firstDay As DayOfWeek
Dim lastDay As DayOfWeek
firstDay = New Date(referenceDate.Year, referenceDate.Month, 1).DayOfWeek
lastDay = New Date(referenceDate.Year, referenceDate.Month, 1).AddMonths(1).AddDays(-1).DayOfWeek
If New Date(referenceDate.Year, referenceDate.Month, 1).AddMonths(1).AddDays(-1).Day = 28 Then
Return 4
ElseIf lastDay >= firstDay AndAlso weekday >= firstDay AndAlso weekday <= lastDay Then
Return 5
ElseIf lastDay < firstDay AndAlso weekday >= firstDay - 7 AndAlso weekday <= lastDay Then
Return 5
ElseIf lastDay < firstDay AndAlso weekday >= firstDay AndAlso weekday <= lastDay + 7 Then
Return 5
Else
Return 4
End If
End Function
That one got a bit hairy because is the extra days started at the end of the week with a high DayOfWeek value, and ended early in the week with a low DayOfWeek value, we had to compensate at each end. That’s the reasons for all the different IF conditions. There’s also a specific condition for February’s 28 days.
Finally, a function to determine the first, second, third, fourth, or fifth weekday in a month. Useful when calculating holidays like Labor Day and Thanksgiving.
Private Shared Function NthDayOfMonth(ByVal index As Integer, ByVal weekDay As DayOfWeek, _
ByVal referenceDate As Date) As Date
Dim firstDay As DayOfWeek
Dim dayOfMonth As Integer
firstDay = New Date(referenceDate.Year, referenceDate.Month, 1).DayOfWeek
dayOfMonth = (7 * (index - 1)) + _
CInt(IIf(firstDay > weekDay, 7 + weekDay - firstDay, weekDay - firstDay))
Return New Date(referenceDate.Year, referenceDate.Month, dayOfMonth + 1)
End Function