How to group dates into weeks? - sql

I have the following table:
I somehow managed to write a query which allowed me to sum and group the total of visitors to a zoo per day (as the table), but I need to group even further into weeks in order to calculate the percentage of growth of the amount of visitors week over week for the last four weeks of the data.
How can I do that in Access SQL?
This is the query:
SELECT TOP 35 date_info.calendar_date, Sum(restaurants_visitors.reserve_visitors) AS SUMreserve_visitors
FROM date_info INNER JOIN restaurants_visitors ON date_info.calendar_date = restaurants_visitors.visit_date
GROUP BY date_info.calendar_date
ORDER BY date_info.calendar_date DESC;

Access stores dates as a floating point number whose integer part represents days since 1900 and whose fractional part is the time expressed as a fraction of a day. If I divide the date by 7 and truncate to integer I will have a week number spanning Saturday to Friday. Put that in a Group By and you're good to go.
Group By Fix(date_info.calendar_date/7)

First, you need a function to calculate the week number. Also the year of the week numbers, as weeks can span New Year.
This you can format like 2021W23 to list orderly:
' Returns, for a date value, a formatted string expression with
' year and weeknumber according to ISO-8601.
' Optionally, a W is used as separator between the year and week parts.
'
' Typical usage:
'
' FormatWeekIso8601(Date)
' -> 2017-23
'
' FormatWeekIso8601(Date, True)
' -> 2017W23
'
' 2017-04-28. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function FormatWeekIso8601( _
ByVal Expression As Variant, _
Optional ByVal WSeparator As Boolean) _
As String
Const Iso8601Separator As String = "W"
Const NeutralSeparator As String = "-"
Dim Result As String
Dim IsoYear As Integer
Dim IsoWeek As Integer
If IsDate(Expression) Then
IsoWeek = Week(DateValue(Expression), IsoYear)
Result = _
VBA.Format(IsoYear, String(3, "0")) & _
IIf(WSeparator, Iso8601Separator, NeutralSeparator) & _
VBA.Format(IsoWeek, String(2, "0"))
End If
FormatWeekIso8601 = Result
End Function
' Returns the ISO 8601 week of a date.
' The related ISO year is returned by ref.
'
' 2016-01-06. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function Week( _
ByVal Date1 As Date, _
Optional ByRef IsoYear As Integer) _
As Integer
Const MaxMonthValue As Integer = 12
Const MinMonthValue As Integer = 1
Const MaxWeekValue As Integer = 53
Const MinWeekValue As Integer = 1
Dim Month As Integer
Dim Interval As String
Dim Result As Integer
Interval = "ww"
Month = VBA.Month(Date1)
' Initially, set the ISO year to the calendar year.
IsoYear = VBA.Year(Date1)
Result = DatePart(Interval, Date1, vbMonday, vbFirstFourDays)
If Result = MaxWeekValue Then
If DatePart(Interval, DateAdd(Interval, 1, Date1), vbMonday, vbFirstFourDays) = MinWeekValue Then
' OK. The next week is the first week of the following year.
Else
' This is really the first week of the next ISO year.
' Correct for DatePart bug.
Result = MinWeekValue
End If
End If
' Adjust year where week number belongs to next or previous year.
If Month = MinMonthValue Then
If Result >= MaxWeekValue - 1 Then
' This is an early date of January belonging to the last week of the previous ISO year.
IsoYear = IsoYear - 1
End If
ElseIf Month = MaxMonthValue Then
If Result = MinWeekValue Then
' This is a late date of December belonging to the first week of the next ISO year.
IsoYear = IsoYear + 1
End If
End If
' IsoYear is returned by reference.
Week = Result
End Function
Now you can create a query like this to list the visitor counts from the last week and four weeks back:
SELECT
FormatWeekIso8601(date_info.calendar_date) As Year_Week,
Sum(restaurants_visitors.reserve_visitors) AS SUMreserve_visitors
FROM
date_info
INNER JOIN
restaurants_visitors
ON date_info.calendar_date = restaurants_visitors.visit_date
GROUP BY
FormatWeekIso8601(date_info.calendar_date)
HAVING
FormatWeekIso8601(date_info.calendar_date) Between
FormatWeekIso8601(DateAdd("ww", -4, date_info.calendar_date)) And
FormatWeekIso8601(DateAdd("ww", -1, date_info.calendar_date))
ORDER BY
FormatWeekIso8601(date_info.calendar_date) DESC;
Source: VBA.Date

