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

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

Related

Query to return records within the same business year

Why are records which were entered before 31/07/2021 still showing in this query? I want to show records in the current business year only.
Company business year Starts in 01/08/YYYY and end on 31/07/YYYY the next year. Can the query works for every business year? i.e. "at this point of time next year".
I have this SQL code so far:
SELECT
FORMAT(DATEADD("m", 5, [OrderDate]), "\Qq") AS Quarter, *
FROM
tblOrders
WHERE
[OrderDate] BETWEEN DateSerial(Year(Date()) - 1, 8, 1)
AND DateSerial(Year(Date()), 8, 0)
ORDER BY
tblOrders.OrderDate DESC;
DateSerial(Year(Date()) - 1, 8, 1) returns 01.08.2020 and DateSerial(Year(Date()), 8, 0) returns 31.07.2021. So in the end you are getting all the dates between 01.08.2020 and 31.07.2021.
I guess you could make the Year(Date()) dynamic. In case current date is equal or past 01.08.XXXX but not past 31.12.XXXX then it should be current year and current year + 1 otherwise it should stay as it is now - current year - 1 and current year.
Maybe something like this:
SELECT
FORMAT(DATEADD("m", 5, [OrderDate]), "\Qq") AS Quarter, *
FROM
tblOrders
WHERE
[OrderDate] BETWEEN IIF(Date() >= DateSerial(Year(Date()), 8, 1) AND Date() <= DateSerial(Year(Date()), 13, 0), DateSerial(Year(Date()), 8, 1), DateSerial(Year(Date()) - 1, 8, 1))
AND IIF(Date() >= DateSerial(Year(Date()), 8, 1) AND Date() <= DateSerial(Year(Date()), 13, 0), DateSerial(Year(Date()) + 1, 8, 0), DateSerial(Year(Date()), 8, 0))
ORDER BY
tblOrders.OrderDate DESC;
You can use these two functions found in module DateBank here: VBA.Date.
' Returns the primo calendar date of the specified financial/fiscal year.
' Returns the primo calender date of the current financial year, if no
' financial year is specified.
'
' 2021-05-09. Gustav Brock. Cactus Data ApS, CPH.
'
Public Function DateFinancialYearPrimo( _
Optional ByVal FinancialYear As Integer) _
As Date
Dim Month As Integer
Dim Day As Integer
Dim Years As Integer
Dim Primo As Date
Month = VBA.Month(DateFinancialStart())
Day = VBA.Day(DateFinancialStart())
If IsYear(FinancialYear) Then
Years = FinancialYear - VBA.Year(FinancialStart)
If Month = MinMonthValue And Day = MinDayValue Then
' The financial year is the calendar year.
Else
Years = Years - 1
End If
Else
Years = VBA.Year(DateCalendar(Date)) - VBA.Year(FinancialStart)
End If
Primo = DateAdd("yyyy", Years, FinancialStart)
DateFinancialYearPrimo = Primo
End Function
' Returns the ultimo calendar date of the specified financial/fiscal year.
' Returns the ultimo calender date of the current financial year, if no
' financial year is specified.
'
' 2021-05-09. Gustav Brock. Cactus Data ApS, CPH.
'
Public Function DateFinancialYearUltimo( _
Optional ByVal FinancialYear As Integer) _
As Date
Dim Ultimo As Date
Ultimo = DateAdd("d", -1, DateAdd("yyyy", 1, DateFinancialYearPrimo(FinancialYear)))
DateFinancialYearUltimo = Ultimo
End Function
' Gets or 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.
'
' Examples:
' ' Set financial year.
' StartDate = DateFinancialStart(10, 1)
' ' StartDate -> 2000-10-01
'
' ' Get financial year.
' StartDate = DateFinancialStart
' ' StartDate -> 2000-10-01
' EndDate = DateFinancialEnd
' ' EndDate -> 2000-09-30
'
' 2021-05-08. Gustav Brock. Cactus Data ApS, CPH.
'
Public Function DateFinancialStart( _
Optional ByVal StartMonth As Integer, _
Optional ByVal StartDay As Integer) _
As Date
' Validate input.
If IsMonth(StartMonth) And IsDayAllMonths(StartDay) Then
FinancialStart = DateSerial(Year(DefaultStart), StartMonth, StartDay)
End If
If FinancialStart = #12:00:00 AM# Then
FinancialStart = DefaultStart
End If
DateFinancialStart = FinancialStart
End Function
Note: Partial code only.

How to group dates into weeks?

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

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

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)