Search code examples
t-sqliso8601

Function to find the number of ISO-weeks in a month


In T-SQL, I need to find the number of ISO-weeks in a month.

According to client specifications, a week belongs to the month that has the sunday of the week number.

For instance, week 5 2024 belongs to February, not January, because Sunday Feb 4th is in February.

Context: I need to use it to find the average staffing needs for a given month, based on staffing needs specified for each week in that month, as in this simplified pseudo code:

WeeklyStaffingNeedsTotal / GetNumberOfIsoWeeksInMonth(year, month) AS MonthlyStaffingNeed


Solution

  • An ISO week runs from Monday to Sunday. To count the number of ISO weeks that end on a Sunday contained in a given month, you really just need to count Sundays in that month. If you can identify the last Sunday of the month, the day number will tell you the total number of Sundays in that month. If the last Sunday falls on the 28th or earlier, there are four Sundays and four ISO weeks that end in that month. If the last Sunday falls on the 29th or later, there are five Sundays and five ISO weeks that end in that month. This can also be calculated as (DAY(last-Sunday-of-month + 6) / 7.

    To apply the above logic, we need to first calculate the last Sunday of the month. We can obtain the last day of a month using the EOMONTH() function. We can then use the DATE_BUCKET() function (new in SQL Server 2022) to adjust that date to the last Sunday using the week option and an origin date representing some arbitrary reference Sunday.

    In summary, we need to:

    1. Calculate the last day of the month using EOMONTH(date).
    2. Calculate the last Sunday using DATE_BUCKET(week, 1, end-of-month, reference-sunday)
    3. Calculate the number of Sundays in the month using (DAY(last-Sunday) + 6) / 7

    The combined calculation would be:

    DECLARE @OriginSunday DATE = '20240107' -- 7 January 2024 (any Sunday will do)
    DECLARE @Date DATE = '20240301' -- March 2024
    SELECT (DAY(DATE_BUCKET(week, 1, EOMONTH(@Date), @OriginSunday)) + 6) / 7 AS NumSundays
    

    The result for the above is 5.

    For all of 2024, the calculations would yield:

    Dt EndOfMonth LastSunday SundaysInMonth
    2024-01-01 2024-01-31 2024-01-28 4
    2024-02-01 2024-02-29 2024-02-25 4
    2024-03-01 2024-03-31 2024-03-31 5
    2024-04-01 2024-04-30 2024-04-28 4
    2024-05-01 2024-05-31 2024-05-26 4
    2024-06-01 2024-06-30 2024-06-30 5
    2024-07-01 2024-07-31 2024-07-28 4
    2024-08-01 2024-08-31 2024-08-25 4
    2024-09-01 2024-09-30 2024-09-29 5
    2024-10-01 2024-10-31 2024-10-27 4
    2024-11-01 2024-11-30 2024-11-24 4
    2024-12-01 2024-12-31 2024-12-29 5

    See this db<>fiddle for a demo that includes and entire 28-year calendar cycle.