Related

ACCESS VBA: Error when converting ISO Week Number to Date Range

I am trying to create a simple week selector based on the ISO Week number, which will give me the Monday date and the Sunday date every time the user clicks on "Current Week" or "Previous Week" or "Next Week" Buttons, as I will select all the transactions within those dates.
I have managed doing that following the steps from
MS Access get ISO standard week number
to get the correct week number for a specific date, and then converting the week number back to date following https://answers.microsoft.com/en-us/msoffice/forum/msoffice_access-mso_other/convert-week-number-to-date/3d0f8c90-a155-e011-8dfc-68b599b31bf5.
My conversion works fine for this year, every time I click in previous or next week, it brings the correct Monday and Sunday along with its correct week number, however, when it arrives on week 1 of 2021, which brings the correct dates of 04/01/2021 and 10/01/2021 (from and to respectively), the next click on "next week" brings the dates "from = 06/01/2021" and "to = 12/01/2021", and it stops going forward, the clicks don't change the dates.
When clicking "Previous Week", it goes well till week 1 of 2020, which brings the correct dates of 30/12/2019 and 05/01/2020, but the next click on "Previous Week" brings the dates 23/12/2018 and 29/12/18, but in this case, if I continue to click in Previous Week button it continues going back into 2018 correctly. It is just mad how it occurs.
I believe that the problem is in the DateSerial when converting the Week Number to Date Range, I have tried to figure it out, but I couldn't do it.
I hope you guys can help me out.
Thank you in advance.
'''' This is the function in a module to get the week number
Public Function ISOWeek(MyDate As Date) As Integer
ISOWeek = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
If ISOWeek > 52 Then
If Format(MyDate + 7, "ww", vbMonday, vbFirstFourDays) = 2 Then ISOWeek = 1
End If
End Function
'''' These subs run on the form code
Private Sub NextWeek_Click()
Dim SelectedWeek As Date
SelectedWeek = Me.Date_From.Value
FirstDayWeek = DateAdd("ww", ISOWeek(SelectedWeek), DateSerial(Year(SelectedWeek), 1, 1) - 2)
LastDayWeek = DateAdd("ww", ISOWeek(SelectedWeek), DateSerial(Year(SelectedWeek), 1, 1) + 4)
Me.Date_From.Value = FirstDayWeek
Me.Date_To.Value = LastDayWeek
End Sub
Private Sub PreviousWeek_Click()
Dim SelectedWeek As Date
SelectedWeek = Me.Date_From.Value
FirstDayWeek = DateAdd("ww", ISOWeek(SelectedWeek) - 2, DateSerial(Year(SelectedWeek), 1, 1) - 2)
LastDayWeek = DateAdd("ww", ISOWeek(SelectedWeek) - 2, DateSerial(Year(SelectedWeek), 1, 1) + 4)
Me.Date_From.Value = FirstDayWeek
Me.Date_To.Value = LastDayWeek
End Sub
Leave the week numbers from the date calculations, they only complicate matters.
By using the generic functions listed below, your two functions can be reduced to:
Private Sub NextWeek_Click()
Me.Date_From.Value = DateNextWeekPrimo(Me.Date_From.Value, vbMonday)
Me.Date_To.Value = DateNextWeekUltimo(Me.Date_From.Value, vbMonday)
End Sub
Private Sub PreviousWeek_Click()
Me.Date_From.Value = DatePreviousWeekPrimo(Me.Date_From.Value, vbMonday)
Me.Date_To.Value = DatePreviousWeekUltimo(Me.Date_From.Value, vbMonday)
End Sub
' Returns the primo date of the week following the week of the date passed.
'
' 2016-01-13. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DateNextWeekPrimo( _
ByVal DateThisWeek As Date, _
Optional ByVal FirstDayOfWeek As VbDayOfWeek = vbSunday) _
As Date
Dim Interval As String
Dim Number As Double
Dim ResultDate As Date
Number = 1
Interval = "ww"
' Offset date.
ResultDate = DateAdd(Interval, Number, DateThisWeek)
' Return first weekday with no time part.
ResultDate = DateAdd("d", 1 - Weekday(ResultDate, FirstDayOfWeek), Fix(ResultDate))
DateNextWeekPrimo = ResultDate
End Function
' Returns the ultimo date of the week following the week of the date passed.
'
' 2016-01-13. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DateNextWeekUltimo( _
ByVal DateThisWeek As Date, _
Optional ByVal FirstDayOfWeek As VbDayOfWeek = vbSunday) _
As Date
Dim Interval As String
Dim Number As Double
Dim ResultDate As Date
Number = 1
Interval = "ww"
' Offset date.
ResultDate = DateAdd(Interval, Number, DateThisWeek)
' Return last weekday with no time part.
ResultDate = DateAdd("d", 7 - Weekday(ResultDate, FirstDayOfWeek), Fix(ResultDate))
DateNextWeekUltimo = ResultDate
End Function
' Returns the primo date of the week preceding the week of the date passed.
'
' 2016-01-13. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DatePreviousWeekPrimo( _
ByVal DateThisWeek As Date, _
Optional ByVal FirstDayOfWeek As VbDayOfWeek = vbSunday) _
As Date
Dim Interval As String
Dim Number As Double
Dim ResultDate As Date
Number = -1
Interval = "ww"
' Offset date.
ResultDate = DateAdd(Interval, Number, DateThisWeek)
' Return first weekday with no time part.
ResultDate = DateAdd("d", 1 - Weekday(ResultDate, FirstDayOfWeek), Fix(ResultDate))
DatePreviousWeekPrimo = ResultDate
End Function
' Returns the ultimo date of the week preceding the week of the date passed.
'
' 2016-01-13. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DatePreviousWeekUltimo( _
ByVal DateThisWeek As Date, _
Optional ByVal FirstDayOfWeek As VbDayOfWeek = vbSunday) _
As Date
Dim Interval As String
Dim Number As Double
Dim ResultDate As Date
Number = -1
Interval = "ww"
' Offset date.
ResultDate = DateAdd(Interval, Number, DateThisWeek)
' Return last weekday with no time part.
ResultDate = DateAdd("d", 7 - Weekday(ResultDate, FirstDayOfWeek), Fix(ResultDate))
DatePreviousWeekUltimo = ResultDate
End Function

