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,
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,
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
(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
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