'Get Start and End Date from Week Number SQL Server
We have WeekNo and Year entered into a table in following format:
YYYY-ww (2021-48)
I want start date and end date from given week no with year as above format.
How to get it from SQL query or procedure?
Thanks,
Solution 1:[1]
(An extra answer, since this method is a bit different.)
This method is about using a tally table with dates and date related stuff.
Then such calendar table can be used to lookup the year and week.
For example
Create calendar table
create table REF_Calendar (
cal_date date primary key not null,
cal_year smallint not null,
cal_month tinyint not null,
cal_day tinyint not null,
cal_dayofyear smallint not null,
cal_quarter tinyint not null,
cal_weekday_abbrev char(3) not null,
cal_iso_week tinyint,
cal_df7_week tinyint not null,
cal_df7_weekday tinyint not null,
cal_df1_week tinyint,
cal_df1_weekday tinyint,
cal_mooncycle tinyint,
index idx_Calendar_year_week unique (cal_year, cal_df7_week, cal_df7_weekday)
);
Fill the calendar
SET DATEFIRST 7; -- 7: week starts on sunday
declare @startdate date = '2020-01-01';
declare @enddate date = '2023-01-01';
with rcte_calendar as (
select @startdate as cal_date
union all
select dateadd(day, 1, cal_date)
from rcte_calendar
where cal_date < @enddate
)
insert into ref_calendar
(cal_date, cal_year, cal_month, cal_day, cal_dayofyear, cal_quarter, cal_weekday_abbrev,
cal_df7_week, cal_df7_weekday, cal_mooncycle)
select
cal_date
, datepart(year, cal_date) as cal_year
, datepart(month, cal_date) as cal_month
, datepart(day, cal_date) as cal_day
, datepart(dayofyear, cal_date) as cal_dayofyear
, datepart(quarter, cal_date) as cal_quarter
, lower(left(datename(weekday, cal_date), 3)) as cal_weekday_abbrev
, datepart(week, cal_date) as cal_df7_week
, datepart(weekday, cal_date) as cal_df7_weekday
, abs(1-abs(round(((14-1.0*CONVERT(int, CONVERT(nvarchar(2), cal_date, 131)))/14)
, 1, 1)))*100 as cal_mooncycle
from rcte_calendar cte
where not exists (
select 1
from ref_calendar cal
where cal.cal_date = cte.cal_date
)
option (maxrecursion 0);
Updating extra's
SET DATEFIRST 1; -- 1: week starts on monday
update ref_calendar
set cal_df1_week = datepart(week, cal_date)
, cal_df1_weekday = datepart(weekday, cal_date)
, cal_iso_week = datepart(iso_week, cal_date)
where cal_df1_week is null
or cal_df1_weekday is null
or cal_iso_week is null;
Check how it looks
select top 9 *
from ref_calendar
where cal_mooncycle = 100
and cal_date >= getdate()
and cal_year <= 1 + datepart(year, current_timestamp)
order by cal_date asc;
| cal_date | cal_year | cal_month | cal_day | cal_dayofyear | cal_quarter | cal_weekday_abbrev | cal_iso_week | cal_df7_week | cal_df7_weekday | cal_df1_week | cal_df1_weekday | cal_mooncycle |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2021-12-17 | 2021 | 12 | 17 | 351 | 4 | fri | 50 | 51 | 6 | 51 | 5 | 100 |
| 2021-12-18 | 2021 | 12 | 18 | 352 | 4 | sat | 50 | 51 | 7 | 51 | 6 | 100 |
| 2021-12-19 | 2021 | 12 | 19 | 353 | 4 | sun | 50 | 52 | 1 | 51 | 7 | 100 |
| 2022-01-16 | 2022 | 1 | 16 | 16 | 1 | sun | 2 | 4 | 1 | 3 | 7 | 100 |
| 2022-01-17 | 2022 | 1 | 17 | 17 | 1 | mon | 3 | 4 | 2 | 4 | 1 | 100 |
| 2022-01-18 | 2022 | 1 | 18 | 18 | 1 | tue | 3 | 4 | 3 | 4 | 2 | 100 |
| 2022-02-14 | 2022 | 2 | 14 | 45 | 1 | mon | 7 | 8 | 2 | 8 | 1 | 100 |
| 2022-02-15 | 2022 | 2 | 15 | 46 | 1 | tue | 7 | 8 | 3 | 8 | 2 | 100 |
| 2022-02-16 | 2022 | 2 | 16 | 47 | 1 | wed | 7 | 8 | 4 | 8 | 3 | 100 |
Test applying the calendar to a table
DECLARE @Test TABLE (col char(7));
INSERT INTO @Test VALUES
('2021-01'), ('2021-02')
, ('2021-48')
, ('2022-01'), ('2022-02')
;
select *
from @test t
outer apply (
select
min(cal_date) as startofweek
, max(cal_date) as endofweek
from (values (try_cast(left(col, 4) as int),
abs(try_cast(right(col, 2) as int)))
) as val(yr, ww)
join ref_calendar as cal
on ((cal_year = yr and cal_df7_week = ww)
or (ww = 1 and cal_year = yr-1 and cal_df7_week = 53)
or (ww = 53 and cal_year = yr+1 and cal_df7_week = 1))
) cal
| col | startofweek | endofweek |
|---|---|---|
| 2021-01 | 2020-12-27 | 2021-01-02 |
| 2021-02 | 2021-01-03 | 2021-01-09 |
| 2021-48 | 2021-11-21 | 2021-11-27 |
| 2022-01 | 2021-12-26 | 2022-01-01 |
| 2022-02 | 2022-01-02 | 2022-01-08 |
Demo on db<>fiddle here
Solution 2:[2]
How about this?
declare @dateText varchar(7);
declare @week int;
declare @year int;
set datefirst 1; -- Set first day of week to Monday
set @dateText = '2021-48';
set @year = SUBSTRING(@dateText, 1, 4)
set @week = SUBSTRING(@dateText, 6, 2)
select DATEADD(week, @week, DATEADD(year, @year-1900, 0)) - 4 -
DATEPART(dw, DATEADD(week, @week, DATEADD(year, @year-1900, 0)) - 4) + 1
Output
-----------------
2021-11-29 00:00:00.000
Alternatively you could create a function:
CREATE FUNCTION dbo.GetStartOfWeek(@dateText varchar(7))
RETURNS Datetime
BEGIN
declare @week int;
declare @year int;
declare @offset int;
select
@offset = @@DATEFIRST - 1,
@year = SUBSTRING(@dateText, 1, 4),
@week = SUBSTRING(@dateText, 6, 2)
RETURN DATEADD(week, @week, DATEADD(year, @year-1900, 0)) - 4 -
DATEPART(dw, DATEADD(week, @week, DATEADD(year, @year-1900, 0) + @offset) - 4) + 1
END;
Which you can use in a SQL query or stored procedure:
select dbo.GetStartOfWeek('2021-48') as StartDate, DATEADD(d, 7, dbo.GetStartOfWeek('2021-48')) as EndDate
Solution 3:[3]
Here are 2 ways to calculate the start & end day of a Year-Week string in a query.
1) Without UDF
SET DATEFIRST 7; -- 7: Sunday has weekday 1
DECLARE @Test TABLE ([YEAR-WW] char(7));
INSERT INTO @Test VALUES
('2020-53'), ('2021-01'), ('2021-02')
, ('2021-48')
, ('2021-53'), ('2022-01');
SELECT [YEAR-WW]
, [FirstDayOfWeek] = CAST(DATEADD(day, 1-DATEPART(weekday, DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01')), DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01')) AS DATE)
, [LastDayOfWeek] = CAST(DATEADD(day, 6, DATEADD(day, 1-DATEPART(weekday, DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01')), DATEADD(week, ABS(RIGHT([YEAR-WW],2))-1, LEFT([YEAR-WW],5)+'01-01'))) AS DATE)
FROM @Test
ORDER BY 1;
| YEAR-WW | FirstDayOfWeek | LastDayOfWeek |
|---|---|---|
| 2020-53 | 2020-12-27 | 2021-01-02 |
| 2021-01 | 2020-12-27 | 2021-01-02 |
| 2021-02 | 2021-01-03 | 2021-01-09 |
| 2021-48 | 2021-11-21 | 2021-11-27 |
| 2021-53 | 2021-12-26 | 2022-01-01 |
| 2022-01 | 2021-12-26 | 2022-01-01 |
2) Using this UDF
CREATE FUNCTION dbo.GetDateFromYearWeek (
@YearWeek VARCHAR(7) = '000101', -- default
@WeekDay INT = 1, -- default
@FirstWeekDayName VARCHAR(9) = 'mon' -- default
) RETURNS DATE
BEGIN
IF @YearWeek = '000101'
SET @YearWeek = CONCAT(DATEPART(year, GETDATE()), '-', DATEPART(week, GETDATE()));
IF @YearWeek NOT LIKE '[0-9][0-9][0-9][0-9]%[0-9-][0-9]'
RETURN NULL;
IF @WeekDay < 1 OR @WeekDay > 7
RETURN NULL;
DECLARE @FirstWeekDay INT = CHARINDEX(LOWER(LEFT(@FirstWeekDayName,3)), ' montuewedthufrisatsun')/3;
IF @FirstWeekDay = 0 -- not found in string
SET @FirstWeekDay = @@DATEFIRST;
DECLARE @Year INT = TRY_CAST(LEFT(@YearWeek, 4) AS INT);
DECLARE @Week INT = ABS(TRY_CAST(RIGHT(@YearWeek, 2) AS INT));
DECLARE @Date DATE = TRY_CAST(CONCAT(@Year,'-01-01') AS DATE);
SET @Date = DATEADD(week, @Week-1, @Date);
DECLARE @DowDiff INT = (6-@FirstWeekday+@@DATEFIRST+DATEPART(weekday,@Date))%7;
SET @Date = DATEADD(day, -@DowDiff, @Date);
SET @Date = DATEADD(day, @WeekDay-1, @Date);
RETURN @Date;
END;
Example usage
DECLARE @Test TABLE ([YEAR-WW] char(7));
INSERT INTO @Test VALUES
('2020-53'), ('2021-01'), ('2021-02')
, ('2021-48')
, ('2021-53'), ('2022-01'), ('2022-02')
;
SELECT [YEAR-WW]
, [FirstOfWeek_Mon] = dbo.GetDateFromYearWeek([YEAR-WW], 1, 'mon')
, [LastOfWeek_Mon] = dbo.GetDateFromYearWeek([YEAR-WW], 7, 'Monday')
, [FirstOfWeek_Sun] = dbo.GetDateFromYearWeek([YEAR-WW], 1, 'sun')
, [LastOfWeek_Sun] = dbo.GetDateFromYearWeek([YEAR-WW], 7, 'SUNDAY')
FROM @Test
ORDER BY 1;
| YEAR-WW | FirstOfWeek_Mon | LastOfWeek_Mon | FirstOfWeek_Sun | LastOfWeek_Sun |
|---|---|---|---|---|
| 2020-53 | 2020-12-28 | 2021-01-03 | 2020-12-27 | 2021-01-02 |
| 2021-01 | 2020-12-28 | 2021-01-03 | 2020-12-27 | 2021-01-02 |
| 2021-02 | 2021-01-04 | 2021-01-10 | 2021-01-03 | 2021-01-09 |
| 2021-48 | 2021-11-22 | 2021-11-28 | 2021-11-21 | 2021-11-27 |
| 2021-53 | 2021-12-27 | 2022-01-02 | 2021-12-26 | 2022-01-01 |
| 2022-01 | 2021-12-27 | 2022-01-02 | 2021-12-26 | 2022-01-01 |
| 2022-02 | 2022-01-03 | 2022-01-09 | 2022-01-02 | 2022-01-08 |
Demo on db<>fiddle here
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 |