Can I change the fristweekofyear property in DatePart to whatever I want?

I work for a company that has a very weird fiscal year. Their FY starts in November. I want to use DatePart function to get the Month, Quarter, and Year data out of a specified date, but I need to make sure they are sorted based on whenever the first week of the year is. How can I change that value to make it that year starts in November?
The trick is to create a pseudo calendar date from the financial year date. Then you can perform all the usual calculations etc.
Here are some code snippets to get you started:
Option Explicit
' A fiscal year is designated as the calendar year in which it ends.
' For example, if the fiscal year runs from June 1, 2022, to May 31, 2023,
' it could be designated:
'
' financial year 2023
' fiscal year 2023
' FY2023
'
' Initially, call function SetFinancialStartMonth or SetFinancialEndMonth to
' define the financial/fiscal year.
' Constants.
' Default start day and month of the financial/fiscal year applied a
' neutral year for storing the values as a Date value.
Private Const DefaultStart As Date = #1/1/2000#
' Maximum day value valid for any month and year.
Private Const MaxDayAllMonthsValue As Integer = 28
' Statics.
' Local static variable to hold the selected start day and month of the financial/fiscal year.
' These are defined and read by the function:
'
' SetFinancialStart
' SetFinancialEnd
'
Private FinancialStart As Date
' Returns a calendar date as its equivalent pseudo date of the financial/fiscal year.
' This is mostly useful for creating search criteria, or to obtain the quarter, the
' month, or the year of the financial/fiscal year for a calendar date.
'
' 2019-10-06. Gustav Brock. Cactus Data ApS, CPH.
'
Public Function DateFinancial( _
ByVal CalendarDate As Date) _
As Date
Dim StartMonth As Integer
Dim StartDay As Integer
Dim Days As Integer
Dim FinancialDate As Date
StartMonth = Month(GetFinancialStart)
StartDay = Day(GetFinancialStart)
FinancialDate = DateAdd("m", MinMonthValue - StartMonth + MaxMonthValue, CalendarDate)
' Correct ultimo month dates of CalendarDate.
If Day(CalendarDate) > Day(FinancialDate) Then
Days = DaysInMonth(CalendarDate)
Else
Days = DaysInMonth(FinancialDate)
End If
FinancialDate = DateAdd("d", MinDayValue - StartDay + Days, FinancialDate)
DateFinancial = FinancialDate
End Function
' Returns the financial start month and day as a Date value applied a neutral year.
' If the start month and day have not been set, the default value is returned.
'
' 2019-01-26. Gustav Brock. Cactus Data ApS, CPH.
'
Public Function GetFinancialStart() As Date
If FinancialStart = #12:00:00 AM# Then
' Set default value for start date.
SetFinancialStart
End If
GetFinancialStart = FinancialStart
End Function
' Sets the start day and month of the financial/fiscal year as a
' date value applied a neutral year.
'
' The start month can be any month.
' The start day can be any day less than or equal 28, which is the
' highest day value valid for any month.
'
' Default value is January 1st.
'
' 2019-01-26. Gustav Brock. Cactus Data ApS, CPH.
'
Public Sub SetFinancialStart( _
Optional ByVal StartMonth As Integer = MinMonthValue, _
Optional ByVal StartDay As Integer = MinDayValue)
Const MaxMonthValue As Integer = 12
Const MinMonthValue As Integer = 1
Const MinDayValue As Integer = 1
' Validate input.
If StartMonth < MinMonthValue Or _
StartMonth > MaxMonthValue Or _
StartDay < MinDayValue Or _
StartDay >= MaxDayAllMonthsValue Then
Err.Raise DtError.dtInvalidProcedureCallOrArgument
End If
FinancialStart = DateSerial(Year(DefaultStart), StartMonth, StartDay)
End Sub
' Sets - based on the end day and month - the start day and month of the
' financial/fiscal year as a date value applied a neutral year.
'
' The end month can be any month.
' The end day can be any day larger than 1. If end day is larger than 28,
' which is the highest day value valid for any month, start day is set to 1.
'
' Default value is January 1st.
'
' 2019-01-26. Gustav Brock. Cactus Data ApS, CPH.
'
Public Sub SetFinancialEnd( _
Optional ByVal EndMonth As Integer = MaxMonthValue, _
Optional ByVal EndDay As Integer = MaxDayValue)
Dim StartMonth As Integer
Dim StartDay As Integer
' Validate input.
If EndMonth < MinMonthValue Or _
EndMonth > MaxMonthValue Or _
EndDay < MinDayValue Or _
EndDay > MaxDayValue Then
Err.Raise DtError.dtInvalidProcedureCallOrArgument
End If
If EndDay < MaxDayAllMonthsValue - 1 Then
' Set start date of the financial year as the day after the end day.
StartMonth = EndMonth
StartDay = EndDay + 1
Else
' Set start date of the financial year as primo next month.
StartMonth = EndMonth Mod MonthsPerYear + 1
StartDay = 1
End If
SetFinancialStart StartMonth, StartDay
End Sub
' Returns the count of days of the month of Date1.
'
' 2016-02-14. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DaysInMonth( _
ByVal Date1 As Date) _
As Integer
Const MaxDateValue As Date = #12/31/9999#
Const MaxDayValue As Integer = 31
Dim Days As Integer
If DateDiff("m", Date1, MaxDateValue) = 0 Then
Days = MaxDayValue
Else
Days = Day(DateSerial(Year(Date1), Month(Date1) + 1, 0))
End If
DaysInMonth = Days
End Function

Simplest way to SELECT * FROM MyTable WHERE mydate is in specific week number?

Is there a simpel way to select and sum rows where the date is in a specific week number?
Something like this code?
mysum = RS.execute("Select Sum(mypoints) as sumpoints from MyTable WHERE
datepart(week, mydate) = '42'")("sumpoints")
Correct syntax for SQL statement and DatePart() function - uses default first day of week of Sunday:
SELECT Sum(mypoints) AS sumpoints FROM MyTable WHERE DatePart("ww", mydate) = 42
As you probably have Monday as the first day of the week, it would be:
Select Sum(mypoints) As sumpoints
From MyTable
Where DatePart('ww', mydate, 2) = 42
However, if you need exact ISO 8601 week numbering, you will have to use a custom function like this:
Public Function ISO_WeekNumber( _
ByVal datDate As Date) _
As Byte
' Calculates and returns week number for date datDate according to the ISO 8601:1988 standard.
' 1998-2000, Gustav Brock, Cactus Data ApS, CPH.
' May be freely used and distributed.
Const cbytFirstWeekOfAnyYear As Byte = 1
Const cbytLastWeekOfLeapYear As Byte = 53
Dim bytWeek As Byte
Dim bytISOThursday As Byte
Dim datLastDayOfYear As Date
bytWeek = DatePart("ww", datDate, vbMonday, vbFirstFourDays)
If bytWeek = cbytLastWeekOfLeapYear Then
bytISOThursday = Weekday(vbThursday, vbMonday)
datLastDayOfYear = DateSerial(Year(datDate), 12, 31)
If Weekday(datLastDayOfYear, vbMonday) >= bytISOThursday Then
' OK, week count of 53 is caused by leap year.
Else
' Correct for Access97/2000 bug.
bytWeek = cbytFirstWeekOfAnyYear
End If
End If
ISO_WeekNumber = bytWeek
End Function
to replace DatePart.
Do note, that you will also have to filter on the year, as ISO weeknumbers 1 and 52/53 typically will cross calendar year boundaries.
Addendum:
This is the VBScript version in case the VBA version (above) can't be used:
Option Explicit
MsgBox ISO_WeekNumber(Date)
WScript.Quit
Function ISO_WeekNumber(ByVal Date1)
' Calculates and returns week number for date Date1 according to the ISO 8601:1988 standard.
' 2019, Gustav Brock, Cactus Data ApS, CPH.
' May be freely used and distributed.
Const FirstWeekOfAnyYear = 1
Const LastWeekOfLeapYear = 53
Dim Week
Dim ISOThursday
Dim LastDayOfYear
Week = DatePart("ww", Date1, vbMonday, vbFirstFourDays)
If Week = LastWeekOfLeapYear Then
ISOThursday = Weekday(vbThursday, vbMonday)
LastDayOfYear = DateSerial(Year(Date1), 12, 31)
If Weekday(LastDayOfYear, vbMonday) >= ISOThursday Then
' OK, week count of 53 is caused by leap year.
Else
' Correct for VBA bug.
Week = FirstWeekOfAnyYear
End If
End If
ISO_WeekNumber = Week
End Function

MS Access query, how to use SQL to group single dates into weeks

I currently have two tables. One has Employee Names and a number associated to that name.
The other date has time sheet date with columns for the employee number, the date and the number of hours worked on that date.
I want to create a cross tab query that shows the employee names in one column with the date for the end of the week in each column, then show to sum of hours for that week for a particular employee.
My current query works but only groups by month. I am struggle to work out how to group days into weeks.
TRANSFORM Sum(tblTimeSheetData.WorkHours) AS SumOfHours
SELECT tblEmployees.Combined
FROM tblTimeSheetData RIGHT JOIN tblEmployees ON tblTimeSheetData.EmployeeID =
tblEmployees.EmployeeID
GROUP BY tblEmployees.Combined
ORDER BY tblEmployees.Combined, Format([WorkDate],"yyyy-mm")
PIVOT Format([WorkDate],"yyyy-mm");
As the first and last week numbers cross calendar years, both year and week number must be included:
Option Compare Database
Option Explicit
Public Const MaxWeekValue As Integer = 53
Public Const MinWeekValue As Integer = 1
Public Const MaxMonthValue As Integer = 12
Public Const MinMonthValue As Integer = 1
' Returns, for a date value, a formatted string expression with
' year and weeknumber according to ISO-8601.
' Optionally, a W is used as separator between the year and week parts.
'
' Typical usage:
'
' FormatWeekIso8601(Date)
' -> 2017-23
'
' FormatWeekIso8601(Date, True)
' -> 2017W23
'
' 2017-04-28. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function FormatWeekIso8601( _
ByVal Expression As Variant, _
Optional ByVal WeekSeparator As Boolean) _
As String
Const Iso8601Separator As String = "W"
Const NeutralSeparator As String = "-"
Dim Result As String
Dim IsoYear As Integer
Dim IsoWeek As Integer
If IsDate(Expression) Then
IsoWeek = Week(DateValue(Expression), IsoYear)
Result = _
VBA.Format(IsoYear, String(3, "0")) & _
IIf(WeekSeparator, Iso8601Separator, NeutralSeparator) & _
VBA.Format(IsoWeek, String(2, "0"))
End If
FormatWeekIso8601 = Result
End Function
' Returns the ISO 8601 week of a date.
' The related ISO year is returned by ref.
'
' 2016-01-06. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function Week( _
ByVal Date1 As Date, _
Optional ByRef IsoYear As Integer) _
As Integer
Dim Month As Integer
Dim Interval As String
Dim Result As Integer
Interval = "ww"
Month = VBA.Month(Date1)
' Initially, set the ISO year to the calendar year.
IsoYear = VBA.Year(Date1)
Result = DatePart(Interval, Date1, vbMonday, vbFirstFourDays)
If Result = MaxWeekValue Then
If DatePart(Interval, DateAdd(Interval, 1, Date1), vbMonday, vbFirstFourDays) = MinWeekValue Then
' OK. The next week is the first week of the following year.
Else
' This is really the first week of the next ISO year.
' Correct for DatePart bug.
Result = MinWeekValue
End If
End If
' Adjust year where week number belongs to next or previous year.
If Month = MinMonthValue Then
If Result >= MaxWeekValue - 1 Then
' This is an early date of January belonging to the last week of the previous ISO year.
IsoYear = IsoYear - 1
End If
ElseIf Month = MaxMonthValue Then
If Result = MinWeekValue Then
' This is a late date of December belonging to the first week of the next ISO year.
IsoYear = IsoYear + 1
End If
End If
' IsoYear is returned by reference.
Week = Result
End Function
If you just want to pivot on the ultimo week date, you can use this expression:
DateAdd("d", 7 - Weekday([WorkDate], vbMonday), [WorkDate])
This assumes ISO week numbering where Monday is the first day of the week.
You can try getting the week with datepart function like
SELECT DATEPART(ww, 'your date') AS week;
and then group by week.
Here more info
https://www.w3schools.com/sql/func_sqlserver_datepart.asp

Return the 'week of the month' by the month

Does anybody have a function to return the "week of the month" by the month? Most of the functions searched start the week on the 1st. I would like to go by the week, i.e 1st March 2018 will be week 5 of February. Week 1 of March starts on the 4th of March.
How can I do that?
First find the previous Sunday of the date of the month as this date could fall in the previous month:
DateAdd("d", 1 - Weekday(DateOfMonth), DateOfMonth)
Then use this generic function to find the first Sunday of the month:
' Calculates the date of the occurrence of Weekday in the month of DateInMonth.
'
' If Occurrence is 0 or negative, the first occurrence of Weekday in the month is assumed.
' If Occurrence is 5 or larger, the last occurrence of Weekday in the month is assumed.
'
' If Weekday is invalid or not specified, the weekday of DateInMonth is used.
'
' 2016-06-09. Gustav Brock, Cactus Data ApS, CPH.
'
Public Function DateWeekdayInMonth( _
ByVal DateInMonth As Date, _
Optional ByVal Occurrence As Integer, _
Optional ByVal Weekday As VbDayOfWeek = -1) _
As Date
Const DaysInWeek As Integer = 7
Dim Offset As Integer
Dim Month As Integer
Dim Year As Integer
Dim ResultDate As Date
' Validate Weekday.
Select Case Weekday
Case _
vbMonday, _
vbTuesday, _
vbWednesday, _
vbThursday, _
vbFriday, _
vbSaturday, _
vbSunday
Case Else
' Zero, none or invalid value for VbDayOfWeek.
Weekday = VBA.Weekday(DateInMonth)
End Select
' Validate Occurence.
If Occurrence <= 0 Then
Occurrence = 1
ElseIf Occurrence > 5 Then
Occurrence = 5
End If
' Start date.
Month = VBA.Month(DateInMonth)
Year = VBA.Year(DateInMonth)
ResultDate = DateSerial(Year, Month, 1)
' Find offset of Weekday from first day of month.
Offset = DaysInWeek * (Occurrence - 1) + (Weekday - VBA.Weekday(ResultDate) + DaysInWeek) Mod DaysInWeek
' Calculate result date.
ResultDate = DateAdd("d", Offset, ResultDate)
If Occurrence = 5 Then
' The latest occurrency of Weekday is requested.
' Check if there really is a fifth occurrence of Weekday in this month.
If VBA.Month(ResultDate) <> Month Then
' There are only four occurrencies of Weekday in this month.
' Return the fourth as the latest.
ResultDate = DateAdd("d", -DaysInWeek, ResultDate)
End If
End If
DateWeekdayInMonth = ResultDate
End Function
Finally, assemble these and use DateDiff to obtain the count of Sundays and add 1 (one) to obtain the weeknumber:
MonthWeekNumber = 1 + DateDiff("w", DateWeekdayInMonth(DateAdd("d", 1 - Weekday(DateOfMonth), DateOfMonth), 1, vbSunday), DateOfMonth)